/*
 * Licensed Materials - Property of Rogue Wave Software, Inc. 
 * © Copyright Rogue Wave Software, Inc. 2014, 2015 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */
package monitoring.generator;

import ilog.cpl.datasource.IlpMutableDataSource;
import ilog.cpl.model.IlpObject;
import ilog.tgo.model.IltAlarm;
import ilog.tgo.model.IltObject;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * An alarm generator implementation that creates TGO based alarms instances of 
 * <code>ilog.tgo.model.IltAlarm</code>.
 * 
 */
public class AlarmGenerator extends AbstractAlarmGenerator {

  /**
   * The data source that contains all the generated alarms.
   */
  protected IlpMutableDataSource alarmDataSource;

  /**
   * Creates an alarm generator.  
   * @param period used as the wait time between alarm generation
   * @param moDataSource used as the source of Managed Object identifiers to generate alarms on.
   * @param alarmDataSource used to store the generated alarms.
   *
   */
  public AlarmGenerator(int period, int maxNumberOfAlarms, IlpMutableDataSource moDataSource, IlpMutableDataSource alarmDataSource) {
    super(period, maxNumberOfAlarms, moDataSource);
    this.alarmDataSource = alarmDataSource;    
  }

  /**
   * Initialize the arrays of types with the appropriate TGO values.
   * <p>
   * This method must be called just once, at initialization time.
   */
  public static void initialize() {    

    //Alarm severities
    ALARM_SEVERITIES = getFieldsOfType(IltAlarm.Severity.class, IltAlarm.Severity.class);

    //Alarm trends 
    ALARM_TRENDS = getFieldsOfType(IltAlarm.TrendIndication.class, IltAlarm.TrendIndication.class); 

    //Probable causes
    PROBABLE_CAUSES = getFieldsOfType(IltAlarm.ProbableCause.class, IltAlarm.ProbableCause.class); 

    //Alarm types
    ALARM_TYPES = getFieldsOfType(IltAlarm.AlarmType.class, IltAlarm.AlarmType.class);    
  }

  /**
   * Returns a random generated alarm
   */
  private Object getRandomAlarm() {
    Object[] alarms = alarmDataSource.getObjects().toArray();
    return alarms.length > 0 ? alarms[randomGenerator.nextInt(alarms.length)] : null;
  }

  /**
   * Returns all the fields defined in the provided <code>type</code> that is of the provided
   * <code>attributeType</code>.
   */
  private static Object[] getFieldsOfType(Class<?> type, Class<?> attributeType) {
    Object[] fields = null;

    int i = 0;
    Field[] allFields = type.getFields();
    try {
      List<Object> fieldsList = new ArrayList<Object>(allFields.length);
      for (i = 0; i < allFields.length; i++) {

        Object fieldItself = allFields[i].get(null);

        if (fieldItself.getClass().isAssignableFrom(attributeType)) {
          fieldsList.add(fieldItself);          
        }
      }
      fields = fieldsList.toArray();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return fields;

  }

  /**
   * We do not generate severities of type <code>CLEAR</code>.
   *  
   * @see monitoring.generator.AbstractAlarmGenerator#generateRandomSeverity()
   */
  protected synchronized Object generateRandomSeverity() {
    Object severity = null;

    do {
      severity = super.generateRandomSeverity();      
    } while (severity == IltAlarm.Severity.Cleared);

    return severity;
  }

  /**
   * Clears the provided alarm. If the provided alarm is already acknowledged we also 
   * remove it from the alarm data source.
   * 
   * @see monitoring.generator.AbstractAlarmGenerator#clearOneAlarm(java.lang.Object)
   */
  public synchronized void clearOneAlarm(Object object) {
    if(object instanceof IltAlarm) {
      IltAlarm alarm = ((IltAlarm)object);
      alarm.setAttributeValue(IltAlarm.PerceivedSeverityAttribute, IltAlarm.Severity.Cleared);
      alarm.setAttributeValue(IltAlarm.AlarmClearedTimeAttribute, new Date());

      alarm.setClearSystemId(getSystemId());
      alarm.setClearUserId(getUserId());

      if(alarm.getAlarmAckState()) {
        removeAlarm(alarm);
      }
    } else {
      Object alarm = getRandomAlarm();
      if (null != alarm) clearOneAlarm(alarm);
    }
  }

  /**
   * Removes the alarm from the alarm and managed object data source.
   * 
   * @see monitoring.generator.AbstractAlarmGenerator#removeAlarm(java.lang.Object)
   */
  public synchronized void removeAlarm(IltAlarm alarm) {
    
    super.removeAlarm(alarm);
    
    if(alarm instanceof IltAlarm) {
      alarmDataSource.removeObject((IltAlarm)alarm, true);
      moDataSource.removeObject((IltAlarm)alarm, true);
    }
  }

  /**
   * Generates one alarm on a random Managed Object and adds this alarm to the 
   * alarm data source and to the managed object data source. 
   * 
   * @see monitoring.generator.AbstractAlarmGenerator#generateOneAlarm()
   */
  protected synchronized void generateOneAlarm() {
    generateOneAlarm(generateRandomMOI(), (IltAlarm.Severity)generateRandomSeverity());
  }
  
  /**
   * Generates one alarm on the given <code>IlpObject</code> with the provided severity.
   * <p/>
   * 
   * @param managedObject target managed object
   * @param severity alarm severity
   */
  public synchronized void generateOneAlarm(IlpObject managedObject, IltAlarm.Severity severity) {
    if (null != managedObject) {
      String alarmId = "alarm-" + System.currentTimeMillis();
      IltAlarm alarm = new IltAlarm(alarmId);
  
      alarm.setAttributeValue(IltObject.NameAttribute.getName(), managedObject.getAttributeValue(IltObject.NameAttribute)); 
      alarm.setAlarmAckState(false); 
      alarm.setAttributeValue(IltAlarm.AlarmTypeAttribute, generateRandomAlarmType());
      alarm.setAttributeValue(IltAlarm.PerceivedSeverityAttribute, severity);
      alarm.setAttributeValue(IltAlarm.ProbableCauseAttribute, generateRandomProbableCause());
  
      alarm.setManagedObjectClass(getMOC(managedObject));
      alarm.setManagedObjectInstance((String) managedObject.getIdentifier());
      Date d = new Date();
      alarm.setNotificationId(Long.toString(d.getTime()));
      alarm.setAlarmRaisedTime(d);
      alarm.setAttributeValue(IltAlarm.TrendIndicationAttribute, generateRandomTrend());
  
      alarmDataSource.addObject(alarm);
      generatedAlarms.add(alarm);
    }    
    //We need to pause for a bit so that the necessary notifications can 
    //propagate and be processed
    try {
      Thread.sleep(50);        
    } catch (Exception e) {
    }
  }

  /**
   * Removes all the alarms from the alarm data source and from the 
   * managed object data source.
   *  
   * @see monitoring.generator.AbstractAlarmGenerator#removeAllAlarms()
   */
  public synchronized void removeAllAlarms() {

    //remove them from the alarm data source 
    alarmDataSource.removeObjects(generatedAlarms, true);

    //remove them from the MO data source
    moDataSource.removeObjects(generatedAlarms, true);

    //Clear the alarms datasource
    generatedAlarms.clear();
  }

  public IlpMutableDataSource getAlarmDataSource() {
    return alarmDataSource;
  }
}