/*---------------------------------------------------------------------------
arrobj.c

a array object based on pardef

Markus Zolliker, Feb 2006
----------------------------------------------------------------------------
*/

#include <stdio.h>
#include <limits.h>
#include "sics.h"
#include "splitter.h"
#include "pardef.h"
#include "initializer.h"

typedef struct ArrayItem {
  struct ArrayItem *next;
  char *name;
  char *value;
  char *unit;
} ArrayItem;

typedef struct {
  ParData p;
  ArrayItem *items, *freeItems;
  char *saveFile;
} ArrayObj;

static ParClass arrayObjClass = { "array", sizeof(ArrayObj) };


typedef struct WrtObjContext {
  FILE *file;
  int pos;
  char filename[PATH_MAX];
} WrtObjContext;

/*-----------------------------------------------------------------------*/
int WrtObjOpen(WrtObjContext * ctx, char *fileName)
{

  int iret;
  char *slashpos;

  /* create a temporary file first */
  slashpos = strrchr(fileName, '/');
  if (slashpos == NULL) {
    ctx->pos = 0;
  } else {
    ctx->pos = slashpos - fileName + 1;
  }
  iret =
      snprintf(ctx->filename, sizeof(ctx->filename), "%.*s.%s", ctx->pos,
               fileName, fileName + ctx->pos);
  if (iret < 0 || iret >= sizeof(ctx->filename)) {
    return 0;
  }
  remove(ctx->filename);        /* remove already existing temporary file */
  ctx->file = fopen(ctx->filename, "w");
  return ctx->file != NULL;
}

/*-----------------------------------------------------------------------*/
void WrtObj(WrtObjContext * ctx, char *objectName)
{
  CommandList *pCom = NULL;
  Dummy *pDum = NULL;

  if (ctx) {
    pCom = FindCommand(pServ->pSics, objectName);
    if (pCom == NULL) {
      return;
    }
    pDum = pCom->pData;
    if (pDum != NULL) {
      pDum->pDescriptor->SaveStatus(pCom->pData, pCom->pName, ctx->file);
    }
  }
}

/*-----------------------------------------------------------------------*/
void WrtObjClose(WrtObjContext * ctx)
{
  char finalName[PATH_MAX];

  if (ctx) {
    fclose(ctx->file);
    snprintf(finalName, sizeof finalName, "%.*s%s", ctx->pos,
             ctx->filename, ctx->filename + ctx->pos + 1);
    rename(ctx->filename, finalName);
  }
}

/*----------------------------------------------------------------------------*/
static int ArrayExists(void *object, void *arg, int argc, char *argv[])
{
  ArrayObj *arr = ParCast(&arrayObjClass, object);
  ArrayItem *item;

  assert(arr);
  if (argc == 1) {
    for (item = arr->items; item != NULL; item = item->next) {
      if (strcasecmp(argv[0], item->name) == 0) {
        ParPrintf(object, eValue, "1");
        return 1;
      }
    }
  }
  ParPrintf(object, eValue, "0");
  return 1;
}

/*----------------------------------------------------------------------------*/
static int ArrayItems(void *object, void *arg, int argc, char *argv[])
{
  ArrayObj *arr = ParCast(&arrayObjClass, object);
  ArrayItem *item;
  int l;
  char *result, *p;

  assert(arr);
  l = 1;
  for (item = arr->items; item != NULL; item = item->next) {
    l += strlen(item->name);
    l++;
  }
  result = calloc(l, 1);
  if (result) {
    p = result;
    for (item = arr->items; item != NULL; item = item->next) {
      strcpy(p, item->name);
      p += strlen(item->name);
      *p = ' ';
      p++;
    }
    if (p > result)
      p--;
    *p = '\0';
    ParPrintf(object, eValue, result);
    free(result);
  }
  return 1;
}

/*----------------------------------------------------------------------------*/
static int ArrayMakeItem(void *object, void *delete, int argc,
                         char *argv[])
/* make or delete */
{
  ArrayObj *arr = ParCast(&arrayObjClass, object);
  ArrayItem *item, **last;
  int iarg;

  assert(arr);
  if (argc < 1)
    goto Usage;
  /* missing check for user privilege */
  last = &arr->items;
  for (item = arr->items; item != NULL; item = item->next) {
    if (strcasecmp(argv[0], item->name) == 0) {
      if (delete) {
        *last = item->next;     /* remove item from list */
        item->next = arr->freeItems;
        arr->freeItems = item;
        return 1;
      }
      break;
    }
    last = &item->next;
  }
  if (delete) {
    ParPrintf(object, eError, "ERROR: %s.%s not found", arr->p.name,
              argv[0]);
    return 0;
  }
  if (item == NULL) {
    if (arr->freeItems) {
      item = arr->freeItems;
      arr->freeItems = item->next;
      if (item->name) {
        free(item->name);
        item->name = NULL;
      }
      item->next = NULL;
      *last = item;
    } else {
      item = calloc(1, sizeof(ArrayItem));
      if (item == NULL)
        return 0;
      *last = item;
      item->next = NULL;
      item->unit = NULL;
      item->value = NULL;
    }
  }
  if (item->unit) {
    free(item->unit);
    item->unit = NULL;
  }
  if (item->value) {
    free(item->value);
    item->value = NULL;
  }
  if (item->name != NULL) {
    if (0 != strcmp(item->name, argv[0])) {
      free(item->name);
      item->name = strdup(argv[0]);
    }
  } else {
    item->name = strdup(argv[0]);
  }
  iarg = 1;
  if (iarg < argc) {
    if (argv[iarg] && argv[iarg][0]) {
      item->value = argv[iarg];
    }
    iarg++;
    if (iarg < argc) {
      if (argv[iarg] && argv[iarg][0]) {
        item->unit = argv[iarg];
      }
      iarg++;
      if (iarg < argc)
        goto Usage;
    }
  }
  if (item->unit)
    item->unit = strdup(item->unit);
  if (item->value)
    item->value = strdup(item->value);
  ParInitPar(object, item->name);
  SCparChange(arr->p.conn);

  return 1;
Usage:
  ParPrintf(object, eError, "Usage: %s makeitem <name> [value] [unit]",
            arr->p.name);
  return 0;
}

/*----------------------------------------------------------------------------*/
static void ArrayObjParDef(void *object)
{
  ArrayObj *arr = ParCast(&arrayObjClass, object);
  FILE *saveFile;
  ArrayItem *item, *next;
  char *u, *v;
  WrtObjContext context;
  static int doNotNest = 0;
  int saveObjects;
  
  saveFile = ParSaveFile();
  if (!doNotNest && saveFile && arr->saveFile && *arr->saveFile) {
    saveObjects = WrtObjOpen(&context, arr->saveFile);
  } else {
    saveObjects = 0;
  }
  for (item = arr->items; item != NULL; item = item->next) {
    if (saveFile) {
      if (item->unit) {
        u = item->unit;
      } else {
        u = "";
      }
      if (item->value) {
        v = item->value;
      } else {
        v = "";
      }
      if (strchr(v, '\n') != NULL) {
        fprintf(saveFile, "%s makeitem %s {%s} \"%s\"\n", arr->p.name,
                item->name, v, u);
      } else {
        fprintf(saveFile, "%s makeitem %s \"%s\" \"%s\"\n", arr->p.name,
                item->name, v, u);
      }
      if (saveObjects) {
        WrtObj(&context, item->name);
      }
    }
    ParName(item->name);
    ParSave(2);
    ParAccess(usUser);
    if (item->unit) {
      ParTail(item->unit);
    } else {
      ParList(NULL);
    }
    ParStr(&item->value, "");
  }
  if (saveObjects) {
    doNotNest = 1;
    WrtObj(&context, arr->p.name);
    doNotNest = 0;
    WrtObjClose(&context);
  }
  ParName("makeitem");
  ParAccess(usUser);
  ParCmd(ArrayMakeItem, NULL);
  ParName("deleteitem");
  ParAccess(usUser);
  ParCmd(ArrayMakeItem, "del");
  ParName("exists");
  ParAccess(usSpy);
  ParCmd(ArrayExists, NULL);
  ParName("items");
  ParAccess(usSpy);
  ParCmd(ArrayItems, NULL);
  ParName("saveFile"); ParLogAs(NULL);
  ParAccess(usUser);
  ParStr(&arr->saveFile, "");
  ParStdDef();
  if (ParActionIs(PAR_KILL)) {
    for (item = arr->items; item != NULL; item = next) {
      next = item->next;
      if (item->name)
        free(item->name);
      if (item->unit)
        free(item->unit);
      if (item->value)
        free(item->value);
      free(item);
    }
    for (item = arr->freeItems; item != NULL; item = next) {
      next = item->next;
      if (item->name)
        free(item->name);
      if (item->unit)
        free(item->unit);
      if (item->value)
        free(item->value);
      free(item);
    }
  }
}

/*----------------------------------------------------------------------------*/
static int ArrayObjInit(SConnection * con, int argc, char *argv[],
                        int dynamic)
{
  ArrayObj *arr = NULL;
  char *creationCmd = NULL;

  if (dynamic) {
    creationCmd = Arg2Tcl(argc, argv, NULL, 0);
  }
  arr = ParMake(con, argv[1], &arrayObjClass, ArrayObjParDef, creationCmd);
  arr->freeItems = NULL;
  arr->items = NULL;
  return arr != NULL;
}

/*----------------------------------------------------------------------------*/
void ArrayObjStartup(void)
{
  ParMakeClass(&arrayObjClass, NULL);
  MakeDriver("array", ArrayObjInit, 0, "String Array Object");
}
