/*
 *        JacORB - a free Java ORB
 *
 *   Copyright (C) 1997-2014 Gerald Brose / The JacORB Team.
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Library General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 of the License, or (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *   Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free
 *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

package org.jacorb.idl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Stack;
import java.util.logging.Level;

/**
 * This class deals with IDL input files and their inclusion relationships.
 *
 * @author Gerald Brose
 */

public class GlobalInputStream
{
    private static InputStream stream;
    private static Stack<Integer> lookahead_stack;
    private static boolean included;
    private static StringBuffer expandedText;
    private static int pos;
    private static boolean eof;
    private static File currentFile;
    private static String[] path_names;

    /** stack of information for lexical scopes */
    static Stack<PositionInfo> positions;

    public static void init()
    {
        lookahead_stack = new Stack<Integer>();
        included = false;
        expandedText = new StringBuffer();
        pos = 0;
        eof = false;
        currentFile = null;
        positions = new Stack<PositionInfo>();
    }

    public static void cleanUp()
    {
        stream = null;
        lookahead_stack = null;
        included = false;
        expandedText = null;
        pos = 0;
        eof = false;
        currentFile = null;
        path_names = null;
    }

    public static void setInput(String fname)
        throws java.io.IOException
    {
        currentFile = new File(fname);
        stream = new java.io.FileInputStream(currentFile);
    }

    /**
     * Test if this input stream (or rather the underlying IDL file)
     * is more recent (was modified at a later time than) another
     * file.  (Used, e.g., to determine if code has been previosuly
     * generated from an IDL file).
     *
     * @param other the file to compare this stream against
     * @return true, if this stream's IDL file is more recent than the other file  .
     */

    public static boolean isMoreRecentThan(File other)
    {
        return (parser.forceOverwrite ||
                 (other.lastModified() < currentFile.lastModified())) ;
    }

    public static boolean includeState()
    {
        return included;
    }

    public static void insert(String str)
    {
        expandedText.insert(pos, str);
    }

    public static void include(String fname, int lookahead, boolean useIncludePath)
        throws FileNotFoundException
    {
        included = true;
        PositionInfo position = lexer.getPosition();
        position.file = currentFile();
        position.stream = stream;

        stream = find(fname, useIncludePath);

        positions.push(position);
        lookahead_stack.push(new Integer(lookahead));

        if (parser.logger.isLoggable(Level.FINEST))
            parser.logger.log(Level.FINEST, "Including " + fname);
        /* files form their own scopes, so we have to open a new one here */
    }

    public static void setIncludePath(String path)
    {
        java.util.StringTokenizer strtok =
                new java.util.StringTokenizer(path, File.pathSeparator);

        int i;
        if (path_names == null)
        {
            path_names = new String[ strtok.countTokens() ];
            i = 0;
        }
        else
        {
            i = path_names.length;

            String[] _path_names = new String[ strtok.countTokens() + path_names.length ];
            for (int j = 0; j < path_names.length; j++)
                _path_names[ j ] = path_names[ j ];
            path_names = _path_names;
        }

        while(strtok.hasMoreTokens())
        {
            path_names[ i++ ] = strtok.nextToken();
        }
    }

    /**
     *  tries to locate and open a new file for "fname",
     *  updates currentFile if successful
     */

    private static FileInputStream find(String fname, boolean useIncludePath)
            throws FileNotFoundException
    {
        if (!useIncludePath)
        {
            if (fname.indexOf(File.separator) != 0)
            {
                String dir = null;
                try
                {
                    dir = currentFile.getCanonicalPath();
                    if (dir.indexOf(File.separator) > -1)
                    {
                        dir = dir.substring(0, dir.lastIndexOf(File.separator));
                    }
                }
                catch(java.io.IOException ioe)
                {
                    parser.logger.log(Level.SEVERE, "Caught error finding file ", ioe);
                }

                if (parser.logger.isLoggable(Level.FINEST))
                    parser.logger.log(Level.FINEST, "opening " + dir + File.separator + fname);
                currentFile = new File(dir + File.separator + fname);
            }
            else
                currentFile = new File(fname);

            try
            {
                return new FileInputStream(currentFile);
            }
            catch(java.io.IOException iof)
            {
                return find(fname, true);
            }
        }
        else
        {
            if (path_names == null)
            {
                org.jacorb.idl.parser.fatal_error("File " + fname +
                        " not found in include path", null);
            }
            else
            {
                for (int i = 0; i < path_names.length; i++)
                {
                    try
                    {
                        if (parser.logger.isLoggable(Level.FINEST))
                            parser.logger.log(Level.FINEST, "opening " + path_names[ i ] + File.separator + fname);

                        currentFile = new File(path_names[ i ] + File.separator + fname);
                        return new FileInputStream(currentFile);
                    }
                    catch(FileNotFoundException fnfex)
                    {
                    }
                }
            }

            org.jacorb.idl.parser.fatal_error("File " + fname +
                    " not found in include path", null);
            return null;
        }
    }

    public static File currentFile()
    {
        return currentFile;
    }

    public static InputStream currentStream()
    {
        return stream;
    }

    public static int read()
        throws IOException
    {
        int ch = 0;

        // Might be the end of file for main file but still have an included file
        // to process.
        if (eof && positions.size() == 0)
        {
            return -1;
        }

        if (expandedText.length() > 0)
        {
            if (pos < expandedText.length())
                ch = expandedText.charAt(pos++);
            if (pos == expandedText.length())
            {
                expandedText = new StringBuffer();
                pos = 0;
            }
        }
        else
        {
            ch = currentStream().read();

            /*
             * if eof is reached, see whether we were reading
             * from the main input stream or from an include file.
             * If the latter, switch back to the main stream
             */

            if (ch == -1)
            {
                // the following line was moved here to fix bug #385
                currentStream().close();
                if (included)
                {

                    // undo effects of inhibition pragma
                    parser.setInhibitionState(false);

                    // return to last position in previous file
                    PositionInfo positionInfo = positions.pop();
                    stream = positionInfo.stream;
                    currentFile = positionInfo.file;
                    ch = lookahead_stack.pop().intValue();

                    included = !(positions.empty());

                    if (parser.logger.isLoggable(Level.FINEST))
                        parser.logger.log(Level.FINEST, "returning to " + currentFile + " included: " + included);

                    lexer.restorePosition(positionInfo);
                }
                else
                {
                    eof = true;
                }
            }
        }
        return ch;
    }

}
