/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the License).  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the license at
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Copyright (c) Ericsson AB, 2004-2007. All rights reserved.
 */
package com.ericsson.ssa.sip;


import com.ericsson.ssa.container.startup.SipMonitoring;
import com.ericsson.ssa.sip.persistence.PersistenceUtil;


import com.sun.enterprise.ee.web.sessmgmt.ReplicationUtil;

import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.util.LifecycleSupport;

import org.jvnet.glassfish.comms.util.LogUtil;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;


/**
 * A singleton manager for dialog fragments.
 */
public class DialogFragmentManager implements Lifecycle {
    private static final LogUtil SIP_LOGGER = new LogUtil(LogUtil.SIP_LOG_DOMAIN);

    private static DialogFragmentManager dfManager = null;

    static {
        try {
            dfManager = (DialogFragmentManager) Class.forName(
                "org.jvnet.glassfish.comms.replication.dialogmgmt.ReplicationDialogFragmentManager").newInstance();
        } catch (Throwable t) {
            throw new ExceptionInInitializerError(t);
        }
    }

    private AtomicLong concurrentSipDialogs;
    private AtomicLong totalSipDialogCount;
    private AtomicLong totalSipDialogLifeTime;
    private AtomicLong expiredSipDialogs;

    // Active cache of dialog fragments
    protected ConcurrentHashMap<String, DialogFragment> dialogFragments;

    // Has this DialogFragmentManager been started?
    protected AtomicBoolean isStarted = new AtomicBoolean(); // defaults to false

    // Has this DialogFragmentManager been initialized?
    protected AtomicBoolean isInitialized = new AtomicBoolean(); //defaults to false

    // The lifecycle event support.
    protected LifecycleSupport lifecycle = new LifecycleSupport(this);

    /**
     * Constructor.
     *
     * Scope is protected since this is a singleton.
     */
    protected DialogFragmentManager() {
        dialogFragments = new ConcurrentHashMap<String, DialogFragment>();
        concurrentSipDialogs = new AtomicLong();
        totalSipDialogCount = new AtomicLong();
        totalSipDialogLifeTime = new AtomicLong();
        expiredSipDialogs = new AtomicLong();
    }

    /**
     * Gets the singleton DialogFragmentManager instance.
     *
     * @return The singleton DialogFragmentManager instance
     */
    public static DialogFragmentManager getInstance() {
        
        // FIXME.   Quick fix to make sure DialogFragmentManager gets started.
        if (dfManager != null && ! dfManager.isInitialized.get()) {
            try {
                // FIXME
                // Would like to have this called from org.jvnet.glassfish.comms.startup.SipServiceListener.lifecycleevent()
                // Call DialogFragmentManager.getInstance().start() after startSipStack() in lifecyleEvent().  
                // Also, have DialogFragmentManager.getInstance().stop() called after stopSipStack() in lifecycleEvent.
                dfManager.start();
            } catch (LifecycleException le) {
                // FIXME.  Log caught exception.
            }
        }
        return dfManager;
    }
   
    /**
     * Factory method to instansiate DialogFragments
     * @param ds
     * @return
     */
    public DialogFragment createDialogFragment(DialogSet ds) {
        return new DialogFragment(ds);
    }

    /**
     * Adds the given DialogFragment.
     *
     * @param d The DialogFragment to add
     */
    public void registerDialogFragment(DialogFragment d) {
 
        if (SipMonitoring.isEnabled(SipMonitoring.SESSION_MANAGER) && 
                (d.getFragmentId().equals(DialogFragment.DEFAULT_FRAGMENT_ID))) {
            concurrentSipDialogs.incrementAndGet();
        }
        
        dialogFragments.put(d.getDialogId(), d);
    }

    /**
     * Gets the DialogFragment with the given id.
     *
     * @param id The DialogFragment id to match
     *
     * @return The DialogFragment with matching id, or null if not found
     */
    public DialogFragment findDialogFragment(String id) {
        return dialogFragments.get(id);
    }

    /**
     * Removes the given DialogFragment.
     *
     * @param df The DialogFragment to be removed
     * @param hasTimedOut true if the DialogFragment is removed because it has
     * expired, false otherwise
     */
    public void removeDialogFragment(DialogFragment df, boolean hasTimedOut) {
        DialogFragment storedDlg = dialogFragments.remove(df.getDialogId());

        // if dialogfragment storedDlg is found/removed it was a "real" dialog
        if ((storedDlg != null) && SipMonitoring.isEnabled(SipMonitoring.SESSION_MANAGER) &&
                (df.getFragmentId().equals(DialogFragment.DEFAULT_FRAGMENT_ID))) {
            concurrentSipDialogs.decrementAndGet();
            totalSipDialogCount.incrementAndGet();
            totalSipDialogLifeTime.addAndGet(df.getDialogLifetime());

            if (SIP_LOGGER.isLoggable(Level.FINE)) {
                long nDialogs = totalSipDialogCount.get();
                double cumulativeLifetime = totalSipDialogLifeTime.doubleValue();
                double average = cumulativeLifetime / nDialogs;
                SIP_LOGGER.log(Level.FINE,
                    "DialogLifetime=" + df.getDialogLifetime() + "; nDialogs=" +
                    nDialogs + "; cumulativeLifetime=" + cumulativeLifetime +
                    ";averageLifetime=" + average);
            }

            if (hasTimedOut) {
                expiredSipDialogs.incrementAndGet();
            }
        }
    }

    /**
     * Gets the total number of dialog fragments that were ever created.
     *
     * @return The total number of dialog fragments that were ever created
     */
    public long getEasTotalSipDialogCount() {
        return totalSipDialogCount.longValue();
    }

    /**
     * Gets the number of dialog fragments that are currently active.
     *
     * @return The number of dialog fragments that are currently active.
     */
    public long getEasConcurrentSipDialogs() {
        return concurrentSipDialogs.longValue();
    }

    /**
     * Gets the cumulative lifetime of all dialog fragments.
     *
     * @return The cumulative lifetime of all dialog fragments
     */
    public long getEasTotalSipDialogLifeTime() {
        return totalSipDialogLifeTime.longValue();
    }

    /**
     * Gets the total number of dialog fragments that have expired.
     *
     * @return The total number of dialog fragments that have expired
     */
    public long getEasExpiredSipDialogs() {
        return expiredSipDialogs.longValue();
    }

    /**
     * Starts this DialogFramgmentManager.
     */
    public void start() throws LifecycleException {
        if (isInitialized.compareAndSet(false, true)) {
            init();
        }

        if (isStarted.compareAndSet(false, true)) {
            doStart();
        }
    }

    /**
     * Stops this DialogFragmentManager.
     */
    public void stop() throws LifecycleException {
        if (isStarted.compareAndSet(true, false)) {
            // only stop once
            doStop();
        }
    }

    /**
     * Adds a lifecycle event listener to this SipSessionManager.
     *
     * @param listener The lifecycle event listener to add
     */
    public void addLifecycleListener(LifecycleListener listener) {
        lifecycle.addLifecycleListener(listener);
    }

    /**
     * Gets the lifecycle event listeners of this SipSessionManager.
     *
     * @return Array (possibly of zero length) containing the lifecycle
     * event listeners of this SipSessionManager
     */
    public LifecycleListener[] findLifecycleListeners() {
        return lifecycle.findLifecycleListeners();
    }

    /**
     * Removes the give lifecycle event listener from this
     * SipSessionManager.
     *
     * @param listener The lifecycle event listener to remove
     */
    public void removeLifecycleListener(LifecycleListener listener) {
        lifecycle.removeLifecycleListener(listener);
    }

    protected void init() {
    }
    
    protected void doStart() {
    }
    
    protected void doStop() {
        // FIXME Clean up dialog fragments, ..
    }
}
