/*------------------------------------------------------------------------
                                  P S I

 This is the site specific interface to SICS for PSI. This file implements
 the interface defined in  ../site.h
 
 
 copyright: see file COPYRIGHT

 Mark Koennecke, June 2003 - May 2007

 Modules initialized with an installation sics command are added to 
 AddPsiCommands with SCMD.
 Modules initialized with a startup C command are added to SiteInit with
 INIT.
 There is no need to add include statements or prototype declarations for
 above items.
 However, for drivers added, we still need to declare the prototypes or
 include the header file.
 Markus Zolliker, Jan 2010
 -----------------------------------------------------------------------*/
#include <tcl.h>
#include "sics.h"
#include "site.h"
#include <motor.h>
#include <site.h>
#include "sinqhmdriv.i"
#include "tdchm.h"
#include "tecsdriv.h"
#include "itc4.h"
#include "bruker.h"
#include "ltc11.h"
#include "eurodriv.h"
#include "el755driv.h"
#include <evdriver.i>
#include "serial.h"
#include "sinqhttp.h"

/*--------------------------------------------------------------------------*/
void SiteInit(void)
{

#define INIT(F) { void F(void); F(); }

/* insert here initialization routines ... */

  INIT(IlmStartup);
  INIT(IpsStartup);
  INIT(ItcStartup);
  INIT(IghStartup);
  INIT(Euro2kStartup);
  INIT(StrObjStartup);
  INIT(ArrayObjStartup);
  INIT(Lsc370Startup);
  INIT(LinaStartup);
  INIT(HaakeStartup);
  INIT(AmiStartup);

  /*
   * SICS specific Asynchronous I/O protocols
   */
  INIT(AddAstriumnetProtocoll);
  INIT(AddJulChoProtocoll);
  INIT(AddHttpProtocoll);
  INIT(AddHttpOptProtocoll);
  INIT(AddPMACProtocoll);
  INIT(AddPfeifferProtocoll);
  INIT(AddTermProtocoll);
  INIT(AddPhytronProtocoll);
  INIT(AddSLSEchoProtocoll);
  INIT(AddCharByCharProtocoll);
  INIT(AddBinProtocol);
  INIT(AddSeaClientProtocol);
  INIT(AddDumProtocol);
  INIT(AddJVLProtocoll);
  INIT(AddSputterProtocoll);
  INIT(AddZwickrollProtocoll);

}


static pSite sitePSI = NULL;

/*
 * from tclClock.c
 */
int Tcl_ClockObjCmd (ClientData client, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);

/*----------------------------------------------------------------------*/
static void AddPsiCommands(SicsInterp * pInter)
{
/* declare and add permanent command */
#define PCMD(NAME, FUN) { \
     int FUN(SConnection * pCon, SicsInterp * pSics, void *pData, \
             int argc, char *argv[]); \
     AddCommandWithFlag(pInter, NAME, FUN, NULL, NULL, 0); \
   }
   
/* declare and add startup command */
#define SCMD(NAME, FUN) { \
     int FUN(SConnection * pCon, SicsInterp * pSics, void *pData, \
             int argc, char *argv[]); \
     AddCommandWithFlag(pInter, NAME, FUN, NULL, NULL, 1); \
   }

  /* alphabetic order */
  SCMD("InstallFocusMerge", InstallFocusMerge);
  SCMD("MakeAmorSet",       AmorSetFactory);
  SCMD("MakeAmorStatus",    AmorStatusFactory);
  SCMD("MakeECB",           MakeECB);
  SCMD("MakeFocusAverager", MakeFA);
  SCMD("MakeLMD200",        MakeLMD200);
  SCMD("MakePIMotor",       PIMotorFactory);
  SCMD("MakePoldiFold",     MakePoldiFold);
  SCMD("MakePSDFrame",      MakeFrameFunc);
  SCMD("MakeRitaFix",       MakeRitaFix);
  SCMD("MakeRitaWin",       MakeRitaWin);
  PCMD("MakeRuenBuffer",    InitBufferSys);
  SCMD("MakeSansliRebin",   MakeSansliRebin);
  SCMD("MakeSANSWave",      MakeSANSWave);
  SCMD("MakeSPS",           SPSFactory);
  SCMD("MakeSPSS7",         MakeSPSS7);
  PCMD("MakeSWHPMotor",     MakeSWHPMotor);
  SCMD("MakeSWMotor",       MakeSWMotor);
  SCMD("MakeTableDrive",    TableDriveFactory);
  SCMD("MakeTAS",           TASFactory);
  SCMD("MakeTRICSSupport",  MakeTricsSupport);
  SCMD("PolterInstall",     PolterInstall);
  SCMD("SerialInit",        SerialInit);
  SCMD("MakeEiger",         InitEiger);
  SCMD("MakeEigerMono",         InitEigerMono);
  PCMD("cnvrt",             CnvrtAction);
  SCMD("MakeEpicsAdapter",      MakeEpicsAdapter);

  /*
   * Tcl 8.5 has implemented the clock command in tcl rather then C.
   * This includes the same command, backported from Tcl 8.4
   */
  Tcl_CreateObjCommand(InterpGetTcl(pServ->pSics), "clock", Tcl_ClockObjCmd, NULL, NULL);

/*
    SCMD("MakeDifrac",MakeDifrac);
*/
}

/*---------------------------------------------------------------------*/
MotorDriver *CreateEL734HP(SConnection * pCon, int argc, char *argv[]);
MotorDriver *CreateEL734HPT(SConnection * pCon, int argc, char *argv[]);
MotorDriver *CreateEL734Air(SConnection * pCon, int argc, char *argv[]);
MotorDriver *MakePiPiezo(Tcl_Interp * pTcl, char *pArray);
/*-------------------------------------------------------------------*/
static pMotor CreatePsiMotor(SConnection * pCon, int argc, char *argv[])
{
  MotorDriver *pDriver = NULL;
  pMotor pNew = NULL;
  Tcl_Interp *pTcl = NULL;
  char pBueffel[132];

  if (strcmp(argv[1], "pipiezo") == 0) {
    pTcl = InterpGetTcl(pServ->pSics);
    pDriver = (MotorDriver *) MakePiPiezo(pTcl, argv[2]);
    if (!pDriver) {
      SCWrite(pCon, pTcl->result, eError);
      return NULL;
    }
    /* create the motor */
    pNew = MotorInit("PIPIEZO", argv[0], pDriver);
    if (!pNew) {
      snprintf(pBueffel,131, "Failure to create motor %s", argv[0]);
      SCWrite(pCon, pBueffel, eError);
      return NULL;
    }
  } else if (strcmp(argv[1], "el734hp") == 0) {
    pDriver = (MotorDriver *) CreateEL734HP(pCon, argc - 2, &argv[2]);
    if (!pDriver) {
      return NULL;
    }
    /* create the motor */
    pNew = MotorInit("EL734HP", argv[0], pDriver);
    if (!pNew) {
      snprintf(pBueffel,131, "Failure to create motor %s", argv[1]);
      SCWrite(pCon, pBueffel, eError);
      return NULL;
    }
  } else if (strcmp(argv[1], "el734air") == 0) {
    pDriver = (MotorDriver *) CreateEL734Air(pCon, argc - 2, &argv[2]);
    if (!pDriver) {
      return NULL;
    }
    /* create the motor */
    pNew = MotorInit("EL734air", argv[0], pDriver);
    if (!pNew) {
      snprintf(pBueffel,131, "Failure to create motor %s", argv[1]);
      SCWrite(pCon, pBueffel, eError);
      return NULL;
    }
  } else if (strcmp(argv[1], "el734hpt") == 0) {
    pDriver = (MotorDriver *) CreateEL734HPT(pCon, argc - 2, &argv[2]);
    if (!pDriver) {
      return NULL;
    }
    /* create the motor */
    pNew = MotorInit("EL734HPT", argv[0], pDriver);
    if (!pNew) {
      snprintf(pBueffel,131, "Failure to create motor %s", argv[1]);
      SCWrite(pCon, pBueffel, eError);
      return NULL;
    }
  }
  return pNew;
}

/*-------------------------------------------------------------------*/
extern pCounterDriver CreateEL737Counter(SConnection * pCon, char *name,
                                         int argc, char *argv[]);
extern pCounterDriver MakeEL737HP(SConnection * pCon, char *name,
                                  int argc, char *argv[]);
extern pCounterDriver MakeEL737HPV2(SConnection * pCon, char *name,
                                    int argc, char *argv[]);
pCounterDriver MakeEL737hpsps(SConnection * pCon, char *name,
                              int argc, char *argv[]);

pCounterDriver MakeEPICSCounter(char *rootname);
/*-------------------------------------------------------------------*/
static pCounterDriver CreatePsiCounterDriver(SConnection * pCon,
                                             int argc, char *argv[])
{
  pCounterDriver pNew = NULL;

  /*
     our caller has already checked for enough arguments
   */
  if (strcmp(argv[2], "el737") == 0) {
    pNew = CreateEL737Counter(pCon, argv[1], argc - 3, &argv[3]);
  } else if (strcmp(argv[2], "el737hp") == 0) {
    pNew = MakeEL737HP(pCon, argv[1], argc - 3, &argv[3]);
  } else if (strcmp(argv[2], "el737hpsps") == 0) {
    pNew = MakeEL737hpsps(pCon, argv[1], argc - 3, &argv[3]);
  } else if (strcmp(argv[2], "el737hpv2") == 0) {
    pNew = MakeEL737HPV2(pCon, argv[1], argc - 3, &argv[3]);
    /*
  } else if (strcmp(argv[2], "epics") == 0) {
    if (argc < 4) {
      SCWrite(pCon,
              "ERROR: insufficient no of arguments to create epics counter",
              eError);
      return NULL;
    }
    pNew = MakeEPICSCounter(argv[3]);
    */
  }
  return pNew;
}
/*--------------------------------------------------------------------*/
static HistDriver *CreatePsiHistMem(char *name, pStringDict pOptions)
{
  HistDriver *pNew = NULL;

  if (strcmp(name, "sinqhm") == 0) {
    pNew = CreateSINQDriver(pOptions);
  } else if (strcmp(name, "tdc") == 0) {
    pNew = MakeTDCHM(pOptions);
  } else if (strcmp(name, "sinqhttp") == 0) {
    pNew = CreateSinqHttpDriver(pOptions);
  }
  return pNew;
}

/*------------------------------------------------------------------*/
extern pCodri MakeCookerDriver(char *pHost, int iPort, int iChannel);
/*-------------------------------------------------------------------*/
static pCodri CreatePsiController(SConnection * pCon, int argc,
                                  char *argv[])
{
  pCodri pNew = NULL;
  Tcl_Interp *pTcl = InterpGetTcl(pServ->pSics);
  int iPort, iChannel, iRet, iSingle = 0;
  char pBueffel[512];

  if (strcmp(argv[0], "sanscook") == 0) {
    if (argc < 4) {
      SCWrite(pCon,
              "ERROR: Insufficient number of arguments to install SANS Cooker driver",
              eError);
      return NULL;
    }
    iRet = Tcl_GetInt(pTcl, argv[2], &iPort);
    if (iRet != TCL_OK) {
      snprintf(pBueffel, 511, "ERROR: expected integer as port number, got %s",
              argv[2]);
      SCWrite(pCon, pBueffel, eError);
      return NULL;
    }
    iRet = Tcl_GetInt(pTcl, argv[3], &iChannel);
    if (iRet != TCL_OK) {
      sprintf(pBueffel,
              "ERROR: expected integer as channel number, got %s",
              argv[3]);
      SCWrite(pCon, pBueffel, eError);
      return NULL;
    }
    pNew = MakeCookerDriver(argv[1], iPort, iChannel);
  }
  return pNew;
}

/*-----------------------------------------------------------------
 ConfigureController is a helper function which configures parameters
 for certain controller types
 --------------------------------------------------------------------*/
static void ConfigureController(char *name, pEVControl pNew,
                                SConnection * pCon)
{
  EVCSetPar(pNew, "upperlimit", 300.0, pCon);
  EVCSetPar(pNew, "lowerlimit", 1.0, pCon);
  if (strcmp(name, "tecs") == 0) {
    TecsCustomize(pCon, pNew);
  } else if (strcmp(name, "euro") == 0) {
    EVCSetPar(pNew, "upperlimit", 750.0, pCon);
    EVCSetPar(pNew, "lowerlimit", 15.0, pCon);
  } else if (strcmp(name, "el755") == 0) {
    EVCSetPar(pNew, "upperlimit", 10., pCon);
    EVCSetPar(pNew, "lowerlimit", -10., pCon);
  } else if (strcmp(name, "bruker") == 0) {
    EVCSetPar(pNew, "upperlimit", 45., pCon);
    EVCSetPar(pNew, "lowerlimit", 0., pCon);
  } else if (strcmp(name, "ltc11") == 0) {
    EVCSetPar(pNew, "upperlimit", 500., pCon);
    EVCSetPar(pNew, "lowerlimit", 1.5, pCon);
  }
}

/*-------------------------------------------------------------------*/
extern pEVDriver CreateSLSVMEDriv(int argc, char *argv[]);
/*------------------------------------------------------------------*/

static pEVControl InstallPsiEnvironmentController(SicsInterp * pSics,
                                                  SConnection * pCon,
                                                  int argc, char *argv[])
{
  pEVControl pNew = NULL;
  pEVDriver pDriv = NULL;
  int status = 1, checkError = 0, commandInstalled = 0;
  char pBueffel[512], pError[132];

  /*
     flag usage:
     checkError becomes 1 when a driver has been recognized and an error
     must be issued if something failed.
     commandInstalled becomes 1 when the command has already been installed 
     for the device. If 0 the default command will be installed later on
   */

  if (strcmp(argv[3], "tecs") == 0) {
    checkError = 1;
    pDriv = CreateTecsDriver(argc - 4, &argv[4]);
    if (pDriv != NULL) {
      pNew = CreateEVController(pDriv, argv[2], &status);
      if (pNew) {
        AddCommand(pSics, argv[2], TecsWrapper, DeleteEVController, pNew);
        commandInstalled = 1;
      }
    }
  } else if (strcmp(argv[3], "itc4") == 0) {
    checkError = 1;
    pDriv = CreateITC4Driver(argc - 4, &argv[4]);
    if (pDriv) {
      pNew = CreateEVController(pDriv, argv[2], &status);
      if (pNew != NULL) {
        AddCommand(pSics, argv[2], ITC4Wrapper, DeleteEVController, pNew);
        commandInstalled = 1;
      }
    }
  } else if (strcmp(argv[3], "bruker") == 0) {
    checkError = 1;
    pDriv = CreateBrukerDriver(argc - 4, &argv[4]);
    if (pDriv != NULL) {
      pNew = CreateEVController(pDriv, argv[2], &status);
      if (pNew != NULL) {
        AddCommand(pSics, argv[2], BrukerAction, DeleteEVController, pNew);
        commandInstalled = 1;
      }
    }
  } else if (strcmp(argv[2], "ltc11") == 0) {
    checkError = 1;
    pDriv = CreateLTC11Driver(argc - 4, &argv[4]);
    if (pDriv != NULL) {
      pNew = CreateEVController(pDriv, argv[2], &status);
      if (pNew != NULL) {
        AddCommand(pSics, argv[2], LTC11Action, DeleteEVController, pNew);
        commandInstalled = 1;
      }
    }
  } else if (strcmp(argv[3], "euro") == 0) {
    checkError = 1;
    pDriv = CreateEURODriv(argc - 4, &argv[4]);
    if (pDriv != NULL) {
      pNew = CreateEVController(pDriv, argv[2], &status);
    }
  } else if (strcmp(argv[3], "vme-dsp") == 0) {
    checkError = 1;
    pDriv = CreateSLSVMEDriv(argc - 4, &argv[4]);
    if (pDriv != NULL) {
      pNew = CreateEVController(pDriv, argv[2], &status);
    }
  } else if (strcmp(argv[3], "el755") == 0) {
    checkError = 1;
    pDriv = CreateEL755Driv(argc - 4, &argv[4]);
    if (pDriv != NULL) {
      pNew = CreateEVController(pDriv, argv[2], &status);
    }
  } else {
    snprintf(pBueffel,511, "ERROR: %s not recognized as a valid driver type",
            argv[3]);
    SCWrite(pCon, pBueffel, eError);
    return NULL;
  }

  /* if we recognized the driver, check for installation errors */
  if (checkError == 1) {
    if (pDriv == NULL) {
      SCWrite(pCon, "ERROR: failed to create driver", eError);
      return NULL;
    }
    if (status != 1) {
      SCWrite(pCon, "ERROR: failed to initialize device", eError);
      pDriv->GetError(pDriv, &status, pError, 131);
      snprintf(pBueffel, 511,"HW reported: %s", pError);
      SCWrite(pCon, pBueffel, eError);
    }
    if (pNew == NULL) {
      SCWrite(pCon, "ERROR: failed to create environment device object",
              eError);
      DeleteEVDriver(pDriv);
      return NULL;
    }
    if (commandInstalled == 0) {
      AddCommand(pSics, argv[2], EVControlWrapper, DeleteEVController,
                 pNew);
    }
    ConfigureController(argv[3], pNew, pCon);
  }

  return pNew;
}

/*-----------------------------------------------------------------*/
static int ConfigurePsiScan(pScanData self, char *option)
{
  return 0;
}

/*--------------------------------------------------------------------*/
static void KillPsi(void *site)
{
  free(site);
  sitePSI = NULL;
}

/*---------------------------------------------------------------------
  The scheme here goes along the lines of the singleton design pattern
  ---------------------------------------------------------------------*/
pSite getSite(void)
{
  if (sitePSI == NULL) {
    sitePSI = (pSite) malloc(sizeof(Site));
    /*
       we  cannot go on if we do not even have enough memory to allocate
       the site data structure
     */
    assert(sitePSI);
    /*
       initializing function pointers
     */
    sitePSI->AddSiteCommands = AddPsiCommands;
    sitePSI->RemoveSiteCommands = NULL;
    sitePSI->CreateMotor = CreatePsiMotor;
    sitePSI->CreateCounterDriver = CreatePsiCounterDriver;
    sitePSI->CreateHistogramMemoryDriver = CreatePsiHistMem;
    sitePSI->CreateVelocitySelector = NULL;
    sitePSI->CreateControllerDriver = CreatePsiController;
    sitePSI->InstallEnvironmentController =
        InstallPsiEnvironmentController;
    sitePSI->ConfigureScan = ConfigurePsiScan;
    sitePSI->KillSite = KillPsi;
  }
  return sitePSI;
}
