#include "SmartSamplerInterfaces.hh"
#include "Syslog.h"
#include "ssdbg.hh"

/****************************************************************************
 *
 * SmartSamplerInterface methods
 *
 ****************************************************************************/

// For some reason, the device interface for CTD comes under the name
// "driver" instead of the standard name given in the taskIF header
// file.  At some point, we should really be looking at both CTD's and
// choosing between them in a smart way.
#define ctdDriverName "ctdDriver"
//#define ctdDriverName "ctdDriver2"

SmartSamplerInterfaces::SmartSamplerInterfaces()
  :gulper_ifp(NULL),cluster_ifp(NULL),ctd_ifp(NULL),hs2_ifp(NULL),isus_ifp(NULL),
#ifndef PROFILE_FROM_DEPTH
   prof_ifp(NULL),
#endif
   depth_ifp(NULL){
  ssdbg(DBG_ATTACH)("SmartSampler -- Constructing interface manager.");
}
SmartSamplerInterfaces::~SmartSamplerInterfaces(){
  ssdbg(DBG_ATTACH)("SmartSampler -- Destroying interface manager.");
  if(NULL != gulper_ifp)delete(gulper_ifp);
  if(NULL != cluster_ifp)delete(cluster_ifp);
  if(NULL != ctd_ifp)delete(ctd_ifp);
  if(NULL != hs2_ifp)delete(hs2_ifp);
#ifndef PROFILE_FROM_DEPTH
  if(NULL != prof_ifp)delete(prof_ifp);
#endif
  if(NULL != depth_ifp)delete(depth_ifp);
}

void SmartSamplerInterfaces::updateCTD(ssi_float_t *tempOut,ssi_float_t *condOut,ssi_float_t *salOut, ssi_float_t *oxyOut){
  float freq,tval,cval,sval,v1,v2,v3,v4,v5,v6;
  TimeIF::TimeSpec timebuf;
  bool tok,cok;

  if(!((tempOut->track || condOut->track || salOut->track) && attachCTD()))return;

  ssdbg(DBG_ATTACH)("SSI -- Updating CTD");
#ifdef SELF_TEST
  ctd_ifp->update();
#endif

  tok = ((tempOut->track || salOut->track)) &&
    (DeviceIF::Ok == ctd_ifp->temperature(&tval,&freq,&timebuf));
  if(tok && timeLessThan(ptime_temp,timebuf)){
    tempOut->isnew = true;
    ptime_temp = timebuf;
  }else{
    tempOut->isnew = false;
  }

  cok = ((condOut->track || salOut->track)) &&
    (DeviceIF::Ok == ctd_ifp->conductivity(&cval,&freq,&timebuf));
  if(cok && condOut->track && timeLessThan(ptime_cond,timebuf)){
    condOut->isnew = true;
    ptime_cond = timebuf;
  }else{
    condOut->isnew = false;
  }

  if(salOut->track && (tempOut->isnew || condOut->isnew) &&
     (DeviceIF::Ok == ctd_ifp->salinity(&sval))){
    salOut->isnew = true;
    salOut->val = sval;
  }else{
    salOut->isnew = false;
  }

  // Temorarily used the isnew variables to see if we have a salinity
  // update, but we don't actually want them set to true if we're not
  // tracking...
  if(tempOut->isnew &= tempOut->track){
    tempOut->val = tval;
    ssdbg(DBG_DATA)("SSI -- New temperature value = %.4f, time=%.4f seconds\n",
	  tempOut->val,Time::seconds(&ptime_temp));
  }
  if(condOut->isnew &= condOut->track){
    condOut->val = cval;
    ssdbg(DBG_DATA)("SSI -- New conductivity value = %.4f, time=%.4f seconds\n",
	  condOut->val,Time::seconds(&ptime_cond));
  }
  if(salOut->isnew){
    ssdbg(DBG_DATA)("SSI -- New salinity value = %.4f\n",salOut->val);
  }

  // Dissolved Oxygen is a little simpler, except that it is not attached
  // on ctdDriver2.
  if(oxyOut->track && (DeviceIF::Ok == ctd_ifp->voltages(&v1,&v2,&v3,&v4,&v5,&v6))){
    oxyOut->isnew = true;
    oxyOut->val = v6;
    ssdbg(DBG_DATA)("SSI -- New dissolved oxygen value = %.4f\n",
		    oxyOut->val);
  }else{
    oxyOut->isnew = false;
  }
}

void SmartSamplerInterfaces::updateHS2(ssi_double_t *bbshortOut,ssi_double_t *bblongOut,ssi_double_t *fluorOut){
  hs2time_t timebuf;
  HydroscatIF::Data data;
  double secs;

  if(!((bbshortOut->track || bblongOut->track || fluorOut->track) && attachHS2()))return;

  ssdbg(DBG_ATTACH)("SSI -- Updating HS2");
#ifdef SELF_TEST
  hs2_ifp->update();
#endif

  bbshortOut->isnew = false;
  bblongOut->isnew = false;
  fluorOut->isnew = false;
  // Bit 0 of _hs2data.error == 1: too high background
  // Bit 1 of _hs2data.error == 1: too high signal
  // See page 33 in
  // "HydroScat2 Spectral Backscattering Sensor User's Manual, Revision H, June 2008"
  if((DeviceIF::Ok == hs2_ifp->getData(&data)) && 
     data.deviceReady && ((data.error & 3) == 0)){
    timebuf.secs = data.seconds;
    timebuf.hsecs = data.hseconds;
    if(timeLessThan(ptime_hs2,timebuf)){
      ptime_hs2 = timebuf;
      ssdbg(DBG_DATA)("SSI -- New hs2 data at time=%d seconds, %d hseconds",
	    ptime_hs2.secs,ptime_hs2.hsecs);
      if(bbshortOut->track){
	bbshortOut->isnew = true;
	bbshortOut->val = data.calculated.bb470;
	ssdbg(DBG_DATA)("SSI -- New bbshort value = %.4f",bbshortOut->val);
      }
      if(bblongOut->track){
	bblongOut->isnew = true;
	bblongOut->val = data.calculated.bb676;
	ssdbg(DBG_DATA)("SSI -- New bblong value = %.4f",bblongOut->val);
      }
      if(fluorOut->track){
	fluorOut->isnew = true;
	fluorOut->val = data.calculated.fl676_uncorr;
	ssdbg(DBG_DATA)("SSI -- New fl value = %.4f",fluorOut->val);
      }
    } // endif new
  } // endif getData
}

void SmartSamplerInterfaces::updateIsus(ssi_double_t *nitrateOut){
  IsusIF::Data data;
  if(!(nitrateOut->track && attachIsus()))return;

  ssdbg(DBG_ATTACH)("SSI -- Updating Isus");
#ifdef SELF_TEST
  isus_ifp->update();
#endif

  if((DeviceIF::Ok == isus_ifp->getData(&data)) && 
     data.deviceReady && !data.badComms){
    nitrateOut->isnew = true;
    nitrateOut->val = data.nitrate;
    ssdbg(DBG_DATA)("SSI -- New nitrate value = %.4f",
	  nitrateOut->val);
  }else{
    nitrateOut->isnew = false;
  }
}

void SmartSamplerInterfaces::updateCluster(ssi_cluster_t *clOut){
  unsigned short best;
  double dist;
  if(!attachCluster()) return;

  ssdbg(DBG_ATTACH)("SSI -- Updating cluster with classifier \"%s\"",clOut->classifier);
  if(ClusterIF::Ok == cluster_ifp->getCluster(clOut->classifier,&best,&dist) &&
     ((clOut->best != best) || (clOut->dist != dist))){
    clOut->isnew = true;
    clOut->best = best;
    clOut->dist = dist;
    ssdbg(DBG_DATA)("SSI -- New \"%s\" cluster best = %d, dist=%.4f",
	  clOut->classifier,clOut->best,clOut->dist);
  }else{
    ssdbg(DBG_ATTACH)("SSI -- No update. Best = %d (was %d), dist = %.4f (was %.4f).",
	  best,dist,clOut->best,clOut->dist);
    clOut->isnew = true;
  }
}

void SmartSamplerInterfaces::updateProfileCount(long *cnt){
#ifndef PROFILE_FROM_DEPTH
  if(!attachProfileCounter()) return;
  prof_ifp->update();
  prof_ifp->getCount(cnt);
  ssdbg(DBG_DATA)("SmartSampler -- Profile count returned %d!",*cnt);
#endif
}

void SmartSamplerInterfaces::updateDepth(ssi_double_t *depthOut){
  double val;
  TimeIF::TimeSpec timebuf;

  if(!(depthOut->track && attachDepthSensor()))return;

  ssdbg(DBG_ATTACH)("SSI -- Updating depth sensor");
#ifdef SELF_TEST
  depth_ifp->update();
#endif
  
  depthOut->isnew = false;
  if(depthOut->track && (DeviceIF::Ok == depth_ifp->depth(&val,&timebuf))
     && timeLessThan(ptime_depth,timebuf)){
    ptime_depth = timebuf;
    depthOut->isnew = true;
    depthOut->val = val;
    ssdbg(DBG_DATA)("SSI -- New depth value = %.4f, time=%.4f seconds",
		    depthOut->val,Time::seconds(&ptime_depth));
  } // endif new
}

// These gulper guys are private
bool SmartSamplerInterfaces::fireGulper(int gnum){
  if(attachGulper()){
    //    Syslog::write("SmartSampler -- Gulper fire requested, but gulpers are currently disabled.");
    gulper_ifp->fireGulper((long)gnum);
  }
  return(gulper_ifp != NULL);
}
bool SmartSamplerInterfaces::gulperState(GulperIF::gs10 state){
  if(attachGulper()){
    gulper_ifp->state(state);
  }
  return(gulper_ifp != NULL);
}


// Attachment methods.
bool SmartSamplerInterfaces::attachGulper(){
  if(gulper_ifp == NULL){
//try{gulper_ifp = new GulperIF(GulperIFServerName);}
    try{gulper_ifp = new GulperIF("Gulper");}
    catch(...){
      Syslog::write("SmartSampler -- Failed to attach to GulperIF");
      gulper_ifp = NULL;
    }
    if(gulper_ifp != NULL)
      ssdbg(DBG_ATTACH)("SmartSampler -- Successfully attached to GulperIF");
  }
  return(gulper_ifp != NULL);
}

bool SmartSamplerInterfaces::attachCluster(){
  if(cluster_ifp == NULL){
    try{cluster_ifp = new ClusterIF(ClusterIFServerName);}
    catch(...){
      Syslog::write("SmartSampler -- Failed to attach to ClusterIF");
      cluster_ifp = NULL;
    }
    if(cluster_ifp != NULL)
      ssdbg(DBG_ATTACH)("SmartSampler -- Successfully attached to ClusterIF");
  }
  return(cluster_ifp != NULL);
}

bool SmartSamplerInterfaces::attachCTD(){
  if(ctd_ifp == NULL){
#ifdef SELF_TEST
    ctd_ifp = new FakeCTD(ctdDriverName);
#else
    // WARNING!  Dissolved Oxygen is only found on ctdDriver, NOT on ctdDriver2!
    try{ctd_ifp = new CtdIF(CtdIFServerName,ctdDriverName);}
    catch(...){
      Syslog::write("SmartSampler -- Failed to attach to CTD driver \"%s\"",ctdDriverName);
      ctd_ifp = NULL;
    }
    if(ctd_ifp != NULL)
      ssdbg(DBG_ATTACH)("SmartSampler -- Successfully attached to CTD driver \"%s\"",ctdDriverName);
#endif
    if(ctd_ifp != NULL){
      // Initialize previous time variables.
      ptime_temp.seconds = 0;
      ptime_cond.seconds = 0;
      ptime_temp.nanoSeconds = 0;
      ptime_cond.nanoSeconds = 0;
    }
  }
  return(ctd_ifp != NULL);
}

bool SmartSamplerInterfaces::attachHS2(){
  if(hs2_ifp == NULL){
#ifdef SELF_TEST
    hs2_ifp = new FakeHS2();
#else
    try{hs2_ifp = new HydroscatIF(HydroscatIFServerName);}
    catch(...){
      Syslog::write("SmartSampler -- Failed to attach to %s",HydroscatIFServerName);
      hs2_ifp = NULL;
    }
    if(hs2_ifp != NULL)
      ssdbg(DBG_ATTACH)("SmartSampler -- Successfully attached to %s",HydroscatIFServerName);
#endif
    if(hs2_ifp != NULL){
      // Initialize previous time variables.
      ptime_hs2.secs = 0;
      ptime_hs2.hsecs = 0;
    }
  }
  return(hs2_ifp != NULL);
}

bool SmartSamplerInterfaces::attachIsus(){
  if(isus_ifp == NULL){
#ifdef SELF_TEST
    isus_ifp = new FakeIsus();
#else
    try{isus_ifp = new IsusIF(IsusIFServerName);}
    catch(...){
      Syslog::write("SmartSampler -- Failed to attach to %s",IsusIFServerName);
      isus_ifp = NULL;
    }
    if(isus_ifp != NULL)
      ssdbg(DBG_ATTACH)("SmartSampler -- Successfully attached to %s",IsusIFServerName);
#endif
  }
  return(isus_ifp != NULL);
}

bool SmartSamplerInterfaces::attachProfileCounter(){
#ifdef PROFILE_FROM_DEPTH
  return(true);
#else
  if(prof_ifp == NULL){
    ssdbg(DBG_ATTACH)("SmartSampler -- Attaching fake profile counter!");
    prof_ifp = new FakeProfile();
  }
  return(prof_ifp != NULL);
#endif
}

bool SmartSamplerInterfaces::attachDepthSensor(){
  if(depth_ifp == NULL){
#ifdef SELF_TEST
    depth_ifp = new FakeDepthSensor();
#else
    try{depth_ifp = new DepthSensorIF(DepthSensorIFServerName);}
    catch(...){
      Syslog::write("SmartSampler -- Failed to attach to %s",DepthSensorIFServerName);
      depth_ifp = NULL;
    }
    if(depth_ifp != NULL)
      ssdbg(DBG_ATTACH)("SmartSampler -- Successfully attached to %s",DepthSensorIFServerName);
#endif
  }
  return(depth_ifp != NULL);
}
