/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999 Bull S.A.
 * Contact: jonas-team@objectweb.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.objectweb.jonas_ejb.container;

import java.io.Serializable;

import javax.ejb.EJBException;
import javax.ejb.NoSuchObjectLocalException;
import javax.ejb.Timer;
import javax.ejb.TimerHandle;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.objectweb.jonas.naming.NamingManager;
import org.objectweb.jonas_ejb.container.JContext;
import org.objectweb.jonas_lib.naming.ContainerNaming;
import org.objectweb.jonas.container.EJBServiceImpl;
import org.objectweb.jonas.service.ServiceManager;
import org.objectweb.jonas_timer.TraceTimer;
import org.objectweb.util.monolog.api.BasicLevel;

/**
 * TimerHandle implementation
 */
public class JTimerHandle implements TimerHandle {

    private long period;
    private long starttime;
    private long duration;
    private Serializable info;
    private String beanname;
    private String container;

    /**
     * Encoded PK. If CMP2 is not used, no encoding is done.
     */
    private Serializable pk;

    /**
     * Constructor
     */
    public JTimerHandle(long starttime, long duration, long period, Serializable info, String beanname, String container, Serializable pk) {
        if (TraceTimer.isDebug()) {
            TraceTimer.logger.log(BasicLevel.DEBUG, "New JTimerHandle start = " + starttime + ", initial = " + duration + ", period = " + period);
        }
        this.starttime = starttime;
        this.duration = duration;
        this.period = period;
        this.info = info;
        this.beanname = beanname;
        this.container = container;
        this.pk = pk;
    }

    /**
     * @return  a reference to the timer represented by this handle.
     * @throws java.lang.IllegalStateException If this method is invoked while the instance
     * is in a state that does not allow access to this method.
     * @throws NoSuchObjectLocalException If invoked on a handle whose associated timer
     * has expired or has been cancelled.
     * @throws EJBException If this method could not complete due to a system-level failure.
     */
    public Timer getTimer() throws IllegalStateException, NoSuchObjectLocalException, EJBException {

        // Must reject Timer operations according to the EJB spec
        // See EJB 2.1 page 87 (Stateful session beans)
        try {
            ContainerNaming naming = NamingManager.getInstance();
            Context ctx = naming.getComponentContext();
            JContext sc = (JContext) ctx.lookup("MY_SF_CONTEXT");
            if (sc.getState() != 2) {
                TraceTimer.logger.log(BasicLevel.DEBUG, "This operation is not allowed here");
                throw new IllegalStateException("This operation is not allowed here");
            }
        } catch (NamingException ne) {
            // No context registered. Assume it's OK.
            // It's the case for Entity or MDB.
        }

        EJBServiceImpl ejbserv = null;
        try {
            ejbserv = (EJBServiceImpl) ServiceManager.getInstance().getEjbService();
        } catch (Exception e) {
            TraceTimer.logger.log(BasicLevel.DEBUG, "Cannot use Timer outside jonas server");
            throw new IllegalStateException("Cannot use Timer outside jonas server");
        }
        JContainer cont = (JContainer) ejbserv.getContainer(container);
        if (cont == null) {
            TraceTimer.logger.log(BasicLevel.ERROR, "Cannot get container =" + container);
            throw new IllegalStateException("Cannot get container");
        }
        JFactory bf = (JFactory) cont.getBeanFactory(beanname);
        JTimerService timerservice = null;
        if (bf instanceof JEntityFactory) {
            // entity bean
            JEntityFactory ef = (JEntityFactory) bf;
            Serializable pks = ef.decodePK(pk);
            if (TraceTimer.isDebug()) {
                TraceTimer.logger.log(BasicLevel.DEBUG, "encoded PK=" + pk);
                TraceTimer.logger.log(BasicLevel.DEBUG, "decoded PK=" + pks);
            }
            JEntitySwitch es = ef.getEJB(pks);
            if (es == null) {
                TraceTimer.logger.log(BasicLevel.DEBUG, "No entity for this pk");
                throw new NoSuchObjectLocalException("No entity for this pk");
            }
            timerservice = (JTimerService) es.getEntityTimerService();
        } else {
            // stateless session or message driven bean
            timerservice = (JTimerService) bf.getTimerService();
        }
        if (timerservice == null) {
            TraceTimer.logger.log(BasicLevel.DEBUG, "Cannot retrieve TimerService");
            throw new IllegalStateException("Cannot retrieve TimerService");
        }
        // Get the Timer from the list.
        Timer ret = timerservice.getTimerByTime(duration, period, info);
        if (ret == null) {
            // The Timer should have been canceled.
            throw new NoSuchObjectLocalException("The Timer should have been canceled");
        }
        return ret;
    }

    /**
     * Restart a Timer read from disk (persistent timer)
     * @return
     * @throws IllegalStateException
     * @throws NoSuchObjectLocalException
     * @throws EJBException
     */
    public Timer restartTimer() throws IllegalStateException, NoSuchObjectLocalException, EJBException {
        try {
            ContainerNaming naming = NamingManager.getInstance();
            Context ctx = naming.getComponentContext();
            JContext sc = (JContext) ctx.lookup("MY_SF_CONTEXT");
            if (sc.getState() != 2) {
                TraceTimer.logger.log(BasicLevel.DEBUG, "This operation is not allowed here");
                throw new IllegalStateException("This operation is not allowed here");
            }
        } catch (NamingException ne) {
            // No context registered. Assume it's OK.
            // It's the case for Entity or MDB.
        }

        EJBServiceImpl ejbserv = null;
        try {
            ejbserv = (EJBServiceImpl) ServiceManager.getInstance().getEjbService();
        } catch (Exception e) {
            TraceTimer.logger.log(BasicLevel.DEBUG, "Cannot use Timer outside jonas server");
            throw new IllegalStateException("Cannot use Timer outside jonas server");
        }
        JContainer cont = (JContainer) ejbserv.getContainer(container);
        if (cont == null) {
            TraceTimer.logger.log(BasicLevel.ERROR, "Cannot get container =" + container);
            throw new IllegalStateException("Cannot get container");
        }
        JFactory bf = (JFactory) cont.getBeanFactory(beanname);
        JTimerService timerservice = null;
        if (bf instanceof JEntityFactory) {
            // entity bean
            JEntityFactory ef = (JEntityFactory) bf;
            Serializable pks = ef.decodePK(pk);
            if (TraceEjb.isDebugIc()) {
                TraceEjb.interp.log(BasicLevel.DEBUG, "encoded PK=" + pk);
                TraceEjb.interp.log(BasicLevel.DEBUG, "decoded PK=" + pks);
            }
            JEntitySwitch es = ef.getEJB(pks);
            if (es == null) {
                TraceTimer.logger.log(BasicLevel.DEBUG, "No entity for this pk");
                throw new NoSuchObjectLocalException("No entity for this pk");
            }
            timerservice = (JTimerService) es.getEntityTimerService();
        } else {
            // stateless session or message driven bean
            timerservice = (JTimerService) bf.getTimerService();
        }
        if (timerservice == null) {
            TraceTimer.logger.log(BasicLevel.DEBUG, "Cannot retrieve TimerService");
            throw new IllegalStateException("Cannot retrieve TimerService");
        }
        // Recreate a Timer with recomputed initial duration.
        duration += starttime - System.currentTimeMillis();
        if (duration < 100) {
            duration = 100;
        }
        Timer timer = timerservice.createTimer(duration, period, info);
        if (timer.getTimeRemaining() > 0) {
            TraceTimer.logger.log(BasicLevel.DEBUG, "timer restarted");
        } else {
            TraceTimer.logger.log(BasicLevel.DEBUG, "timer terminated");
            timer.cancel();
        }
        return timer;
    }
}
