/*
 *
 *
 * 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.io.j2me.push;

import java.io.IOException;
import java.io.InterruptedIOException;

import javax.microedition.io.ConnectionNotFoundException;

import com.sun.midp.midlet.MIDletStateHandler;
import com.sun.midp.midlet.MIDletSuite;

import com.sun.midp.security.Permissions;

/**
 * PushRegistry implementation that checks parameters.
 */
public final class PushRegistryImpl {

    /**
     * Hides the default constructor.
     */
    private PushRegistryImpl() { }

    /**
     * Gets a midlet suite object.
     *
     * @return A reference to <code>MIDletSuite</code> object
     */
    private static MIDletSuite getMIDletSuite() {
        return MIDletStateHandler
                .getMidletStateHandler()
                .getMIDletSuite();
    }

    /**
     * Checks that the midlet is registered for the given suite.
     *
     * @param midletSuite host <code>MIDlet suite</code> (MUST be not
     *  <code>null</code>
     *
     * @param midlet Name of <code>MIDlet</code> class to check
     *
     * @throws ClassNotFoundException if <code>MIDlet</code> is not registered
     */
    static void checkMidletRegistered(
            final MIDletSuite midletSuite,
            final String midlet)
            throws ClassNotFoundException {
        // assert midletSuite != null;
        // RFC: the check below might be unnecessary
        if (midlet == null || midlet.length() == 0) {
            throw new ClassNotFoundException("MIDlet missing");
        }

        if (!midletSuite.isRegistered(midlet)) {
            throw new ClassNotFoundException("No MIDlet-<n> registration");
        }
    }

    /**
     * Checks validity of midlet class name.
     *
     * @param midletSuite host <code>MIDlet suite</code> (MUST be not
     *  <code>null</code>
     *
     * @param midlet Name of <code>MIDlet</code> class to check
     *
     * @throws ClassNotFoundException if <code>MIDlet</code> is invalid
     */
    private static void checkMidlet(
            final MIDletSuite midletSuite,
            final String midlet)
            throws ClassNotFoundException {
        checkMidletRegistered(midletSuite, midlet);

        /*
         * IMPL_NOTE: strings in MIDlet-<n> attributes (see the check
         *  above) are not verified to be the names of classes that
         *  subclass javax.microedition.midlet.MIDlet, therefore we
         *  need this check
         */
        final Class midletCls = Class.forName(midlet);
        final boolean isMIDlet = javax.microedition.midlet.MIDlet.class
            .isAssignableFrom(midletCls);

        if (!isMIDlet) {
            throw new ClassNotFoundException("Not a MIDlet");
        }
    }

    /**
     * Register a dynamic connection with the
     * application management software. Once registered,
     * the dynamic connection acts just like a
     * connection preallocated from the descriptor file.
     * The internal implementation includes the storage name
     * that uniquely identifies the <code>MIDlet</code>.
     *
     * @param connection generic connection <em>protocol</em>, <em>host</em>
     *               and <em>port number</em>
     *               (optional parameters may be included
     *               separated with semi-colons (;))
     * @param midlet  class name of the <code>MIDlet</code> to be launched,
     *               when new external data is available
     * @param filter a connection URL string indicating which senders
     *               are allowed to cause the MIDlet to be launched
     *
     * @exception  IllegalArgumentException if the connection string is not
     *               valid
     * @exception ConnectionNotFoundException if the runtime system does not
     *              support push delivery for the requested
     *              connection protocol
     * @exception IOException if the connection is already
     *              registered or if there are insufficient resources
     *              to handle the registration request
     * @exception ClassNotFoundException if the <code>MIDlet</code> class
     *               name can not be found in the current
     *               <code>MIDlet</code> suite
     * @exception SecurityException if the <code>MIDlet</code> does not
     *              have permission to register a connection
     * @exception IllegalStateException if the MIDlet suite context is
     *              <code>null</code>.
     * @see #unregisterConnection
     */
    public static void registerConnection(
            final String connection,
            final String midlet,
            final String filter)
            throws ClassNotFoundException,
                ConnectionNotFoundException, IOException {

        final Connection c = Connection.parse(connection);

        // Quick check of filter to be spec complaint
        if (filter == null) {
            throw new IllegalArgumentException("filter is null");
        }

        final MIDletSuite midletSuite = getMIDletSuite();

        /* This method should only be called by running MIDlets. */
        if (midletSuite == null) {
            throw new IllegalStateException("Not in a MIDlet context");
        }

        /*
         * Validate parameters and Push connection availability
         */
        checkMidlet(midletSuite, midlet);
        /*
         * IMPL_NOTE: as checkRegistration will need to parse
         *  connection and filter anyway, it might be a good
         *  idea to save the results
         */
        ConnectionRegistry.checkRegistration(c, midlet, filter);

        /*
         * Check permissions.
         */
        try {
            midletSuite.checkForPermission(Permissions.PUSH, null);
        } catch (InterruptedException ie) {
            throw new InterruptedIOException(
                "Interrupted while trying to ask the user permission");
        }

        ConnectionRegistry.registerConnection(midletSuite, c, midlet, filter);
    }

    /**
     * Remove a dynamic connection registration.
     *
     * @param connection generic connection <em>protocol</em>,
     *             <em>host</em> and <em>port number</em>
     * @exception SecurityException if the connection was
     *            not registered by the current <code>MIDlet</code>
     *            suite
     * @return <code>true</code> if the unregistration was successful,
     *         <code>false</code> the  connection was not registered.
     * @see #registerConnection
     */
    public static boolean unregisterConnection(final String connection) {

        /* Verify the connection string before using it. */
        if (connection == null || connection.length() == 0) {
            return false;
        }

        final MIDletSuite midletSuite = getMIDletSuite();
        if (midletSuite == null) {
            return false;
        }

        return ConnectionRegistry.unregisterConnection(midletSuite, connection);
    }

    /**
     * Return a list of registered connections for the current
     * <code>MIDlet</code> suite.
     *
     * @param available if <code>true</code>, only return the list of
     *      connections with input available
     * @return array of connection strings, where each connection is
     *       represented by the generic connection <em>protocol</em>,
     *       <em>host</em> and <em>port number</em> identification
     */
    public static String [] listConnections(final boolean available) {
        final MIDletSuite midletSuite = getMIDletSuite();
        if (midletSuite == null) {
            return null;
        }

        return ConnectionRegistry.listConnections(midletSuite, available);
    }

    /**
     * Retrieve the registered <code>MIDlet</code> for a requested connection.
     *
     * @param connection generic connection <em>protocol</em>, <em>host</em>
     *              and <em>port number</em>
     *              (optional parameters may be included
     *              separated with semi-colons (;))
     * @return  class name of the <code>MIDlet</code> to be launched,
     *              when new external data is available, or
     *              <code>null</code> if the connection was not
     *              registered
     * @see #registerConnection
     */
    public static String getMIDlet(final String connection) {

        /* Verify that the connection requested is valid. */
        if (connection == null || connection.length() == 0) {
            return null;
        }

        return ConnectionRegistry.getMIDlet(connection);
    }

    /**
     * Retrieve the registered filter for a requested connection.
     *
     * @param connection generic connection <em>protocol</em>, <em>host</em>
     *              and <em>port number</em>
     *              (optional parameters may be included
     *              separated with semi-colons (;))
     * @return a filter string indicating which senders
     *              are allowed to cause the MIDlet to be launched or
     *              <code>null</code> if the connection was not
     *              registered
     * @see #registerConnection
     */
    public static String getFilter(final String connection) {

        /* Verify that the connection requested is valid. */
        if (connection == null || connection.length() == 0) {
            return null;
        }

        return ConnectionRegistry.getFilter(connection);
    }

    /**
     * Register a time to launch the specified application. The
     * <code>PushRegistry</code> supports one outstanding wake up
     * time per <code>MIDlet</code> in the current suite. An application
     * is expected to use a <code>TimerTask</code> for notification
     * of time based events while the application is running.
     * <P>If a wakeup time is already registered, the previous value will
     * be returned, otherwise a zero is returned the first time the
     * alarm is registered. </P>
     *
     * @param midlet  class name of the <code>MIDlet</code> within the
     *                current running <code>MIDlet</code> suite
     *                to be launched,
     *                when the alarm time has been reached
     * @param time time at which the <code>MIDlet</code> is to be executed
     *        in the format returned by <code>Date.getTime()</code>
     * @return the time at which the most recent execution of this
     *        <code>MIDlet</code> was scheduled to occur,
     *        in the format returned by <code>Date.getTime()</code>
     * @exception ConnectionNotFoundException if the runtime system does not
     *              support alarm based application launch
     * @exception ClassNotFoundException if the <code>MIDlet</code> class
     *              name can not be found in the current
     *              <code>MIDlet</code> suite
     * @see Date#getTime()
     * @see Timer
     * @see TimerTask
     */
    public static long registerAlarm(
            final String midlet,
            final long time)
            throws ClassNotFoundException, ConnectionNotFoundException {

        final MIDletSuite midletSuite = getMIDletSuite();
        if (midletSuite == null) {
            throw new IllegalStateException("Not in a MIDlet context");
        }

        checkMidlet(midletSuite, midlet);
        try {
            midletSuite.checkForPermission(Permissions.PUSH, null);
        } catch (InterruptedException ie) {
            throw new RuntimeException(
                "Interrupted while trying to ask the user permission");
        }

        return ConnectionRegistry.registerAlarm(midletSuite, midlet, time);
    }
}
