package org.objectweb.alarm.beans;

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 java.rmi.RemoteException;
import javax.rmi.PortableRemoteObject;
import org.objectweb.jonas.service.ServiceManager;
import org.objectweb.jonas.service.AbsServiceImpl;
import org.objectweb.jonas.service.ServiceException;
import org.objectweb.jonas.common.Log;
import org.objectweb.util.monolog.api.Logger;
import org.objectweb.util.monolog.api.BasicLevel;
/**
 * AlarmService implementation.
 */
public class AlarmService extends AbsServiceImpl {

    static private Logger logger = null;
    Context ictx = null;
    AlarmRecordHome arh = null;
    AlarmRecord arcount = null;
    LinkedList profilList = new LinkedList();

    /**
     * Init the Service.
     * Configuration information is passed thru a Context object.
     */
    public void doInit(Context ctx) throws ServiceException {

        if(logger == null)
            logger = Log.getLogger("org.objectweb.examples");
    }

    /**
     * Start the Service
     */
    public void doStart() throws ServiceException {
	logger.log(BasicLevel.DEBUG, "AlarmService - starting");

	// Get a ref on AlarmRecordHome
	try {
	    ictx = new InitialContext();
	    arh = (AlarmRecordHome) PortableRemoteObject.narrow(ictx.lookup("alarmrecord"), 
								AlarmRecordHome.class);
	} catch (NamingException e) {
	    logger.log(BasicLevel.ERROR, "AlarmService : 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();
		logger.log(BasicLevel.DEBUG, "AlarmService: "+alarmid+" alarm records");
	    } catch (Exception e) {
		if (trycount > 0) {
		    logger.log(BasicLevel.ERROR, "AlarmService - bad start"); 
		    return;
		}
		logger.log(BasicLevel.DEBUG, "AlarmTable does not exist: create it");
		try {
		    arh.create();
		} catch (Exception f) {
		    logger.log(BasicLevel.ERROR, "AlarmService: Cannot init database:"+e);
		}
		trycount++;
	    }
	}

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

	logger.log(BasicLevel.DEBUG, "AlarmService - started");
    }

    /**
     * Stop the Service
     */
    public void doStop() throws ServiceException {
	logger.log(BasicLevel.DEBUG, "AlarmService stopped");
    }

    /**
     * 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)
     */
    public synchronized void alarm(int severity, String from, String reason) {
	logger.log(BasicLevel.DEBUG, "AlarmService 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) {
	} catch (RemoteException e) {
	    logger.log(BasicLevel.ERROR, "AlarmService: "+e);
	}

	if (arec == null) {
	    // If New Alarm: Create the AlarmRecord
	    logger.log(BasicLevel.DEBUG, "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) {
		logger.log(BasicLevel.ERROR, "AlarmService: "+e);
	    } catch (RemoteException e) {
		logger.log(BasicLevel.ERROR, "AlarmService: "+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.
	    logger.log(BasicLevel.DEBUG, "AlarmRecord already known");
	    try {
		arec.update(severity);
	    } catch (RemoteException e) {
		logger.log(BasicLevel.ERROR, "AlarmService: "+e);
	    }
	}

    }

    /**
     * Mark an AlarmRecord as processed.
     * We don't remove it now to keep it in history.
     */
    public void forgetAlarm(String pk) throws RemoteException {
	logger.log(BasicLevel.DEBUG, "entering for "+pk);
	
	// Find this Alarm by its PK
	AlarmRecord arec = null;
	try {
	    arec = arh.findByPrimaryKey(pk);
	} catch (FinderException e) {
	    logger.log(BasicLevel.ERROR, "AlarmService Alarm not found");
	    throw new RemoteException("Alarm not found");
	} catch (RemoteException e) {
	    logger.log(BasicLevel.ERROR, "AlarmService: "+e);
	    throw e;
	}

	// Change Alarm state
	try {
	    arec.setProcessed();
	} catch (RemoteException e) {
	    logger.log(BasicLevel.ERROR, "AlarmService: "+e);
	    throw e;
	}
    }

    /**
     * Makes a new Profil
     */
    public Profil newProfil(String from, String maxsev) {
	logger.log(BasicLevel.DEBUG, "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;
    }

    /**
     * returns the list of available profils
     */
    public String [] getProfilNames() {
	logger.log(BasicLevel.DEBUG, "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]);
    }

    /**
     * returns a reference on Profil object, given its name.
     */
    public Profil getProfil(String name) {
	logger.log(BasicLevel.DEBUG, "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
     */
    public boolean delProfil(String name) {
	logger.log(BasicLevel.DEBUG, "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;
    }
}
