/**
   * Nachos main entry point.
   *
   * @param args the command line arguments.
   */
  public static void main(final String[] args) {
    System.out.print("nachos 5.0j initializing...");

    Lib.assertTrue(Machine.args == null);
    Machine.args = args;

    processArgs();

    Config.load(configFileName);

    // get the current directory (.)
    baseDirectory = new File(new File("").getAbsolutePath());
    // get the nachos directory (./nachos)
    nachosDirectory = new File(baseDirectory, "nachos");

    String testDirectoryName = Config.getString("FileSystem.testDirectory");

    // get the test directory
    if (testDirectoryName != null) {
      testDirectory = new File(testDirectoryName);
    } else {
      // use ../test
      testDirectory = new File(baseDirectory.getParentFile(), "test");
    }

    securityManager = new NachosSecurityManager(testDirectory);
    privilege = securityManager.getPrivilege();

    privilege.machine = new MachinePrivilege();

    TCB.givePrivilege(privilege);
    privilege.stats = stats;

    securityManager.enable();
    createDevices();
    checkUserClasses();

    autoGrader = (AutoGrader) Lib.constructObject(autoGraderClassName);

    new TCB()
        .start(
            new Runnable() {
              public void run() {
                autoGrader.start(privilege);
              }
            });
  }
  private static void processArgs() {
    for (int i = 0; i < args.length; ) {
      String arg = args[i++];
      if (arg.length() > 0 && arg.charAt(0) == '-') {
        if (arg.equals("-d")) {
          Lib.assertTrue(i < args.length, "switch without argument");
          Lib.enableDebugFlags(args[i++]);
        } else if (arg.equals("-h")) {
          System.out.print(help);
          System.exit(1);
        } else if (arg.equals("-m")) {
          Lib.assertTrue(i < args.length, "switch without argument");
          try {
            numPhysPages = Integer.parseInt(args[i++]);
          } catch (NumberFormatException e) {
            Lib.assertNotReached("bad value for -m switch");
          }
        } else if (arg.equals("-s")) {
          Lib.assertTrue(i < args.length, "switch without argument");
          try {
            randomSeed = Long.parseLong(args[i++]);
          } catch (NumberFormatException e) {
            Lib.assertNotReached("bad value for -s switch");
          }
        } else if (arg.equals("-x")) {
          Lib.assertTrue(i < args.length, "switch without argument");
          shellProgramName = args[i++];
        } else if (arg.equals("-z")) {
          System.out.print(copyright);
          System.exit(1);
        }
        // these switches are reserved for the autograder
        else if (arg.equals("-[]")) {
          Lib.assertTrue(i < args.length, "switch without argument");
          configFileName = args[i++];
        } else if (arg.equals("--")) {
          Lib.assertTrue(i < args.length, "switch without argument");
          autoGraderClassName = args[i++];
        }
      }
    }

    Lib.seedRandom(randomSeed);
  }
  /**
   * Return the name of the process class that the kernel should use. In the multi-programming
   * project, returns <tt>nachos.userprog.UserProcess</tt>. In the VM project, returns
   * <tt>nachos.vm.VMProcess</tt>. In the networking project, returns
   * <tt>nachos.network.NetProcess</tt>.
   *
   * @return the name of the process class that the kernel should use.
   * @see nachos.userprog.UserKernel#run
   * @see nachos.userprog.UserProcess
   * @see nachos.vm.VMProcess
   * @see nachos.network.NetProcess
   */
  public static String getProcessClassName() {
    if (processClassName == null) processClassName = Config.getString("Kernel.processClassName");

    Lib.assertTrue(processClassName != null);
    return processClassName;
  }
  /**
   * Return the name of the shell program that a user-programming kernel must run. Make sure
   * <tt>UserKernel.run()</tt> <i>always</i> uses this method to decide which program to run.
   *
   * @return the name of the shell program to run.
   */
  public static String getShellProgramName() {
    if (shellProgramName == null) shellProgramName = Config.getString("Kernel.shellProgram");

    Lib.assertTrue(shellProgramName != null);
    return shellProgramName;
  }
  private static void checkUserClasses() {
    System.out.print(" user-check");

    Class aclsInt = (new int[0]).getClass();
    Class clsObject = Lib.loadClass("java.lang.Object");
    Class clsRunnable = Lib.loadClass("java.lang.Runnable");
    Class clsString = Lib.loadClass("java.lang.String");

    Class clsKernel = Lib.loadClass("nachos.machine.Kernel");
    Class clsFileSystem = Lib.loadClass("nachos.machine.FileSystem");
    Class clsRiderControls = Lib.loadClass("nachos.machine.RiderControls");
    Class clsElevatorControls = Lib.loadClass("nachos.machine.ElevatorControls");
    Class clsRiderInterface = Lib.loadClass("nachos.machine.RiderInterface");
    Class clsElevatorControllerInterface =
        Lib.loadClass("nachos.machine.ElevatorControllerInterface");

    Class clsAlarm = Lib.loadClass("nachos.threads.Alarm");
    Class clsThreadedKernel = Lib.loadClass("nachos.threads.ThreadedKernel");
    Class clsKThread = Lib.loadClass("nachos.threads.KThread");
    Class clsCommunicator = Lib.loadClass("nachos.threads.Communicator");
    Class clsSemaphore = Lib.loadClass("nachos.threads.Semaphore");
    Class clsLock = Lib.loadClass("nachos.threads.Lock");
    Class clsCondition = Lib.loadClass("nachos.threads.Condition");
    Class clsCondition2 = Lib.loadClass("nachos.threads.Condition2");
    Class clsRider = Lib.loadClass("nachos.threads.Rider");
    Class clsElevatorController = Lib.loadClass("nachos.threads.ElevatorController");

    Lib.checkDerivation(clsThreadedKernel, clsKernel);

    Lib.checkStaticField(clsThreadedKernel, "alarm", clsAlarm);
    Lib.checkStaticField(clsThreadedKernel, "fileSystem", clsFileSystem);

    Lib.checkMethod(clsAlarm, "waitUntil", new Class[] {long.class}, void.class);

    Lib.checkConstructor(clsKThread, new Class[] {});
    Lib.checkConstructor(clsKThread, new Class[] {clsRunnable});

    Lib.checkStaticMethod(clsKThread, "currentThread", new Class[] {}, clsKThread);
    Lib.checkStaticMethod(clsKThread, "finish", new Class[] {}, void.class);
    Lib.checkStaticMethod(clsKThread, "yield", new Class[] {}, void.class);
    Lib.checkStaticMethod(clsKThread, "sleep", new Class[] {}, void.class);

    Lib.checkMethod(clsKThread, "setTarget", new Class[] {clsRunnable}, clsKThread);
    Lib.checkMethod(clsKThread, "setName", new Class[] {clsString}, clsKThread);
    Lib.checkMethod(clsKThread, "getName", new Class[] {}, clsString);
    Lib.checkMethod(clsKThread, "fork", new Class[] {}, void.class);
    Lib.checkMethod(clsKThread, "ready", new Class[] {}, void.class);
    Lib.checkMethod(clsKThread, "join", new Class[] {}, void.class);

    Lib.checkField(clsKThread, "schedulingState", clsObject);

    Lib.checkConstructor(clsCommunicator, new Class[] {});
    Lib.checkMethod(clsCommunicator, "speak", new Class[] {int.class}, void.class);
    Lib.checkMethod(clsCommunicator, "listen", new Class[] {}, int.class);

    Lib.checkConstructor(clsSemaphore, new Class[] {int.class});
    Lib.checkMethod(clsSemaphore, "P", new Class[] {}, void.class);
    Lib.checkMethod(clsSemaphore, "V", new Class[] {}, void.class);

    Lib.checkConstructor(clsLock, new Class[] {});
    Lib.checkMethod(clsLock, "acquire", new Class[] {}, void.class);
    Lib.checkMethod(clsLock, "release", new Class[] {}, void.class);
    Lib.checkMethod(clsLock, "isHeldByCurrentThread", new Class[] {}, boolean.class);

    Lib.checkConstructor(clsCondition, new Class[] {clsLock});
    Lib.checkConstructor(clsCondition2, new Class[] {clsLock});

    Lib.checkMethod(clsCondition, "sleep", new Class[] {}, void.class);
    Lib.checkMethod(clsCondition, "wake", new Class[] {}, void.class);
    Lib.checkMethod(clsCondition, "wakeAll", new Class[] {}, void.class);
    Lib.checkMethod(clsCondition2, "sleep", new Class[] {}, void.class);
    Lib.checkMethod(clsCondition2, "wake", new Class[] {}, void.class);
    Lib.checkMethod(clsCondition2, "wakeAll", new Class[] {}, void.class);

    Lib.checkDerivation(clsRider, clsRiderInterface);

    Lib.checkConstructor(clsRider, new Class[] {});
    Lib.checkMethod(clsRider, "initialize", new Class[] {clsRiderControls, aclsInt}, void.class);

    Lib.checkDerivation(clsElevatorController, clsElevatorControllerInterface);

    Lib.checkConstructor(clsElevatorController, new Class[] {});
    Lib.checkMethod(
        clsElevatorController, "initialize", new Class[] {clsElevatorControls}, void.class);
  }