#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "myc_mem.h"
#include "myc_err.h"
#include "myc_str.h"
#include "myc_time.h"
#include "myc_fortran.h"
#include "coc_util.h"
#include "tecs_cli.h"
#include "tecs_data.h"

static char response[COC_RES_LEN];
static char *rwCode="rwacs";
static char *rdCode="rdacs";

pTecsClient TeccStart(char *startcmd, char *host, int port) {
  CocConn *conn;
  int iret;

/* called by tecs client -> set write access later by function tecs_rights */

  NEW(conn, CocConn);
  ERR_I(iret=CocInitClient(conn, host, port, rdCode, 0, startcmd));
  if (iret==1) ErrShort("TECS_INIT: can not connect to TecsServer");
  return((pTecsClient)conn);
  OnError:
    if (conn!=NULL) FREE(conn);
    return NULL;
}

pTecsClient TeccInit(char *startcmd, int port) {
  CocConn *conn=NULL;
  int iret;

/* called by sics server -> always read and write */

  NEW(conn, CocConn);
  if (startcmd[0]=='#') { /* obsolete */
    ERR_I(iret=CocInitClient(conn, startcmd+1, port, rwCode, 0, ""));
  } else {
    ERR_I(iret=CocInitClient(conn, "", port, rwCode, 0, startcmd));
  }
  if (iret==1) ErrShort("TECS_INIT: can not connect to TecsServer");
  return((pTecsClient)conn);
  OnError:
    if (conn!=NULL) FREE(conn);
    return(NULL);
}

int TeccGet3(pTecsClient conn, float *tC, float *tX, float *tP) {
  int iret;

  CocReset(conn);
  ERR_I(CocGetFloat(conn, "set", tC));
  ERR_I(CocGetFloat(conn, "tempX", tX));
  ERR_I(CocGetFloat(conn, "tempP", tP));
  ERR_I(CocPutInt(conn, "readTemp", 1));
  ERR_I(iret=CocDoIt(conn, response, sizeof(response)));
  if (iret) ERR_MSG(response);
  return 0;
  OnError: return(-1);
}

int TeccGet(pTecsClient conn, float *temp) {
  int iret;

  CocReset(conn);
  ERR_I(CocGetFloat(conn, "tempP", temp));
  ERR_I(CocPutInt(conn, "readTemp", 1));
  ERR_I(iret=CocDoIt(conn, response, sizeof(response)));
  if (iret) ERR_MSG(response);
  return 0;
  OnError: return(-1);
}

int TeccGetX(pTecsClient conn, float *tC, float *tP, float *tDif) {
  int iret;
  float tx;

  CocReset(conn);
  CocReset(conn);
  ERR_I(CocGetFloat(conn, "set", tC));
  ERR_I(CocGetFloat(conn, "tx", &tx));
  ERR_I(CocGetFloat(conn, "tempP", tP));
  ERR_I(CocPutInt(conn, "readTemp", 1));
  ERR_I(iret=CocDoIt(conn, response, sizeof(response)));
  *tDif = *tC - tx;
  if (iret) ERR_MSG(response);
  return 0;
  OnError: return(-1);
}

int TeccGetMult(pTecsClient conn, int argc, char *argv[], void (* outfunc)(char *, void *), void *arg) {
  OutFunc f;
  int iret, i;

  f.func = outfunc;
  f.arg = arg;
  CocReset(conn);
  for (i=0; i<argc; i++) {
    ERR_I(CocGetOut(conn, argv[i], &f));
  }
  ERR_I(iret=CocDoIt(conn, response, sizeof(response)));
  if (iret) ERR_MSG(response);
  return 0;
  OnError: return -1;
}

int TeccSet(pTecsClient conn, float temp) {
  int iret;

  CocReset(conn);
  ERR_I(CocPutFloat(conn, "set", temp));
  ERR_I(iret=CocDoIt(conn, response, sizeof(response)));
  if (iret) ERR_MSG(response);
  return 0;
  OnError: return(-1);
}

int TeccQuitServer(pTecsClient conn, int kill) {
  int iret, cnt;

  ERR_I(iret=CocCheck(conn));
  if (iret>0) return iret;
  CocReset(conn);
  ERR_I(CocPutInt(conn, "quit", 1+kill));
  ERR_I(iret=CocDoIt(conn, response, sizeof(response)));
  if (iret) ERR_MSG(response);
  cnt=50;
  while (iret==0 && cnt>0) {
    CocDelay(100);
    ERR_I(iret=CocCheck(conn));
    cnt--;
  }
  if (iret==0) ERR_MSG("Does not quit within 5 seconds");
  return 0;
  OnError:
    return(-1);
}

int TeccSend(pTecsClient conn, char *cmd, char *reply, int replyLen) {
  return(CocSetGetN(conn, "send", cmd, reply, replyLen));
}

void TeccClose(pTecsClient conn) {
  if (conn!=NULL) {
    CocCloseClient(conn);
    FREE(conn);
  }
}

/* fortran wrappers --------------------------------------------------

  reduced functionality:
  connection is static, so only one connection at a time may be opened 

*/

#ifdef F_CHAR
/* compile only when fortran c interface stuff is defined */


static pTecsClient conn=NULL;

int F_FUN(tecs_set_par)(F_CHAR(name), F_CHAR(par), int *show, int name_len, int par_len) {
  char nbuf[64], pbuf[COC_CMD_LEN];

  STR_TO_C(nbuf, name);
  STR_TO_C(pbuf, par);

  CocReset(conn);
  ERR_I(CocPutStr(conn, nbuf, pbuf));
  ERR_I(CocDoIt(conn, response, sizeof(response)));
  if (*show) {
    printf("%s", response);
  }
  return 0;
  OnError: return(-1);
}

int F_FUN(tecs_get_par)(F_CHAR(name), F_CHAR(par), int *show, int name_len, int par_len) {
  char *b, nbuf[64], pbuf[COC_RES_LEN];

  STR_TO_C(nbuf, name);
  CocReset(conn);
  ERR_I(CocGetStr(conn, nbuf, pbuf, sizeof(pbuf)));
  ERR_I(CocDoIt(conn, response, sizeof(response)));
  if (*show==2) {
    b=response;
  } else {
    b=strchr(response,'=');
    if (b==NULL) {
      b=response;
    } else {
      b++;
    }
  }
  if (*show) {
    printf("%s", b);
  }
  return(STR_TO_F(par, pbuf));
  OnError: return(-1);
}

int F_FUN(tecs_get_mult)(F_CHAR(names), int *time, int *nvalues, float values[], int names_len) {
  char nbuf[64];
  char *nams, nam[32];
  int i;

  STR_TO_C(nbuf, names);
  CocReset(conn);

  ERR_I(CocGetInt(conn, "rdTim", time));
  i=0;
  nams=nbuf;
  while (nams!=NULL && i < *nvalues) {
    nams=str_split(nam, nams, ' ');
    if (nam[0]!='\0') {
      values[i]=DATA_UNDEF;
      CocGetFloat(conn, nam, values+i);
      i++;
    }
  }

  ERR_I(CocDoIt(conn, response, sizeof(response)));
  return 0;
  OnError: return(-1);
}

int F_FUN(tecs_init)(F_CHAR(startcmd), int *port, int startcmd_len) {
  char sbuf[132];

  STR_TO_C(sbuf, startcmd);
  ERR_P(conn=TeccInit(sbuf, *port));
  return 0;
  OnError: return(-1);  
}

int F_FUN(tecs_start)(F_CHAR(startcmd), F_CHAR(host), int *port, int startcmd_len, int host_len) {
  char sbuf[132], hbuf[64];

  STR_TO_C(sbuf, startcmd);
  STR_TO_C(hbuf, host);
  ERR_P(conn=TeccStart(sbuf, hbuf, *port));
  if (conn->fd<0) return 1;
  return 0;
  OnError: return(-1);
}

int F_FUN(tecs_rights)(int write) {
  if (write) {
    ERR_I(CocSendMagic(conn, rwCode));
  } else {
    ERR_I(CocSendMagic(conn, rdCode));
  }
  return 0;
  OnError: return(-1);  
}

int F_FUN(tecs_get)(float *temp) {
  ERR_I(TeccGet(conn, temp));
  return 0;
  OnError: return(-1);  
}

int F_FUN(tecs_get3)(float *t1, float *t2, float *t3) {
  ERR_I(TeccGet3(conn, t1, t2, t3));
  return 0;
  OnError: return(-1);  
}

int F_FUN(tecs_set)(float *temp) {
  ERR_I(TeccSet(conn, *temp));
  return 0;
  OnError: return(-1);  
}

int F_FUN(tecs_is_open)(void) {
  return(conn!=NULL);
}

void F_FUN(tecs_close)(void) {
  TeccClose(conn);
  conn=NULL;
}

int F_FUN(tecs_quit_server)(int *kill) {
  int iret;

  ERR_I(iret=TeccQuitServer(conn, *kill));
  return(iret);
  OnError: return(-1);  
}

int F_FUN(tecs_watch_log)(F_CHAR(list), int list_len) {
  char buf[16];
  STR_TO_C(buf, list);
  ERR_I(CocWatchLog(conn, buf));
  return 0;
  OnError: return(-1);  
}

int F_FUN(tecs_get_data)(F_CHAR(names), int *startTime, int *endTime, int *step, int *tbase
                  , float xdata[], float ydata[], int *maxLen, int *width
                  , int retLen[], int names_len) {
  char nam[64];
  char str[128];
  float offset, fact, *py, data[COC_RES_LEN/4-100];
  int i, j, k, l, iret;

  if (*endTime - *startTime > *step * (*maxLen-1)) {
    printf("maxLen too small\n");
  }
  STR_TO_C(nam, names);
  if (*step <= 0) {
    *step = 60;
  }
  sprintf(str, "%d %d %d %s", *startTime, *endTime, *step, nam);
  
  CocReset(conn);
  ERR_I(CocPutStr(conn, "grapar", str));
  ERR_I(CocGetArray(conn, "gradata", data, sizeof(data)/sizeof(float)));
  ERR_I(iret=CocDoIt(conn, response, sizeof(response)));
  if (iret) ERR_MSG(response);
  py=data;
  for (i=0; i < *width; i++) {
    l=*py; py++;
    if (l>0) {
      fact = (float)(*endTime - *startTime) / l;
    }
    offset = *startTime - *tbase;
    k = i * *maxLen;
    for (j=0; j<l; j++) {
      if (py[j] != DATA_GAP) {
        ydata[k] = py[j];
        xdata[k] = offset + j * fact;
        k++;
      }
    }
    py+=l;
    retLen[i] = k - i * *maxLen;
  }
  return 0;
  OnError:
    return -1;
}


/*
float tecs_date_(time_t *time) {
  struct tm tim;

  tim=*localtime(time);
  return(tim.tm_mday+(tim.tm_mon+1)*0.01);
}

time_t tecs_time_(float *date) {
  struct tm tim;
  time_t t, now;
  int m;

  time(&now);
  tim=*localtime(&now);
  tim.tm_hour=0;
  tim.tm_min=0;
  tim.tm_sec=0;
  tim.tm_mday=(int)*date;
  m=(int)(*date*100+0.1) % 100;
  if (m > 0) tim.tm_mon=m-1;
  t=mktime(&tim);
  if (t > now) {
    tim.tm_year--;
    t=mktime(&tim);
  }
  return t;
}
*/
#endif
