/*
 *
 *
 * Copyright  1990-2006 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation. 
 * 
 * This program 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
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt). 
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA 
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions. 
 */

package com.sun.midp.appmanager;

import javax.microedition.midlet.*;

import javax.microedition.lcdui.*;

import com.sun.midp.i18n.Resource;
import com.sun.midp.i18n.ResourceConstants;

import com.sun.cldc.isolate.*;

import com.sun.midp.midlet.*;

import com.sun.midp.installer.*;

import com.sun.midp.midletsuite.*;

import com.sun.midp.main.*;

import com.sun.midp.io.j2me.push.*;

import com.sun.midp.configurator.Constants;


/**
 * This is an implementation of the ApplicationManager interface
 * for the MVM mode of the VM running with only one midlet at time.
 *
 * Application manager controls midlet life cycle:
 *    - installs, updates and removes midlets/midlet suites
 *    - launches and terminates midlets
 *    - displays info about a midlet/midlet suite
 *    - shuts down the AMS system
 */
public class SMMManager extends MIDlet
    implements MIDletProxyListListener, Runnable, ApplicationManager {

    /** Constant for the discovery application class name. */
    private static final String DISCOVERY_APP =
        "com.sun.midp.installer.DiscoveryApp";

    /** Constant for the graphical installer class name. */
    private static final String INSTALLER =
        "com.sun.midp.installer.GraphicalInstaller";

    /** Constant for the CA manager class name. */
    private static final String CA_MANAGER =
        "com.sun.midp.appmanager.CaManager";

    /** True until constructed for the first time. */
    private static boolean first = true;

    /** MIDlet Suite storage object. */
    private MIDletSuiteStorage midletSuiteStorage;

    /** Screen that displays all installed midlets and installer */
    private AppManagerUI appManagerUI;

    /** MIDlet proxy list reference. */
    private MIDletProxyList midletProxyList;

    /** If true, MIDlet can be launched. */
    private boolean allowMidletLaunch = true;

    /** ID of the suite to run. */
    int suiteId;

    /** Class name of the MIDlet to run. */
    String className;

    /** Display name of the MIDlet to run. */
    String displayName;

    /**
     * If not null, this string will be available to the
     * MIDlet as application property arg-0
     */
    String arg0;

    /**
     * If not null, this string will be available to the
     * MIDlet as application property arg-1
     */
    String arg1;

    /**
     * If not null, this string will be available to the
     * MIDlet as application property arg-2
     */
    String arg2;


    /** UI to display error alerts. */
    DisplayError displayError;

    /**
     * Create and initialize a new Manager MIDlet.
     */
    public SMMManager() {
        midletSuiteStorage = MIDletSuiteStorage.getMIDletSuiteStorage();

        /*
         * Listen to the MIDlet proxy list.
         * this allows us to notify the Application Selector
         * of any changes whenever switch back to the AMS.
         */
        midletProxyList = MIDletProxyList.getMIDletProxyList();
        midletProxyList.addListener(this);
        midletProxyList.setDisplayController(
            new SMMDisplayController(midletProxyList,
                MIDletSuite.INTERNAL_SUITE_ID, this.getClass().getName()));

        PushRegistryInternal.setMvmSingleMidletMode();

        GraphicalInstaller.initSettings();

	first = (getAppProperty("logo-displayed") == null);

	Display display = Display.getDisplay(this);
	displayError = new DisplayError(display);

	// AppSelector will be set to be current at the end of its constructor
        appManagerUI = new AppManagerUI(this, display, displayError, first, null);

        if (first) {
            first = false;
        }
    }

    /**
     * Start app; there is nothing that needs to be done at start up.
     */
    public void startApp() {
    }

    /**
     * Pause; there are no resources that need to be released.
     */
    public void pauseApp() {
    }

    /**
     * Destroy cleans up.
     *
     * @param unconditional is ignored; this object always
     * destroys itself when requested.
     */
    public void destroyApp(boolean unconditional) {
        // IMPL_NOTE: remove this:
        GraphicalInstaller.saveSettings(null, MIDletSuite.UNUSED_SUITE_ID);

        // Ending this MIDlet ends all others.
        midletProxyList.shutdown();
    }

    // ==============================================================
    // ------ Implementation of the MIDletProxyListListener interface

    /**
     * Called when a MIDlet is added to the list.
     *
     * @param midlet The proxy of the MIDlet being added
     */
    public void midletAdded(MIDletProxy midlet) {
        appManagerUI.notifyMidletStarted(midlet);
    }

    /**
     * Called when the state of a MIDlet in the list is updated.
     *
     * @param midlet The proxy of the MIDlet that was updated
     * @param fieldId code for which field of the proxy was updated
     */
    public void midletUpdated(MIDletProxy midlet, int fieldId) {
        appManagerUI.notifyMidletStateChanged(midlet);
    }

    /**
     * Called when a MIDlet is removed from the list.
     *
     * @param midlet The proxy of the removed MIDlet
     */
    public void midletRemoved(MIDletProxy midlet) {
        appManagerUI.notifyMidletExited(midlet);
    }

    /**
     * Called when error occurred while starting a MIDlet object.
     *
     * @param externalAppId ID assigned by the external application manager
     * @param suiteId Suite ID of the MIDlet
     * @param className Class name of the MIDlet
     * @param errorCode start error code
     * @param errorDetails start error code
     */
    public void midletStartError(int externalAppId, int suiteId,
                                 String className, int errorCode,
                                 String errorDetails) {
        allowMidletLaunch = true;
        appManagerUI.notifyMidletStartError(suiteId, className,
                errorCode, errorDetails);
    }


    /** Launches an application Isolate and waits for it to end. */
    public void run() {
        try {
            Isolate appIsolate = AmsUtil.startMidletInNewIsolate(suiteId,
                                     className, displayName,
                                     arg0, arg1, arg2);

            appIsolate.waitForExit();
        } finally {
            suiteId = MIDletSuite.UNUSED_SUITE_ID;
            className = null;
            displayName = null;
            arg0 = null;
            arg1 = null;
            arg2 = null;
            allowMidletLaunch = true;
        }
    }

    // ===================================================================
    // ---- Implementation of the ApplicationManager interface ------------

    /**
     * Discover and install a suite.
     */
    public void installSuite() {
        try {
            runMidlet(MIDletSuite.INTERNAL_SUITE_ID, DISCOVERY_APP,
                Resource.getString(ResourceConstants.INSTALL_APPLICATION));

        } catch (Exception ex) {
            displayError.showErrorAlert(Resource.getString(
                                  ResourceConstants.INSTALL_APPLICATION),
                        ex, null, null);
        }
    }

    /** Launch the CA manager. */
    public void launchCaManager() {
        try {
            MIDletSuiteUtils.execute(MIDletSuite.INTERNAL_SUITE_ID, CA_MANAGER,
                Resource.getString(ResourceConstants.CA_MANAGER_APP));
        } catch (Exception ex) {
            displayError.showErrorAlert(Resource.getString(
                ResourceConstants.CA_MANAGER_APP), ex, null, null);
        }
    }

    /**
     * Launches a suite.
     *
     * @param suiteInfo information for suite to launch
     * @param midletToRun class name of the MIDlet to launch
     */
    public void launchSuite(RunningMIDletSuiteInfo suiteInfo,
                            String midletToRun) {

        if (Constants.MEASURE_STARTUP) {
            System.err.println("Application Startup Time: Begin at "
                        +System.currentTimeMillis());
        }

        try {
            // Create an instance of the MIDlet class
            // All other initialization happens in MIDlet constructor
            runMidlet(suiteInfo.suiteId, midletToRun,
                      suiteInfo.displayName);

        } catch (Exception ex) {
            displayError.showErrorAlert(suiteInfo.displayName, ex, null, null);
        }
    }

    /**
     * Update a suite.
     *
     * @param suiteInfo information for suite to update
     */
    public void updateSuite(RunningMIDletSuiteInfo suiteInfo) {
        try {
            runMidlet(MIDletSuite.INTERNAL_SUITE_ID, INSTALLER,
                      null, "U", String.valueOf(suiteInfo.suiteId), null);

        } catch (Exception ex) {
            displayError.showErrorAlert(suiteInfo.displayName, ex, null, null);
        }
    }

    /**
     * Shut downt the system
     */
    public void shutDown() {
        midletProxyList.shutdown();
    }

    /**
     * Bring the midlet with the passed in midlet suite info to the
     * foreground.
     *
     * @param suiteInfo information for the midlet to be put to foreground
     */
    public void moveToForeground(RunningMIDletSuiteInfo suiteInfo) {}


    /**
     * Exit the midlet with the passed in midlet suite info.
     *
     * @param suiteInfo information for the midlet to be terminated
     */
    public void exitMidlet(RunningMIDletSuiteInfo suiteInfo) {}


    // ==============================================================
    // ----------------- PRIVATE methods ---------------------------

    /**
     * Starts a MIDlet in a new Isolate.
     *
     * @param id ID of an installed suite
     * @param midlet class name of MIDlet to invoke
     * @param displayName name to display to the user
     */
    private void runMidlet(int id, String midlet, String displayName) {
        runMidlet(id, midlet, displayName, null, null, null);
    }

    /**
     * Starts a MIDlet in a new Isolate with the given arguments.
     *
     * @param id ID of an installed suite
     * @param midlet class name of MIDlet to invoke
     * @param displayName name to display to the user
     * @param arg0 if not null, this parameter will be available to the
     *             MIDlet as application property arg-0
     * @param arg1 if not null, this parameter will be available to the
     *             MIDlet as application property arg-1
     * @param arg2 if not null, this parameter will be available to the
     *             MIDlet as application property arg-2
     */
    private void runMidlet(int id, String midlet,
            String displayName, String arg0, String arg1, String arg2) {
        if (!allowMidletLaunch) {
            return;
        }

        this.suiteId = id;
        this.className = midlet;
        this.displayName = displayName;
        this.arg0 = arg0;
        this.arg1 = arg1;
        this.arg2 = arg2;

        new Thread(this).start();

        allowMidletLaunch = false;
    }
}
