package code;

import code.loader.CurryModulePath;
import code.loop.Batcher;
import code.loop.ReadEvalPrint;
import code.loop.Command;
import code.stuff.Tracer;
import code.table.ModuleTable;

import java.io.*;
import java.net.Socket;
import java.util.*;

/**
 * The Main class of the Curry Virtual Machine.
 * <p/>
 * <P>
 * <B>Class Responsibilities</B> :
 * <OL>
 * <LI> Parse Command Line Arguments and take appropriate action. </LI>
 * <LI> Load built-in Modules.  </LI>
 * </OL>
 * <B>Class Collaborators</B> :
 * See the imports section of this file.
 * <P>
 *
 * @author Sergio Antoy
 * @since June 17, 2003
 */

public class Main {

    // Is the following variable ever used?
    public static Hashtable <String,String> envVarTab 
      = new Hashtable <String,String> ();
    //    public static long startTime = System.currentTimeMillis();
    //    public static ArrayList argsArr = new ArrayList();


    public static void main(String[] arg) {

        String moduleName = null;

        // If an expression is typed to the read-eval-print loop,
        // any symbols of the expression is searched in the MapTable.
        // Until the ModuleTable is loaded, MapTable is not filled.
        // If no module has been loaded, it is possible that 
        // no access has been made to ModuleTable and thus that the
        // builtin/standard modules have not been loaded.
        // The consequence would be that an expression such as
        // 2+2 would report that + is an unknown operation.
        // Therefore, we touch the class here to force loading.
        // Which better method to call?
        //

        //Create a new command interpreter to use
        Command command =
            new Command(
                new BufferedReader(new InputStreamReader(System.in)),
                new BufferedWriter(new OutputStreamWriter(System.err)));

        int i = 0;
        while (i < arg.length) {
            String flag = arg[i++];
            if (flag.equals("-help")) {
                usage();
                return;
            } else if (flag.equals("-script")) {
                Tracer.script = true;
            } else if (flag.equals("-batch")) {
                moduleName = arg[i++];
                Tracer.batch = true;
            } else if (flag.equals("-test")) {
                moduleName = arg[i++];
                Tracer.test = true;
            }
            //direct paths to use
            else if (flag.equals("-currypath")) {
                try {
		    if (arg[i].startsWith ("-")) {
			System.err.println("error: no arg specified for -currypath");
			System.exit(1);
		    }
		    else {
			String tmp = arg[i++];
			CurryModulePath.append(tmp, CurryModulePath.CURRYPATH);
			}
                    //if it is the last option at the command line
                } catch (ArrayIndexOutOfBoundsException e) {
                    System.err.println("error: no curry path specified");
                    System.exit(1);
                } catch (IOException e) {
                    System.err.println(e.getMessage());
                    System.exit(1);
                }
            }
            //direct nativelib_classpath to use
            else if (flag.equals("-nativelib_classpath")) {
                try {
		    if (arg[i].startsWith ("-")) {
			System.err.println("error: no arg specified for -nativelib_classpath");
			System.exit(1);
		    }
		    else {
			String tmp = arg[i++];
			CurryModulePath.append(tmp, CurryModulePath.NATIVEPATH);
			}
                    //if it is the last option at the command line
                } catch (ArrayIndexOutOfBoundsException e) {
		    System.err.println("error: no arg specified for -nativelib_classpath");
                    System.exit(1);
                } catch (IOException e) {
                    System.err.println(e.getMessage());
                    System.exit(1);
                }
            }
            //direct nativelib_pkgs to use
            else if (flag.equals("-nativelib_pkgs")) {
                try {
		    if (arg[i].startsWith ("-")) {
			System.err.println("error: no arg specified for -nativelib_pkgs");
			System.exit(1);
		    }
		    else
			CurryModulePath.appendPkgs(arg[i++]);
                    //if it is the last option at the command line
                } catch (ArrayIndexOutOfBoundsException e) {
		    System.err.println("error: no arg specified for -nativelib_pkgs");
                    System.exit(1);
                } catch (IOException e) {
                    System.err.println(e.getMessage());
                    System.exit(1);
                }
            }
            else if (flag.equals("-env")) {
                try {
		    if (arg[i].startsWith ("-")) {
			System.err.println("error: no arg specified for -env");
			System.exit(1);
		    }
		    else {
		      String envArg = arg[i++];
//System.out.println ("\n\nenvArg : <  " + envArg + " >\n\n");
		      String [] envVarSettings = envArg.split ("[ \t\n]");
		      for (int j=0; j<envVarSettings.length; j++) {
			String envVarSetting = envVarSettings[j];
//System.out.println ("\n\tenvVarSetting : <  " + envVarSetting + " >");
			if (envVarSetting.contains ("=")) {
			  String [] envVarInfo = envVarSetting.split ("=");
			  if (envVarInfo.length == 2)
			    envVarTab.put (envVarInfo[0], envVarInfo[1]);
			  else
			    envVarTab.put (envVarInfo[0], "");
//System.out.println ("\t\t\"" + envVarInfo[0] + "\" = \"" + envVarTab.get (envVarInfo[0]) + "\"");
			}
		      }
		    }
                    //if it is the last option at the command line
                } catch (ArrayIndexOutOfBoundsException e) {
		    System.err.println("error: no arg specified for -env");
                    System.exit(1);
                }
            }
            // Settings of FLVM
            else if (flag.equals("-space")) {
                command.command(":space on");
            } else if (flag.equals("-reduction")) {
                command.command(":reduction on");
            } else if (flag.equals("-computation")) {
                command.command(":computation on");
            } else if (flag.equals("-instruction")) {
                command.command(":instruction on");
            } else if (flag.equals("-rule")) {
                command.command(":rule on");
            } else if (flag.equals("-time")) {
                command.command(":time on");
            } else if (flag.equals("-output")) {
                command.command(":output on");
            }
            //loader's traces
            else if (flag.equals("-symbols")) {
                command.command(":symbols on");
            } else if (flag.equals("-clp")) {
                command.command(":clp on");
            }
            // Unrecognized command
            else if (flag.startsWith ("-")) {
                System.err.println("Unknown flag \"" + flag + "\" Use -help flag for a list of options.");
                System.exit(1);
            }
	    else {
		// argsArr.add (flag);
	    }
        }

        ModuleTable.loadBuiltinModules();

        if (moduleName == null)
            new ReadEvalPrint().start();
        else
            new Batcher(moduleName).start();
    }

    private static void usage() {
        String usageMessage =
        "\njava Main options"+
        "\n    where options are one of the following"+
        "\n          -help             displays this information"+
        "\n          -currypath <path> defines the colon separated paths to search for the modules"+
        "\n          -nativelib_classpath <path> defines the colon separated paths to search for java modules"+
        "\n          -nativelib_pkgs <path> defines the colon separated package names for java modules"+
        "\n          -env <env_var_settings> defines the newline separated environment variable settings"+
        "\n          -script           execute from a script without prompting non-interactively"+
        "\n          -batch <module>   execute \"main\" function of module"+
        "\n          -test <module>    execute \"test*\" functions of module"+
        "\n          -space            traces events in the space of computation"+
        "\n          -reduction        traces reductions steps of a computation"+
        "\n          -computation      traces events in the life of a computation"+
        "\n          -instruction      traces the instruction being executed"+
        "\n          -rule             traces the rewrite rule being fired."+
        "\n          -output           traces an output as it is generated"+
        "\n          -symbols          traces symbols being loaded and unloaded"+
        "\n          -clp              traces the command line parser's actions"+
        "\n          -time             gives time and other statistics at the end of a computation." +
	"\n          ARGS              further arguments passed to application (see System.getArgs)";
        System.out.println (usageMessage) ;
    }
}
