/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2004 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
 *
 * Initial developer: JOnAS Team
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.objectweb.alarm.beans;

import java.rmi.RemoteException;
import java.util.Iterator;
import java.util.LinkedList;

import javax.ejb.CreateException;
import javax.ejb.FinderException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;

/**
 * AlarmManager implementation.
 */
public class AlarmManager {

    /**
     * Unique instance of this class
     */
    private static AlarmManager unique = null;

    /**
     * Initial context
     */
    private Context ictx = null;

    /**
     * Home the bean AlarmRecord
     */
    private AlarmRecordHome arh = null;

    /**
     * Bean alarm Record
     */
    private AlarmRecord arcount = null;

    /**
     * List of profiles
     */
    private LinkedList profilList = new LinkedList();

    /**
     * Default constructor
     */
    private AlarmManager() {
        Debug.log("AlarmManager - creating");

        // Get a ref on AlarmRecordHome
        try {
            ictx = new InitialContext();
            arh = (AlarmRecordHome) PortableRemoteObject.narrow(ictx.lookup("alarmrecord"), AlarmRecordHome.class);
        } catch (NamingException e) {
            Debug.logError("AlarmManager : Cannot get AlarmRecordHome:" + e);
        }

        // Get number of alarms still in database
        // Create AlarmTable if does not exist
        int trycount = 0;
        while (arcount == null) {
            try {
                arcount = arh.findByPrimaryKey("0");
                int alarmid = arcount.getAlarmCount();
                Debug.log("AlarmManager: " + alarmid + " alarm records");
            } catch (Exception e) {
                if (trycount > 0) {
                    Debug.logError("AlarmManager - bad start");
                    return;
                }
                Debug.log("AlarmTable does not exist: create it");
                try {
                    arh.create();
                } catch (Exception f) {
                    Debug.logError("AlarmManager: Cannot init database:" + e);
                }
                trycount++;
            }
        }

        // Create a set of default profils
        newProfil("all", "A");

        Debug.log("AlarmManager - created");
    }

    /**
     * @return unique instance of this class
     */
    public static AlarmManager getInstance() {
        if (unique == null) {
            unique = new AlarmManager();
        }
        return unique;
    }

    /**
     * * a new Alarm is arrived must be synchronized to make sure we do not
     * create several AlarmRecord for the same Alarm (in case of many identical
     * Alarm arriving at the same time)
     * @param severity the level of severity
     * @param from the device name
     * @param reason the reason of the alarm
     */
    public synchronized void alarm(int severity, String from, String reason) {
        Debug.log("AlarmManager new Alarm from " + from + ": " + reason);

        // Search if alarm already known
        // Key information is made of "from"+"reason"
        AlarmRecord arec = null;
        try {
            arec = arh.findAlarm(from, reason);
        } catch (FinderException e) {
            Debug.logError("AlarmManager: " + e);
        } catch (RemoteException e) {
            Debug.logError("AlarmManager: " + e);
        }

        if (arec == null) {
            // If New Alarm: Create the AlarmRecord
            Debug.log("new AlarmRecord");
            AlarmData ad = null;
            try {
                // Allocate a unique ident
                int alarmid = arcount.getNewIdent();
                java.util.Date now = new java.util.Date();
                ad = new AlarmData(alarmid, severity, from, reason, new java.sql.Date(now.getTime()));
                arec = arh.create(ad);
            } catch (CreateException e) {
                Debug.logError("AlarmManager: " + e);
            } catch (RemoteException e) {
                Debug.logError("AlarmManager: " + e);
            }
            // Notice profiles interested
            Iterator i = profilList.iterator();
            while (i.hasNext()) {
                Profil prof = (Profil) i.next();
                if (prof.interestedBy(ad)) {
                    prof.noticeAlarm(arec);
                }
            }
        } else {
            // Old Alarm -> just increment count.
            Debug.log("AlarmRecord already known");
            try {
                arec.update(severity);
            } catch (RemoteException e) {
                Debug.logError("AlarmManager: " + e);
            }
        }

    }

    /**
     * Mark an AlarmRecord as processed. We don't remove it now to keep it in
     * history.
     * @param pk the primary key
     * @throws RemoteException as it is remote it can fails
     */
    public void forgetAlarm(String pk) throws RemoteException {
        Debug.log("entering for " + pk);

        // Find this Alarm by its PK
        AlarmRecord arec = null;
        try {
            arec = arh.findByPrimaryKey(pk);
        } catch (FinderException e) {
            Debug.logError("AlarmManager Alarm not found");
            throw new RemoteException("Alarm not found");
        } catch (RemoteException e) {
            Debug.logError("AlarmManager: " + e);
            throw e;
        }

        // Change Alarm state
        try {
            arec.setProcessed();
        } catch (RemoteException e) {
            Debug.logError("AlarmManager: " + e);
            throw e;
        }
    }

    /**
     * Makes a new Profil
     * @param from the device name
     * @param maxsev the maximum level of severity
     * @return the new profile created
     */
    public Profil newProfil(String from, String maxsev) {
        Debug.log("entering for " + from + "/" + maxsev);

        // Check if already exist
        Iterator i = profilList.iterator();
        while (i.hasNext()) {
            Profil prof = (Profil) i.next();
            if (prof.getDevice().equals(from) && prof.getSeverity().equals(maxsev)) {
                return null;
            }
        }

        Profil p = new Profil(from, maxsev, arh);
        profilList.add(p);
        return p;
    }

    /**
     * @return the list of available profils
     */
    public String[] getProfilNames() {
        Debug.log("entering");
        LinkedList nlist = new LinkedList();
        Iterator i = profilList.iterator();
        while (i.hasNext()) {
            Profil prof = (Profil) i.next();
            nlist.add(prof.getName());
        }
        return (String[]) nlist.toArray(new String[0]);
    }

    /**
     * @param name of the profile to remove
     * @return a reference on Profil object, given its name.
     */
    public Profil getProfil(String name) {
        Debug.log("entering for " + name);
        Profil ret = null;
        Iterator i = profilList.iterator();
        while (i.hasNext()) {
            Profil prof = (Profil) i.next();
            if (prof.getName().equals(name)) {
                ret = prof;
            }
        }
        return ret;
    }

    /**
     * remove a Profil
     * @param name the name of the profile to remove
     * @return true if the profile was removed
     */
    public boolean delProfil(String name) {
        Debug.log("entering for " + name);
        Iterator i = profilList.iterator();
        while (i.hasNext()) {
            Profil prof = (Profil) i.next();
            if (prof.getName().equals(name)) {
                i.remove();
                return true;
            }
        }
        return false;
    }
}