/*
 *
 *
 * 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.main;

import com.sun.midp.events.EventTypes;
import com.sun.midp.events.NativeEvent;
import com.sun.midp.events.EventQueue;


/**
 * This class provides methods to send events of types
 * handled by MIDletControllerEventConsumer I/F implementors.
 * This class completely hide event construction & sending in its methods.
 *
 * This class is intended to be used by MIDletStateHandler & MIDletPeer
 * classes in Allication Isolate.
 * So in some of its sendXXXEvent()methods we can change int IDs to
 * MIDletPeer references.
 *
 * Generic comments for all XXXEventProducers:
 *
 * For each supported event type there is a separate sendXXXEvent() method,
 * that gets all needed parameters to construct an event of an approprate class.
 * The method also performs event sending itself.
 *
 * If a given event type merges a set of logically different subtypes,
 * this class shall provide separate methods for these subtypes.
 *
 * It is assumed that only one object instance of this class
 * is initialized with the system event that is created at (isolate) startup. 
 *
 * This class only operates on the event queue given to it during
 * construction, the class does not obtain any restricted object itself,
 * so it does not need protection.
 *
 * All MIDP stack subsystems that need to send events of supported types,
 * must get a reference to an already created istance of this class.
 * Typically, this instance should be passed as a constructor parameter.
 *
 * Class is NOT final to allow debug/profile/test/automation subsystems
 * to change, substitute, complement default "event sending" functionality :
 * Ex.
 * class LogXXXEventProducer
 *      extends XXXEventProducer {
 *  ...
 *  void sendXXXEvent(parameters) {
 *      LOG("Event of type XXX is about to be sent ...")
 *      super.sendXXXEvent(parameters);
 *      LOG("Event of type XXX has been sent successfully !")
 *  }
 *  ...
 * }
 */
public class MIDletControllerEventProducer {

    /** Cached reference to the MIDP event queue. */
    protected EventQueue eventQueue;
    /** Cached reference to AMS isolate ID. */
    protected int amsIsolateId;
    /** Cached reference to current isolate ID. */
    protected int currentIsolateId;

    /** Preallocate start error event to work in case of out of memory */
    final NativeEvent startErrorEvent;
    /** Preallocate MIDlet created event to work in case of out of memory */
    final NativeEvent midletCreatedEvent;
    /** Preallocate MIDlet active event to work in case of out of memory */
    final NativeEvent midletActiveEvent;
    /** Preallocate MIDlet paused event to work in case of out of memory */
    final NativeEvent midletPausedEvent;
    /** Preallocate MIDlet destroyed event to work in case of out of memory */
    final NativeEvent midletDestroyedEvent;
    /**
     * Preallocate MIDlet resources paused event to work in case of out
     * of memory
     */
    final NativeEvent midletRsPausedEvent;

    /**
     * Construct a new MIDletControllerEventProducer.
     *
     * @param  theEventQueue An event queue where new events will be posted.
     * @param  theAmsIsolateId AMS Isolate Id
     * @param  theCurrentIsolateId Current Isolate Id
     */
    public MIDletControllerEventProducer(
        EventQueue theEventQueue,
        int theAmsIsolateId,
        int theCurrentIsolateId) {

        eventQueue = theEventQueue;
        amsIsolateId = theAmsIsolateId;
        currentIsolateId = theCurrentIsolateId;

        /* Cache all of the notification events. */
        startErrorEvent =
            new NativeEvent(EventTypes.MIDLET_START_ERROR_EVENT);
        midletCreatedEvent =
            new NativeEvent(EventTypes.MIDLET_CREATED_NOTIFICATION);
        midletActiveEvent =
            new NativeEvent(EventTypes.MIDLET_ACTIVE_NOTIFICATION);
        midletPausedEvent =
            new NativeEvent(EventTypes.MIDLET_PAUSED_NOTIFICATION);
        midletDestroyedEvent =
            new NativeEvent(EventTypes.MIDLET_DESTROYED_NOTIFICATION);
        midletRsPausedEvent =
            new NativeEvent(EventTypes.MIDLET_RS_PAUSED_NOTIFICATION);
    }

    /*
     * MIDlet Startup Events:
     *
     * MIDLET_START_ERROR
     * MIDLET_CREATED_NOTIFICATION
     */
    /**
     * Notifies AMS that MIDlet creation failed
     * NEW: earlier it has been explicitely generated by
     * void static AppIsolateMIDletSuiteLoader.main(...)
     *
     * @param midletExternalAppId ID of given by an external application
     *                            manager
     * @param midletSuiteId ID of the MIDlet suite
     * @param midletClassName Class name of the MIDlet
     * @param errorCode start error code
     */
    public void sendMIDletStartErrorEvent(
        int midletSuiteId,
        String midletClassName,
        int midletExternalAppId,
        int errorCode,
        String errorDetails) {

        synchronized (startErrorEvent) {
            // use pre-created event to work in case of handling out of memory
            startErrorEvent.intParam1 = midletSuiteId;
            startErrorEvent.intParam2 = midletExternalAppId;
            startErrorEvent.intParam3 = errorCode;

            startErrorEvent.stringParam1 = midletClassName;
            startErrorEvent.stringParam2 = errorDetails; 

            eventQueue.sendNativeEventToIsolate(startErrorEvent, amsIsolateId);
        }
    }

    /**
     * Called to send a MIDlet created notification to the AMS isolate.
     *
     * @param midletSuiteId ID of the MIDlet suite
     * @param midletClassName Class name of the MIDlet
     * @param midletExternalAppId ID of given by an external application
     *                            manager
     * @param midletDisplayName name to show the user
     */
    public void sendMIDletCreateNotifyEvent(
        int midletSuiteId,
        String midletClassName,
        int midletExternalAppId,
        String midletDisplayName) {

        synchronized (midletCreatedEvent) {
            midletCreatedEvent.intParam1 = midletSuiteId;
            midletCreatedEvent.intParam2 = currentIsolateId;
            midletCreatedEvent.intParam3 = midletExternalAppId;

            midletCreatedEvent.stringParam1 = midletClassName;
            midletCreatedEvent.stringParam2 = midletDisplayName;

            eventQueue.sendNativeEventToIsolate(midletCreatedEvent,
                                                amsIsolateId);
        }
    }

    /*
     * MIDlet State Management (Lifecycle) Events:
     *
     * MIDLET_ACTIVE_NOTIFICATION
     * MIDLET_PAUSE_NOTIFICATION
     * MIDLET_DESTROY_NOTIFICATION
     *
     * MIDLET_DESTROY_REQUEST
     *
     * ACTIVATE_ALL - produced by native code
     * PAUSE_ALL -produced by native code
     * SHUTDOWN/DESTROY_ALL - produced by native code
     *
     * FATAL_ERROR_NOTIFICATION - produced by native code
     *
     */
    /**
     * Called to send a MIDlet active notification to the AMS isolate.
     *
     * @param midletSuiteId ID of the MIDlet suite
     * @param midletClassName Class name of the MIDlet
     */
    public void sendMIDletActiveNotifyEvent(int midletSuiteId,
                                            String midletClassName) {
        sendEvent(midletActiveEvent, midletSuiteId, midletClassName);
    }
    /**
     * Called to send a MIDlet paused notification to the AMS isolate.
     *
     * @param midletSuiteId ID of the MIDlet suite
     * @param midletClassName Class name of the MIDlet
     */
    public void sendMIDletPauseNotifyEvent(int midletSuiteId,
                                           String midletClassName) {
        sendEvent(midletPausedEvent, midletSuiteId, midletClassName);
    }
    /**
     * Called to send a MIDlet destroyed notification to the AMS isolate.
     *
     * @param midletSuiteId ID of the MIDlet suite
     * @param midletClassName Class name of the MIDlet
     */
    public void sendMIDletDestroyNotifyEvent(int midletSuiteId,
                                             String midletClassName) {
        sendEvent(midletDestroyedEvent, midletSuiteId, midletClassName);
    }
    /**
     * Called to send a MIDlet resume request to the AMS isolate.
     *
     * @param midletSuiteId ID of the MIDlet suite
     * @param midletClassName Class name of the MIDlet
     */
    public void sendMIDletResumeRequest(int midletSuiteId,
                                        String midletClassName) {
        sendEvent(new NativeEvent(EventTypes.MIDLET_RESUME_REQUEST),
                  midletSuiteId, midletClassName);
    }
    /**
     * Sends notification for MIDlet resources pause to the AMS isolate.
     *
     * @param midletSuiteId ID of the MIDlet suite
     * @param midletClassName Class name of the MIDlet
     */
    public void sendMIDletRsPauseNotifyEvent(int midletSuiteId,
                                             String midletClassName) {
        sendEvent(midletRsPausedEvent, midletSuiteId, midletClassName);
    }

    /**
     * Called by the display to request the central AMS to destroy the owning
     * MIDlet.
     *
     * @param midletDisplayId ID of the sending Display
     */
    public void sendMIDletDestroyRequestEvent(int midletDisplayId) {
        sendEvent(new NativeEvent(EventTypes.MIDLET_DESTROY_REQUEST_EVENT),
                midletDisplayId);
    }

    /*
     * Foreground MIDlet Management Events:
     *
     * SELECT_FOREGROUND - produced by native code
     * FOREGROUND_TRANSFER
     * SET_FOREGROUND_BY_NAME_REQUEST
     *
     */
    /**
     * Called to send a foreground MIDlet transfer event to the AMS isolate.
     * Former: NEW method, originally sent from CHAPI
     *
     * @param originMIDletSuiteId ID of MIDlet from which
     *        to take forefround ownership away,
     * @param originMIDletClassName Name of MIDlet from which
     *        to take forefround ownership away
     * @param targetMIDletSuiteId ID of MIDlet
     *        to give forefround ownership to,
     * @param targetMIDletClassName Name of MIDlet
     *        to give forefround ownership to
     */
    public void sendMIDletForegroundTransferEvent(
        int originMIDletSuiteId,
        String originMIDletClassName,
        int targetMIDletSuiteId,
        String targetMIDletClassName) {
        NativeEvent event =
            new NativeEvent(EventTypes.FOREGROUND_TRANSFER_EVENT);

        event.intParam1 = originMIDletSuiteId;
        event.intParam2 = targetMIDletSuiteId;

        event.stringParam1 = originMIDletClassName;
        event.stringParam2 = targetMIDletClassName;

        eventQueue.sendNativeEventToIsolate(event, amsIsolateId);
    }
    /**
     * Called to send a request to AMS isolate for a MIDlet be in
     * the foreground.
     *
     * @param suiteId MIDlet's suite ID
     * @param className MIDlet's class name
     */
    public void sendSetForegroundByNameRequestEvent(int suiteId,
            String className) {

        NativeEvent event =
            new NativeEvent(EventTypes.SET_FOREGROUND_BY_NAME_REQUEST);

        event.intParam1 = suiteId;
        event.stringParam1 = className;

        eventQueue.sendNativeEventToIsolate(event, amsIsolateId);
    }


    /**
     * Called to send a Display created notification to the AMS isolate.
     *
     * @param midletDisplayId ID of the sending Display
     * @param midletClassName Class name of the MIDlet that owns the display
     */
    public void sendDisplayCreateNotifyEvent(int midletDisplayId,
                                             String midletClassName) {
        NativeEvent event =
            new NativeEvent(EventTypes.DISPLAY_CREATED_NOTIFICATION);

        event.intParam1 = currentIsolateId;
        event.intParam2 = midletDisplayId;

        event.stringParam1 = midletClassName;

        eventQueue.sendNativeEventToIsolate(event, amsIsolateId);
    }

    /*
     * Foreground Display Management Events:
     *
     * FOREGROUND_REQUEST
     * BACKGROUND_REQUEST
     *
     */
    /**
     * Called to send a foreground request event to the AMS isolate.
     *
     * @param midletDisplayId ID of the sending Display
     * @param isAlert true if the current displayable is an Alert
     */
    public void sendDisplayForegroundRequestEvent(int midletDisplayId,
            boolean isAlert) {
        NativeEvent event =
            new NativeEvent(EventTypes.FOREGROUND_REQUEST_EVENT);

        if (isAlert) {
            event.intParam2 = 1;
        } else {
            event.intParam2 = 0;
        }

        sendEvent(event, midletDisplayId);
    }
    /**
     * Called to send a background request event to the AMS isolate.
     *
     * @param midletDisplayId ID of the sending Display
     */
    public void sendDisplayBackgroundRequestEvent(int midletDisplayId) {
        sendEvent(new NativeEvent(EventTypes.BACKGROUND_REQUEST_EVENT),
                midletDisplayId);
    }

    /*
     * Display Preemption Management Events:
     *
     * PREEMPT
     *
     */
    /**
     * Called to start preempting and end preempting.
     * Probably: will need more parameters, ex. MIDlet ID
     *
     * @param midletDisplayId ID of the sending Display
     */
    public void sendDisplayPreemptStartEvent(int midletDisplayId) {
        NativeEvent event =
            new NativeEvent(EventTypes.PREEMPT_EVENT);

        event.intParam2 = -1; /* start = true */

        sendEvent(event, midletDisplayId);
    }
    /**
     * Called to start preempting and end preempting.
     * Probably: will need more parameters, ex. MIDlet ID
     *
     * @param midletDisplayId ID of the sending Display
     */
    public void sendDisplayPreemptStopEvent(int midletDisplayId) {
        NativeEvent event =
            new NativeEvent(EventTypes.PREEMPT_EVENT);

        event.intParam2 = 0; /* start = false */

        sendEvent(event, midletDisplayId);
    }

    /**
     * Sends standard MIDlet controller event setting two integer parameters
     * for display ID and isolate ID. It is synchronized by the event to be 
     * sent to avoid inconsistent parameters setting.
     *
     * @param event event to be sent
     * @param midletDisplayId ID of the sending Display
     */
    private void sendEvent(NativeEvent event, int midletDisplayId) {
        synchronized (event) {
            event.intParam1 = currentIsolateId;
            event.intParam4 = midletDisplayId;
            eventQueue.sendNativeEventToIsolate(event, amsIsolateId);
        }
    }

    /**
     * Sends standard MIDlet controller event setting two parameters
     * for suite ID and class name. It is synchronized by the event to be 
     * sent to avoid inconsistent parameters setting.
     *
     * @param event event to be sent
     * @param midletSuiteId ID of the MIDlet suite
     * @param midletClassName Class name of the MIDlet
     */
    private void sendEvent(NativeEvent event, int midletSuiteId,
                           String midletClassName) {
        synchronized (event) {
            event.intParam1 = midletSuiteId;
            event.stringParam1 = midletClassName;
            eventQueue.sendNativeEventToIsolate(event, amsIsolateId);
        }
    }
}
