/*------------------------------------------------------------------------
                        H M C O N T R O L

 A module for coordinating several counters and histogram
 memories. One of the counters is the master counter and the rest are
 slaves which have to be kept in sync with the master in their
 operations.

 copyright: see copyright.h
 
 Mark Koennecke, June 2001
-------------------------------------------------------------------------*/

#include <stdlib.h>
#include <assert.h>
#include <tcl.h>
#include "fortify.h"
#include "hmcontrol.h"
#include "devexec.h"


/*-------------------------------------------------------------------------*/
static void *HMCGetInterface(void *pData, int ID)
{
  pHMcontrol self = NULL;

  self = (pHMcontrol)pData;
  assert(self);

  if(ID == COUNTID)
    return self->pCount;
  else
    return NULL;
}
/*------------------------------------------------------------------------*/
static int HMCHalt(void *pData)
{
  int i, retVal = OKOK, status;
  pHMcontrol self = NULL;

  self = (pHMcontrol)pData;
  assert(self);

  for(i = 0; i < self->nSlaves; i++)
  {
    status = self->slaves[i]->Halt(self->slaveData[i]);
    if(status != OKOK)
      retVal = status;
  }
  return retVal;
}
/*-----------------------------------------------------------------------*/
static int HMCStart(void *pData, SConnection *pCon)
{
  int i, status;
  pHMcontrol self = NULL;

  self = (pHMcontrol)pData;
  assert(self);

  for(i = 0; i < self->nSlaves; i++)
  {
    status = self->slaves[i]->StartCount(self->slaveData[i],pCon);
    if(status != OKOK)
    {
      HMCHalt(self);
      return status;
    }
  }
  return OKOK;
}
/*----------------------------------------------------------------------*/
static int HMCStatus(void *pData, SConnection *pCon)
{
  int status; 
  pHMcontrol self = NULL;

  self = (pHMcontrol)pData;
  assert(self);

  status = self->slaves[0]->CheckCountStatus(self->slaveData[0],pCon);
  if(status == HWIdle || status == HWFault)
  {
    /*
     stop counting on slaves when finished or when an error 
     occurred.
    */
    HMCHalt(self);
  }
  return status;
}
/*-------------------------------------------------------------------------*/
static int HMCPause(void *pData, SConnection *pCon)
{
  int i, status;
  pHMcontrol self = NULL;

  self = (pHMcontrol)pData;
  assert(self);

  for(i = 0; i < self->nSlaves; i++)
  {
    status = self->slaves[i]->Pause(self->slaveData[i],pCon);
    if(status != OKOK)
    {
      HMCHalt(self);
      return status;
    }
  }
  return OKOK;
}
/*------------------------------------------------------------------------*/
static int HMCContinue(void *pData, SConnection *pCon)
{
  int i, status;
  pHMcontrol self = NULL;

  self = (pHMcontrol)pData;
  assert(self);

  for(i = 0; i < self->nSlaves; i++)
  {
    status = self->slaves[i]->Continue(self->slaveData[i],pCon);
    if(status != OKOK)
    {
      HMCHalt(self);
      return status;
    }
  }
  return OKOK;
}
/*----------------------------------------------------------------------*/
static int HMCTransfer(void *pData, SConnection *pCon)
{
  int i, retVal = OKOK, status;
  pHMcontrol self = NULL;
  char pBueffel[132];

  self = (pHMcontrol)pData;
  assert(self);

  for(i = 0; i < self->nSlaves; i++)
  {
    status = self->slaves[i]->TransferData(self->slaveData[i], pCon);
    if(status != OKOK)
    {
      retVal = status;
      sprintf(pBueffel,"WARNING:  slave histogram %d failed to transfer data",
              i);
      SCWrite(pCon,pBueffel,eWarning);
    }
  }
  return retVal;
}
/*-----------------------------------------------------------------------*/
static void HMCParameter(void *pData, float fPreset, CounterMode eMode )
{
  int i;
  pHMcontrol self = NULL;

  self = (pHMcontrol)pData;
  assert(self);

  for(i = 0; i < self->nSlaves; i++)
  {
    self->slaves[i]->SetCountParameters(self->slaveData[i], fPreset,
                                        eMode);
  }
}
/*----------------------------------------------------------------------*/
static void KillHMcontrol(void *pData)
{
  pHMcontrol self;

  self = (pHMcontrol)pData;
  if(!self)
    return;

  if(self->pDes)
    DeleteDescriptor(self->pDes);
  if(self->pCount)
    free(self->pCount);
  free(self);
} 
/*-----------------------------------------------------------------------*/
int MakeHMControl(SConnection *pCon, SicsInterp *pSics, 
                          void *pData, int argc, char *argv[])
{
  int i, status;
  pHMcontrol pNew = NULL;
  char pBueffel[132];
  CommandList *pCom;
  pICountable pCount;

  /*
   need at least two parameters
  */
  if(argc < 3)
  {
    SCWrite(pCon,"ERROR: insufficient number of arguments to MakeHMControl",
	    eError);
    return 0;
  }

  /*
     allocate our data structure
  */
  pNew = (pHMcontrol)malloc(sizeof(HMcontrol));
  if(!pNew)
  {
    SCWrite(pCon,"ERROR: out of memory in MakeHMControl",eError);
    return 0;
  }
  memset(pNew,0,sizeof(HMcontrol));
  pNew->pDes = CreateDescriptor("HMcontrol");
  pNew->pCount = CreateCountableInterface();
  if(!pNew->pDes || ! pNew->pCount)
  {
    SCWrite(pCon,"ERROR: out of memory in MakeHMControl",eError);
    KillHMcontrol(pNew);
    return 0;
  }
  
  /*
    assign interface functions
  */
  pNew->pDes->GetInterface = HMCGetInterface;
  pNew->pCount->Halt = HMCHalt;
  pNew->pCount->StartCount = HMCStart;
  pNew->pCount->CheckCountStatus = HMCStatus;
  pNew->pCount->Pause = HMCPause;
  pNew->pCount->Continue = HMCContinue;
  pNew->pCount->TransferData = HMCTransfer;
  pNew->pCount->SetCountParameters = HMCParameter;

  /*
   now loop through the remaining arguments, thereby entering them into
   the slave list.
  */
  for(i = 2; i < argc; i++)
  {
    pCom = FindCommand(pSics,argv[i]);
    if(!pCom)
    {
      sprintf(pBueffel,"ERROR: object %s not found in MakeHMcontrol",
              argv[i]);
      SCWrite(pCon,pBueffel,eError);
      continue;
    }
    pCount = GetCountableInterface(pCom->pData);
    if(!pCount)
    {
      sprintf(pBueffel,"ERROR: object %s is NOT countable",
              argv[i]);
      SCWrite(pCon,pBueffel,eError);
      continue;
    }
    pNew->slaves[pNew->nSlaves] = pCount;
    pNew->slaveData[pNew->nSlaves] = pCom->pData;
    pNew->nSlaves++;
  }

  /*
   now install our action command and we are done
  */
  status = AddCommand(pSics,argv[1],HMControlAction,KillHMcontrol,
                         pNew);
  if(!status)
  {
    sprintf(pBueffel,"ERROR: duplicate command %s not created",argv[1]);
    SCWrite(pCon,pBueffel,eError);
    KillHMcontrol(pNew);
    return 0;
  }                  
  
  return 1;
}
/*-----------------------------------------------------------------------
  Syntax: whatever start preset mode
  ------------------------------------------------------------------------*/
int HMControlAction(SConnection *pCon, SicsInterp *pSics, 
                          void *pData, int argc, char *argv[])
{
  pHMcontrol self;
  char pBueffel[132];
  double dPreset;
  CounterMode eMode;
  int status;

  /* 
   checks
  */
  self = (pHMcontrol)pData;
  assert(self);
  if(argc < 4)
  {
    sprintf("ERROR: Usage %s start preset mode", argv[0]);
    SCWrite(pCon,pBueffel,eError);
    return 0;
  }
  strtolower(argv[1]);
  if(strcmp(argv[1],"start") == 0)  
  {
     /*
       interpret count parameters
     */
     status = Tcl_GetDouble(pSics->pTcl,argv[2],&dPreset);
     if(status != TCL_OK)
     {
       sprintf(pBueffel,"ERROR: failed to convert %s to number",
               argv[2]);
       SCWrite(pCon,pBueffel,eError);
       return 0;
     } 
     strtolower(argv[3]);
     if(strcmp(argv[3],"timer") == 0)
       eMode = eTimer;
     else if(strcmp(argv[3],"monitor") == 0)
       eMode = ePreset;
     else
     {
       sprintf(pBueffel,"ERROR: %s is no recognized count mode",argv[3]);
       SCWrite(pCon,pBueffel,eError);
       return 0;
     }

     /*
       set count parameters and go
     */
     self->pCount->SetCountParameters(self,(float)dPreset,eMode);
     status = StartDevice(pServ->pExecutor,"hmcontrol",self->pDes,
			  self,pCon,99);
     if(!status)
     {
       SCWrite(pCon,"ERROR: failed to start counting",eError);
       return 0;
     }
     SCSendOK(pCon);
  }
  else
  {
     SCWrite(pCon,"ERROR: subcommand not recognized",eError);
     return 0;
  }

  return 1;
}  


