

import Jakarta.util.FixDosOutputStream;
import Jakarta.util.Util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.HashSet;

public class Main {

    final private static String SOURCE_FILE = "source file" ;

    private ArgList instanceArgs = null ;
    private PositionalArg sourceFile = null ;

    private int myLayerID;
    public static boolean verbose = false;

    private static HashSet filesProcessed = new HashSet();
    public static String currentOutputFileName ="";

    // given absolute path of file, directoryPath returns
    // the absolute path of the file's directory

    String directoryPath( String path ) {
        if ( path == null )
            return ".";
        int lst = path.lastIndexOf( File.separatorChar );
        if ( lst == -1 )
            return ".";
        else
            return path.substring( 0, lst );
    }

    /**
     * Overrides previous version to allow multiple source files in the
     * argument list.  This is done by successively substituting the
     * "source file" argument by each of the extra arguments.
     *
     * <p>
     * <em>Note:</em>
     * The argument processing here is a mess, mostly because the command
     * line parsing design is poor.  That's another area of clean-up.
     *
     * @layer<Java>
     */
    protected boolean driver( ArgList args ) {

        instanceArgs = args ;

        // Find the "source file" argument to be updated.
        //
        for ( Iterator p = args.iterator() ; p.hasNext() ; ) {
            Object object = p.next() ;
            if ( object instanceof PositionalArg ) {
                PositionalArg arg = ( PositionalArg ) object ;
                if ( SOURCE_FILE.equals( arg.name ) ) {
                    sourceFile = arg ;
                    break ;
                }
            }
        }

        if ( sourceFile == null )
            throw new IllegalStateException( "invalid source file parse" ) ;

        // Get verbosity level:
        //
        for ( Iterator p = args.iterator() ; p.hasNext() ; ) {
            Object object = p.next() ;
            if ( object instanceof Switch ) {
                Switch sw = ( Switch ) object ;
                if ( sw.name == "quiet" )
                    verbose = false ;
                else
                    if ( sw.name == "verbose" )
                        verbose = true ;
            }
        }

        // Handle first source file argument (at least one is required):
        //
        processing( packageName, sourceFile.binding ) ;

        // If there are no extra arguments, then we're done.
        //
        if ( extraArgs == null || extraArgs.size() < 1 )
            return true;

        // Substitute each extra source file argument into the source file
        // position, then re-evaluate command line.
        //
        for ( Iterator p = extraArgs.iterator() ; p.hasNext() ; )
            processing( packageName, ( String ) p.next() ) ;

        return true;
    }

    protected void processing( String label, String fileName ) {

        sourceFile.binding = fileName ;
        kernelConstants.globals().currentFileName = fileName;
        boolean processed = this.driver( instanceArgs );

        if ( verbose && processed )
            System.err.println( label
                            + ": file \""
                            + fileName
                            + '"' ) ;
    }

    //**************************************************
    // Method called by the top-most layer to allow a layer to request
    // switches and arguments.
    //**************************************************
    protected void argInquire( int _layer ) {
        Switch sw;

        // Save my layer number
        myLayerID = _layer;

        // Register my switches
        sw = new Switch( "d", "debug mode for parser", null, true, _layer );
        switchRegister( sw );
        sw = new Switch( "s", "send output to stdout", null, true, _layer );
        switchRegister( sw );
        sw = new Switch( "b", "bootstrap from JTS to FOP", null, true, _layer );
        switchRegister( sw );
        sw = new Switch( "v", "FOP (exit(1)) or JTS (exit(0)) version", null,  true, _layer );
        switchRegister( sw );
        sw = new Switch( "x", "override default file extension", new String[1],  true, _layer );
        switchRegister( sw );

        // Verbosity selection:
        //
        switchRegister( new Switch( "quiet",
                    "disables verbose output",
                    null,
                    true,
                    _layer ) ) ;

        switchRegister( new Switch( "verbose",
                    "enables verbose output (default)",
                    null,
                    true,
                    _layer ) ) ;

        // Register my command line positional arguments
        posArgRegister( new PositionalArg( SOURCE_FILE, _layer ) ) ;

        // Allow extra arguments:
        extraArgs = new ArrayList() ;

        // Call next layer
        original( nextLayer() );
    }

    //**************************************************
    // createAST()
    //**************************************************
    protected  AstNode createAST( ArgList argObjects ) {
        FileInputStream fis;
        PositionalArg parg;
        File inputFile;
        AstNode root;

        // do no file processing if -v switch is set;
        // just return version

        if ( argObjects.find( "v", Switch.class, myLayerID ) != null ) {
            if ( kernelConstants.LangName.equals( "" ) )
                System.exit( 1 );
            else
                System.exit( 0 );
        }

        Switch sw = ( Switch ) argObjects.find( "x", Switch.class, myLayerID );
        if ( sw != null ) {
            kernelConstants.jakExtension = sw.args[0];
        }
        parg = ( PositionalArg ) argObjects.first( PositionalArg.class, myLayerID );

        kernelConstants.PushParseTreeStack( parg.binding );
        try {
            inputFile = new File( parg.binding );
            fis = new FileInputStream( inputFile );
            kernelConstants.globals().mainProps.setProperty( "input", inputFile );

            // now add inputDirectory property
            String abspath = inputFile.getAbsolutePath();
            kernelConstants.globals().mainProps.setProperty( "inputDirectory",
                              directoryPath( abspath ) );
            kernelConstants.globals().currentAbsPath = abspath;

            // extract file extension -- if there is none, use ""
            String x = "";
            int i = parg.binding.lastIndexOf( '.' );
            if ( i != -1 )
                x = parg.binding.substring( i );
            kernelConstants.globals().currentFileExt = x;

            // see if we have already processed this file.  If so, return null

            if ( filesProcessed.contains( abspath ) ) {
                fis.close();
                kernelConstants.PopParseTreeStack();
                return null;
            }
        }
        catch ( Exception e ) {
            AstNode.fatalError( "Can't open file "+ parg.binding );
            fis = null;
        }

        try {
            Parser parser =  Parser.getInstance( fis ) ;
            root = parser.parseAll() ;
        }
        catch ( ParseException e ) {
            AstNode.parseError( e.toString() );
            root = null;
        }

        return ( root );
    }

    //**************************************************
    // outputAST()
    //**************************************************
    protected void outputAST( ArgList argObjects,  AstNode ast ) {
        PrintWriter pw;
        String outputFileName="";
        String inputFileName;
        String outputDirectory;
        int lastDot;
        File inputFile=null;

        pw = null;
        String lineSeparator =
                System.getProperties().getProperty( "line.separator" );

        if ( argObjects.find( "b", Switch.class, myLayerID ) != null ) {
            kernelConstants.LangName = "";
        }

        if ( argObjects.find( "s", Switch.class, myLayerID ) != null ) {
            if ( lineSeparator.compareTo( "\n" ) != 0 )
                pw = new PrintWriter( new FixDosOutputStream( System.out ) );
            else
                pw = new PrintWriter( System.out );
            outputDirectory = ".";
        }
        else {
            inputFile = ( File )  kernelConstants.globals().mainProps.getProperty( "input" );
            inputFileName = inputFile.getAbsolutePath();
            lastDot = inputFileName.lastIndexOf( '.' );

            if ( lastDot == -1 )
                outputFileName = inputFileName + kernelConstants.outputFileExtension;
            else
                outputFileName = inputFileName.substring( 0, lastDot ) +
                        kernelConstants.outputFileExtension;

            // @test Refactor into a method
            // outputFileName = computeOutputFileName(lastDot, inputFileName);

            outputDirectory = directoryPath( outputFileName );
            try {
                OutputStream os;
                FileOutputStream fos =
                        new FileOutputStream( outputFileName );

                if ( lineSeparator.compareTo( "\n" ) != 0 )
                    os = new FixDosOutputStream( fos );
                else
                    os = fos;
                pw = new PrintWriter( os );
            }
            catch ( IOException e ) {
                AstNode.fatalError( "Cannot open " + outputFileName + ": " +
                                    e.getMessage() );
            }
        }
        kernelConstants.globals().mainProps.setProperty( "output", pw );

        // in some future version, it might be possible to set
        // outputDirectory from a command-line argument. if so,
        // the property of outputDirectory would have been already set

        if ( ! kernelConstants.globals().mainProps.containsProperty( "outputDirectory" ) )
            kernelConstants.globals().mainProps.setProperty( "outputDirectory", outputDirectory );

        ast.reduce2java( kernelConstants.globals().mainProps );
        pw.println();
        pw.flush();

        // Keeps the value of the OutputFileName to be accessed by Jak2aj tool
        // for file renaming
        currentOutputFileName = outputFileName;

        // add to list of files already processed
        filesProcessed.add( kernelConstants.globals().currentFileName );

        kernelConstants.PopParseTreeStack();

        // Call outputAST() for other layers
        original( argObjects, ast );

    }

}
