#include <Attributes.h>
#include <AttributeParser.h>
#include <StringAttribute.h>
#include <FloatAttribute.h>
#include <Syslog.h>
#include "ssdbg.hh"
#include "dtSimpleThreshold.hh"
#include "SmartSamplerExceptions.hh"
#include "System.h"

#define DEFAULT_THRESHOLD -1

/*********************************************************************************
 **
 ** class dtSimpleThreshold  -- Decision tool for choosing to gulp based on a hidden
 **       Markov Model of cluster state.
 **
 *********************************************************************************/

// structors :

dtSimpleThreshold::dtSimpleThreshold(char *nameIn)
  :name(strdup(nameIn)),ssd(NULL)
{
  ssdbg(DBG_LOAD)("creating dtSimpleThreshold \"%s\"",name);
}
dtSimpleThreshold::~dtSimpleThreshold(){
  ssdbg(DBG_CLEAN)("destroying dtSimpleThreshold \"%s\"",name);  
  free((void *)name);
}

// Manipulators :

// Parse configuration file for the datatype and threshold.
void dtSimpleThreshold::loadConfig(char *cfgname,SmartSamplerData *ssdIn){
  double ttemp,tsal,tcond,tnit,tbbs,tbbl,tfluor,tdepth,toxy;
  STData_t info;
  Attributes cfg_attributes(cfgname);
  const char *cfgFileName; 
  cfgFileName = System::configurationFile( cfgname );
  ssdbg(DBG_LOAD)("dtSimpleThreshold -- Loading config file at \"%s\"",cfgFileName);
  System::copyToLogDir(cfgFileName);

  ssd = ssdIn;

  cfg_attributes.add(new FloatAttribute("temp", "temperature firing threshold", 
					&ttemp, DEFAULT_THRESHOLD));
  cfg_attributes.add(new FloatAttribute("sal", "salinity firing threshold", 
					&tsal, DEFAULT_THRESHOLD));
  cfg_attributes.add(new FloatAttribute("cond", "conductivity firing threshold", 
					&tcond, DEFAULT_THRESHOLD));
  cfg_attributes.add(new FloatAttribute("oxy", "oxygen firing threshold", 
					&toxy, DEFAULT_THRESHOLD));
  cfg_attributes.add(new FloatAttribute("nitrate", "nitrate firing threshold", 
					&tnit, DEFAULT_THRESHOLD));
  cfg_attributes.add(new FloatAttribute("bbshort", "short wavelength backscatter firing threshold", 
					&tbbs, DEFAULT_THRESHOLD));
  cfg_attributes.add(new FloatAttribute("bblong", "long wavelength backscatter firing threshold", 
					&tbbl, DEFAULT_THRESHOLD));
  cfg_attributes.add(new FloatAttribute("fluor", "fluorescence firing threshold", 
					&tfluor, DEFAULT_THRESHOLD));
  cfg_attributes.add(new FloatAttribute("depth", "depth firing threshold", 
					&tdepth, DEFAULT_THRESHOLD));

  ssdbg(DBG_LOAD)("dtSimpleThreshold -- Parsing attributes.");
  // pick up attributes defined at this level:
  AttributeParser::reset();
  try{
      AttributeParser::parse(cfgFileName, &cfg_attributes); 
  }catch(...){
    throw LoadError("Failed to parse dtSimpleThreshold attributes.");
    exit(0);
  }
  if(ttemp > 0){
    info.type = SmartSamplerData::ssd_temp;
    info.initthresh = ttemp;
    ssd->useMeasurement(info.type);
    cfg.push_back(info);
    log.addElement((int)info.type,"temperature");
  }
  if(tsal > 0){
    info.type = SmartSamplerData::ssd_sal;
    info.initthresh = tsal;
    ssd->useMeasurement(info.type);
    cfg.push_back(info);
    log.addElement((int)info.type,"salinity");
  }
  if(toxy > 0){
    info.type = SmartSamplerData::ssd_oxy;
    info.initthresh = toxy;
    ssd->useMeasurement(info.type);
    cfg.push_back(info);
    log.addElement((int)info.type,"oxygen");
  }
  if(tcond > 0){
    info.type = SmartSamplerData::ssd_cond;
    info.initthresh = tcond;
    ssd->useMeasurement(info.type);
    cfg.push_back(info);
    log.addElement((int)info.type,"conductivity");
  }
  if(tnit > 0){
    info.type = SmartSamplerData::ssd_nitrate;
    info.initthresh = tnit;
    ssd->useMeasurement(info.type);
    cfg.push_back(info); 
    log.addElement((int)info.type,"nitrate");
  }
  if(tfluor > 0){
    info.type = SmartSamplerData::ssd_fluor;
    info.initthresh = tfluor;
    ssd->useMeasurement(info.type);
    cfg.push_back(info);
    log.addElement((int)info.type,"fluorescence");
  }
  if(tbbs > 0){
    info.type = SmartSamplerData::ssd_bbshort;
    info.initthresh = tbbs;
    ssd->useMeasurement(info.type);
    cfg.push_back(info);
    log.addElement((int)info.type,"bbshort");
  }
  if(tbbl > 0){
    info.type = SmartSamplerData::ssd_bblong;
    info.initthresh = tbbl;
    ssd->useMeasurement(info.type);
    cfg.push_back(info);
    log.addElement((int)info.type,"bblong");
  }
  if(tdepth > 0){
    info.type = SmartSamplerData::ssd_depth;
    info.initthresh = tdepth;
    ssd->useMeasurement(info.type);
    cfg.push_back(info);
    log.addElement((int)info.type,"depth");
  }
  reset();
  for(di_t di=cfg.begin();di!=cfg.end();di++){
    char datastr[16];
    ssd->mTypeToString((*di).type,datastr);
    Syslog::write("SmartSampler SimpleThreshold -- Tracking data type %s with initial threshold %f",datastr,(*di).thresh);
  }
}

// update the state based on current values, request
// gulper fire if any thresholds exceeded
bool dtSimpleThreshold::update(){
  bool fire = false;
  di_t di;
  SmartSamplerData::ssd_measurement_t id;
  SmartSamplerData::data_t curval;

  for(di=cfg.begin();di!=cfg.end();di++){
    id = (*di).type;
    ssd->getMeasurement(id,&curval);
    if(curval > (*di).thresh){
      char datastr[16];
      ssd->mTypeToString(id,datastr);
      Syslog::write("SmartSampler SimpleThreshold -- Requesting fire because %s data value=%f exceeds threshold=%f",
		    datastr,curval,(*di).thresh);
      fire = true;
      log.setValue((int)id,(double)curval);
      // Only trigger again if there is a 20% increase from this value.
      (*di).thresh = curval + curval/5;
    }
  }
  log.callWrite();
  return(fire);
}

void dtSimpleThreshold::reset(){
  di_t di;
  for(di=cfg.begin();di!=cfg.end();di++){
    (*di).thresh = (*di).initthresh;
  }
}
