/**
 * Copyright (c) 1998 by Cornell University.
 * All rights reserved.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL CORNELL UNIVERSITY BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF CORNELL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * CORNELL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND CORNELL UNIVERSITY HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 */
package cornell.slk.jkernel.core;

import java.io.*;
import java.util.*;
import cornell.slk.jkernel.std.Repository; // XXX shouldn't be in the core

/**
 *  @internal_only
 *
 *  The JKernel class represents an entire J-Kernel instance.  To start
 *  a J-Kernel:
 *  <ul>
 *  <li>Create a JKernel object
 *  <li>Set debugging options
 *  <li>Create a resolver (which may make use of the SystemResolver) for
 *      the root task
 *  <li>Call createRootTask
 *  <li>Create a runnable object to handle the root task's first thread.
 *      It is helpful to use getRootTaskClass to get a Class object
 *      defined by the root task, and to then use reflection to instantiate
 *      an object of this class to produce a runnable.
 *  <li>Call runRootTask
 *  <li>Don't do anything else (just return).  startRootTask will return if
 *      the initial thread returns.  The J-Kernel will run until there are
 *      no non-daemon threads left.
 *  </ul>
 *
 *  All of this is automated in std.Main, which is the recommended interface
 *  for starting the J-Kernel.
 */
public final class JKernel
{
    static JKernel jKernel;

    SystemClassLoader systemClassLoader;
    SystemResolver systemResolver;
    public boolean dbug;
    boolean eagerLinking;
    Task rootTask;
    Task systemLoaderTask;
    Vector allTasks = new Vector();
    ThreadGroup globalThreadGroup; // the one and only thread group in the system
    Repository repository; // XXX shouldn't be in the core

    synchronized static void checkInit(JKernel jk)
        throws SecurityException
    {
        if(jKernel != null) throw new SecurityException("Only one J-Kernel can exist in a virtual machine");
        jKernel = jk;
    }

    public JKernel()
        throws SecurityException
    {
        checkInit(this);

        systemClassLoader = new SystemClassLoader();
        systemResolver = new SystemResolver(systemClassLoader);

        systemLoaderTask = new Task(systemResolver, null);
        systemLoaderTask.nativeCodeEnabled = true;
    }

    public synchronized void enableDebug() {dbug = true;}
    public synchronized void enableEagerLinking() {eagerLinking = true;}

    public synchronized Resolver getSystemResolver()
    {
        return systemResolver;
    }

    public synchronized void createRootTask(Resolver resolver)
        throws Exception
    {
        if(rootTask != null) throw new SecurityException("root task already started");

        globalThreadGroup = Thread.currentThread().getThreadGroup();
        rootTask = new Task(resolver, null);
        rootTask.nativeCodeEnabled = true;
    }

    public synchronized Class getRootTaskClass(String className)
        throws ClassNotFoundException
    {
        return rootTask.nodeLoader.getClassNode(className).getClassObject();
    }

    public synchronized void runRootTask(Runnable target)
    {
        ThreadSegment threadSegment = new ThreadSegment(target, rootTask);
        threadSegment.start();
    }
}
