package astri.tcs.TCSHardwareDeviceImpl;

import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Logger;

import alma.acsErrTypeAlarmSourceFactory.ACSASFactoryNotInitedEx;
import alma.acsErrTypeAlarmSourceFactory.SourceCreationErrorEx;
import alma.alarmsystem.Timestamp;
import cern.laser.source.alarmsysteminterface.ASIException;
import cern.laser.source.alarmsysteminterface.AlarmSystemInterface;
import cern.laser.source.alarmsysteminterface.AlarmSystemInterfaceFactory;
import cern.laser.source.alarmsysteminterface.FaultState;

public class AlarmSender extends AlarmHelper {

	private ArrayList<Integer> m_alarmCodes =new ArrayList<Integer>();
	private AlarmSystemInterface m_alarmSource =null;
	private TreeMap<Integer, FaultState> m_alarms = new TreeMap<Integer, FaultState>();
	private TreeMap<Integer, Boolean> m_activated = new TreeMap<Integer, Boolean>();
   
	private TreeMap< Integer, Integer> m_terminateCommandCounter= new TreeMap< Integer, Integer>();
	private TreeMap<  Integer, Integer > m_activateCommandCounter= new TreeMap< Integer, Integer>();
	private TreeMap< Integer, AlarmInformation > m_alarmInformationMap = new TreeMap< Integer, AlarmInformation >();
    private Logger m_logger;
	private long m_inhibitionTime;
    private boolean m_disable;
    private FaultState faultState;
	
    public AlarmSender(Logger logger) throws ASIException {
    	super();
    	this.m_logger=logger;
		m_alarmSource=AlarmSystemInterfaceFactory.createSource("ALARM_SYSTEM_SOURCES");
	}

	@Override
	public boolean findAlarm(int code) {
		if (m_alarms.containsKey(code)) {
			return true;
		}
		return false;
	}

	@Override
	public void resetAlarms() {
		m_alarmCodes.clear();
	    m_alarms.clear();
	    m_activated.clear();
	    m_terminateCommandCounter.clear();
	    m_activateCommandCounter.clear();
	    m_alarmInformationMap.clear();
	    m_inhibitionTime= 0;
	    m_disable= false;

	}

	@Override
	public void initializeAlarms(String family, String member, ArrayList<AlarmInformation> alarmInformation) {
		if(m_alarmSource==null) {
			m_logger.severe("AlarmSystemInterfaceFactory is not be initialised");
			return;
		}	
		for( int counter = 0; counter < alarmInformation.size(); counter++)
	    {
	        // It is possible that an alarm gets that is already active gets
	        // initialized with a different member name. In this case
	        // the control alarm helper will clear the old alarm.
	        if(isAlarmSet(alarmInformation.get(counter).getAlarmCode()))
	        {
	            deactivateAlarm(alarmInformation.get(counter).getAlarmCode());
	        }

	        m_alarms.put(alarmInformation.get(counter).getAlarmCode(),
	            AlarmSystemInterfaceFactory.createFaultState(family, member, alarmInformation.get(counter).getAlarmCode()));
	        m_activated.put(alarmInformation.get(counter).getAlarmCode(),false);
	        m_terminateCommandCounter.put(alarmInformation.get(counter).getAlarmCode(), 0);
	        m_activateCommandCounter.put(alarmInformation.get(counter).getAlarmCode(),0);
	        m_alarmInformationMap.put(alarmInformation.get(counter).getAlarmCode(),alarmInformation.get(counter)) ;
	    }
	}

	@Override
	public void activateAlarm(int code) {
		
		try {
			send(code,true,false);
		} catch (ASIException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void deactivateAlarm(int code) {
		try {
			send(code,false,false);
		} catch (ASIException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	@Override
	public void updateAlarm(int code, boolean state) {
		try {
			send(code,state,false);
		} catch (ASIException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}


	@Override
	public boolean isAlarmSet(int code) {
		return (findAlarm(code) &&  m_activated.get(code));
	}

	@Override
	public void inhibitAlarms(long duration) {
		m_inhibitionTime=Math.max(m_inhibitionTime,System.currentTimeMillis()+duration);

	}

	@Override
	public void resetAlarmsInhibitionTime() {
		m_inhibitionTime = 0;

	}

	@Override
	public void disableAlarms() {
		m_disable = true;

	}

	@Override
	public void enableAlarms() {
		m_disable = false;

	}

	@Override
	public void terminateAllAlarms() {
		for( Map.Entry<Integer,AlarmInformation> entry : m_alarmInformationMap.entrySet())
		{
			Integer key=entry.getKey();
			AlarmInformation value =(AlarmInformation) entry.getValue();
			try {
				send(value.getAlarmCode(),false,false);
			} catch (ASIException e) {
				m_logger.severe(e.getMessage());
				
			}
	    }

	}

	@Override
	public void forceTerminateAllAlarms() {
		if (m_disable==true)
			for( Map.Entry<Integer,AlarmInformation> entry : m_alarmInformationMap.entrySet())
			{
				Integer key=entry.getKey();
				AlarmInformation value =(AlarmInformation) entry.getValue();
				try {
					send(value.getAlarmCode(),false,true);
				} catch (ASIException e) {
					m_logger.severe(e.getMessage());
					
				}
		    }
		else {
			m_logger.info("Alarms are disabled");
		}
	}

	@Override
	public String getAlarmDescription(int code) {
		String ret=null;
		if (findAlarm(code))
			ret = m_alarmInformationMap.get(code).getAlarmDescription();
		return ret;
	}

	@Override
	public String createErrorMessage() {
		String msg=null;
		Integer key; 
		Boolean value; 
		boolean firstMessage=true;
		for(Map.Entry<Integer,Boolean> entry : m_activated.entrySet()) {
			key = entry.getKey();
			value = entry.getValue();
		
			if (value) {
				if (firstMessage) {
					firstMessage=false;
				}
				else {
					msg=msg+"\n";
				}
			}
			msg=msg+getAlarmDescription(key);
	    }
		return msg;
	}

	@Override
	public void handleActivateAlarm(int code) {
		m_logger.severe("alarm rised:"+m_alarmInformationMap.get(code).getAlarmDescription());
	}

	@Override
	public void handleDeactivateAlarm(int code) {
		m_logger.info("alarm deacrivated:"+m_alarmInformationMap.get(code).getAlarmDescription());
	}

	@Override
	public void send(int code, boolean activate, boolean force) throws ASIException {
		if(m_alarmSource==null) {
			m_logger.severe("AlarmSystemInterfaceFactory is not be initialised");
			return;
		}	
		if(findAlarm(code) == false)
	    {
	        String msg="Trying to operate over alarm code "+Integer.toString(code)+"which does not exist.";
	        m_logger.severe( msg);
	        return;
	    }
		String state;
	    if((force == false)&& (m_disable == false))
	    {
	        // Do not activate  if already activated, and
	        // do not deactivate if not activated before
	        if(activate == m_activated.get(code))
	        {
	            return;
	        }
	        else if((m_inhibitionTime!= 0)
	        && (System.currentTimeMillis() < m_inhibitionTime))
	        {
	            // No alarms during inhibition time
	            return;
	        }
	        else
	        {
	            // Set inhibition time to zero so we avoid unnecessary call to
	            m_inhibitionTime= 0;
	        }

	        if(activate)
	        {
	            state = FaultState.ACTIVE;
	            m_activateCommandCounter.put(code,m_activateCommandCounter.get(code)+1);

	            if(m_activateCommandCounter.get(code)
	                < m_alarmInformationMap.get(code).getAlarmActivateCount())
	            {
	                return;
	            }

	            // reset the terminate counter
	            m_terminateCommandCounter.replace(code,0);
	        }
	        else
	        {
	            state = faultState.TERMINATE;
	            m_terminateCommandCounter.put(code,m_terminateCommandCounter.get(code)+1);

	            if(m_terminateCommandCounter.get(code)
	                < m_alarmInformationMap.get(code).getAlarmTerminateCount())
	            {
	                return;
	            }

	            m_activateCommandCounter.put(code,0);
	        }
	    }
	    else
	    {
	        state = faultState.TERMINATE;
	        m_terminateCommandCounter.replace(code,0);
	        m_activateCommandCounter.put(code,0);
	    }

	    m_activated.put(code,activate);
	    m_alarms.get(code).setDescriptor(state);

	    // create a Timestamp and use it to configure the FaultState
	    java.sql.Timestamp ts =new java.sql.Timestamp(System.currentTimeMillis());
	    m_alarms.get(code).setUserTimestamp(ts);

	    // push the FaultState using the AlarmSystemInterface previously created
	    //acsalarm::FaultState stateToPush(*fltstate);
	    m_alarmSource.push(m_alarms.get(code));
	    if(activate == true)
	    {
	        handleActivateAlarm(code);
	    }
	    else
	    {
	        handleDeactivateAlarm(code);
	    }

	}

}
