#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <assert.h>
#include "myc_err.h"
#include "coc_logfile.h"
#include "coc_server.h"
#include "tecs_lsc.h"
#include "myc_str.h"

#define MAX_PAR 16
#define MAX_ARG 9

int LscEqPar(char *par, char *res) {
  char *p, *r, pbuf[SER_BUF_LEN], rbuf[SER_BUF_LEN];
  int i1,i2;
  float f1, f2;

  p=par;
  r=res;
  while (p!=NULL) {
    if (r==NULL) {
      return(0);
    }
    p=str_split(pbuf, p, ',');
    str_trim(pbuf, pbuf, sizeof(pbuf));
    r=str_split(rbuf, r, ',');
    str_trim(rbuf, rbuf, sizeof(rbuf));
    if (pbuf[0]!='\0' && 0!=strcasecmp(pbuf, rbuf)) {
      i1=sscanf(pbuf, "%f", &f1);
      i2=sscanf(rbuf, "%f", &f2);
      if (i1!=1 || i2!=1 || abs(f1-f2)>1e-4+abs(f1)*5e-6) {
        logfileOut(LOG_INFO, "%s#%s\n", pbuf, rbuf);
        return(0);
      }
    }
  }
  return(1);
}

#define nLIST 30

char *LscCmd(SerChannel *ser, const char *cmds) {
  char *blank, *colon, *qu, *res;
  const char *p, *this, *next, *cmd_ptr, *retreq;
  const char *list[nLIST];
  char seg[SER_BUF_LEN], result[SER_BUF_LEN], par[SER_BUF_LEN];
  char cmd[SER_BUF_LEN];
  char varname[32];
  DeclStrBuf(sbuf, SER_BUF_LEN);
  int nres, i, response, try;

  nres=0;
  response=0;
  this=cmds;
  while (this!=NULL) {
    next=str_split(cmd, this, ';');
    retreq=strchr(cmd, '>');
    if (retreq!=NULL) { /* save pointer to return request */
      i=retreq-cmd;
      cmd[i]='\0'; /* cut return request */
      list[nres]=this+i;
      nres++; assert(nres<=nLIST);
    }
    cmd_ptr=sbuf.buf+sbuf.wrpos; /* pointer to command in buffer */
    p=str_split(seg, cmd, '[');
    ERR_I(StrPut(&sbuf, seg, StrNONE));
    while (p!=NULL) { /* substitute variables */
      p=str_split(varname, p, ']');
      if (p==NULL) ERR_MSG("missing ']'");
      ERR_I(CocPutVar(varname, &sbuf, StrNONE));
      p=str_split(seg, p, '[');
      ERR_I(StrPut(&sbuf, seg, StrNONE));
    }
    ERR_I(str_copy(cmd, cmd_ptr));
    colon=strchr(cmd_ptr, ':');
    if (colon!=NULL) { /* command with query */
      blank=strchr(cmd_ptr, ' ');
      if (blank==NULL || blank>colon) {
        *colon=' '; blank=colon;
      } else {
        *colon=',';
      }
      list[nres]=colon+1; nres++; assert(nres<=nLIST); /* pointer to parameters */
      list[nres]=cmd_ptr; nres++; assert(nres<=nLIST); /* pointer to command */
      ERR_I(StrPut(&sbuf, ";", StrNONE));
      cmd[blank-cmd_ptr]='?'; /* build query */
      if (colon==blank) colon++;
      cmd[colon-cmd_ptr]='\0';
      ERR_I(StrPut(&sbuf, cmd, ';')); /* put query */
      response=1;
    } else { 
      qu=strchr(cmd, '?');
      ERR_I(StrPut(&sbuf, ";", StrNONE));
      if (qu!=NULL) { /* command is a query */
        response=1;
        if (retreq==NULL) {
          /* ERR_MSG("missing return format"); */
        }
        list[nres]=NULL; nres++; assert(nres<=nLIST);
      } else {
        if (retreq!=NULL) ERR_MSG("no return request allowed after command without query ");
      }
    }
    this=next;
  }

  if (!response) {
    ERR_I(StrPut(&sbuf, "busy?",'\0'));
  } else {
    sbuf.buf[sbuf.wrpos-1]='\0'; /* strip off trailing ";" */
  }
  
  try = 3;
  while (1) {
    ERR_P(res=SerCmd(ser, sbuf.buf));
    if (0!=strncmp("?TMO", res, 4)) {
      break;
    }
    try--;
    if (try <= 0) {
      ERR_MSG("timeout");
    }
  }
  /*
     list[0..nres-1] contains a now:
       for a command with return request:
       - a pointer to a the request
       then, for a command with query:
       - a pointer to the parameters and
       - a pointer to the command
     this is repeated for every command
  */

  /* check results */
  this=res;
  for (i=0; i<nres; i++) {
    if (this==NULL) ERR_MSG("not all results received");
    this=str_split(result, this, ';');
    retreq=list[i];
    if (retreq!=NULL) {
      if (retreq[0]=='>') {
        i++;
      } else {
        retreq=NULL;
      }
    }
    if (list[i]!=NULL) {
      qu=str_split(par, list[i], ';'); /* get parameters */
      i++;
      if (!LscEqPar(par, result)) { /* try again with single command */
        qu=strchr(qu, ';');
        assert(list[i]!=NULL);
        str_copy(cmd, list[i]);
        if (qu!=NULL) cmd[qu-list[i]]='\0';
        ERR_P(qu=SerCmd(ser, cmd));
        if (!LscEqPar(par, qu)) {
          if (0!=strcmp(result, qu)) {
            ERR_MSG("result does not match");
          } else {
            ERR_MSG("result does not match command");
          }
        }
      }
    }
    if (retreq!=NULL) { /* query */
      StrLink(&sbuf, result);
      str_split(par, retreq+1, ';');
      p=par;
      while (p!=NULL) {
        p=str_split(varname, p, ',');
        if (varname[0]!='\0') {
          if (p==NULL) { /* last element: get rest of line */
            ERR_I(CocGetVar(varname, &sbuf, '\0'));
          } else {
            ERR_I(CocGetVar(varname, &sbuf, ','));
          }
        } else {
          ERR_P(StrGet(&sbuf, varname, ','));
        }
      }
    }
  }
  return(res);
  OnError: return(NULL);
}


char *LscReadStat(int stat) {
  if (stat &128) return("units overrange");
  if (stat & 64) return("units zero");
  if (stat & 32) return("temp overrange");
  if (stat & 16) return("temp underrange");
  if (stat &  2) return("old reading");
  if (stat &  1) return("invalid reading");
  return ("");
}

static char
  *heaterStatus[7]={
    "",
    "heater supply over V",
    "heater supply under V",
    "heater output DAC error",
    "heater Ilimit DAC error",
    "open heater load",
    "heater load < 10 Ohm",
  };

char *LscHtrStat(int stat) {
  if (stat<0 || stat>sizeof(heaterStatus)) {
    return("unknown heater status");
  } else {
    return(heaterStatus[stat]);
  }
}
