//-----------------------------------------------------------------------------
// Copyright (c) 1994,1995 Southeastern Universities Research Association,
//                         Continuous Electron Beam Accelerator Facility
//
// This software was developed under a United States Government license
// described in the NOTICE file included as part of this distribution.
//
//-----------------------------------------------------------------------------
//
// Description:
//      Implementation of table definition
//
// Author:  Jie Chen
//
// Revision History:
//   rsvcTableDef.cc,v
// Revision 1.1  1998/01/22  17:08:24  akers
// Addition of new NameServer
//
//
//
#include <rsvcConfig.h>
#include <rsvcServerConfig.h>
#include "rsvcTableDef.h"
#include <sys/types.h>
#include <sys/stat.h>

#ifndef _WIN32
#include <unistd.h>
#endif

rsvcTableDef::rsvcTableDef (char* name)
:table_ (), defined_ (0), keyname_ (0), keyexp_ (0), keytype_ (RSVC_INVALID),
 keyfunc_ (0), attr0_ (0), attr1_ (0)
{
#ifdef _TRACE_OBJECTS
  printf ("Create rsvcTableDef Class Object\n");
#endif
  name_ = new char[strlen (name) + 1];
  strcpy (name_, name);
}

rsvcTableDef::~rsvcTableDef (void)
{
#ifdef _TRACE_OBJECTS
  printf ("Delete rsvcTableDef Class Object\n");
#endif
  delete []name_;

  if (keyname_)
    delete []keyname_;
  if (keyexp_)
    delete []keyexp_;

  if (attr0_)
    delete []attr0_;
  if (attr1_)
    delete []attr1_;
}

int
rsvcTableDef::create (rsvcData& def)
{
  rsvcDataIterator ite (&def);
  int   match = 0;
  int   empty = 1;

  for (ite.init (); !ite; ++ite) {
    
    if (strcmp (ite.tag (), rsvcServerConfig::table ()) == 0)
      match ++;
    else if (strcmp (ite.tag (), rsvcServerConfig::key ()) == 0)
      match ++;
    else if (strcmp (ite.tag (), rsvcServerConfig::keyExp ()) == 0)
      match ++;
    else if (strcmp (ite.tag (), rsvcServerConfig::keyType ()) == 0)
      match++;
    else
      empty = 0;
  }
  if (match == 4 && !empty) {
    table_ = def;
    defined_ = 1;

    // get key name and key exp
    char temp[256];
    def.get (rsvcServerConfig::key (), temp, sizeof (temp));
    keyname_ = new char[strlen (temp) + 1];
    strcpy (keyname_, temp);

    def.get (rsvcServerConfig::keyExp (), temp, sizeof (temp));
    keyexp_ = new char[strlen (temp) + 1];
    strcpy (keyexp_, temp);    

    keytype_ = def.getType (rsvcServerConfig::keyType ());
    switch (keytype_) {
    case RSVC_BYTE:
    case RSVC_INT16:
    case RSVC_UINT16:
    case RSVC_INT32:
    case RSVC_UINT32:
      keytype_ = RSVC_INT32;
      break;
    case RSVC_FLOAT:
    case RSVC_DOUBLE:
      keytype_ = RSVC_DOUBLE;
      break;
    };

    if (parseKeyExp (keyexp_) != RSVC_SUCCESS)
      return RSVC_ERROR;
    
    return RSVC_SUCCESS;
  }
  else
    return RSVC_ERROR;
}

int
rsvcTableDef::create (void)
{
  FILE* fd = 0;

  char* filename = tableDefFileName (name_);
  
  fd = fopen (filename, "r");
  if (!fd) {
    fprintf (stderr, "Cannot open database table definition file %s\n", 
	     filename);
    return RSVC_ERROR;
  }

  struct stat ffs;

  if (fstat (fileno (fd), &ffs) != 0) {
    fprintf (stderr, "Cannot do fstat on table def file %s\n", filename);
    fclose (fd);
    return RSVC_ERROR;
  }
  size_t buflen = (unsigned int)ffs.st_size;

  if (buflen == 0) {
    fprintf (stderr, "Fatal: file size on table def file %s is zero\n", filename);
    fclose (fd);
    return RSVC_ERROR;
  }

  char* buffer = new char[buflen];
  
  if (read (fileno (fd), (void *)buffer, buflen) != buflen) {
    fprintf (stderr, "Fatal: Read Error from table def %s\n", filename);
    fclose (fd);
    return RSVC_ERROR;
  }
  fclose (fd);
  
  rsvcData tbdata;
  if (tbdata.streamIn (buffer, buflen) != RSVC_SUCCESS) {
    delete []buffer;
    return RSVC_ERROR;
  }

  delete []buffer;
  return create (tbdata);
}

int
rsvcTableDef::output (void)
{
  if (!defined_)
    return RSVC_ERROR;

  FILE* fd = 0;
  char* filename = tableDefFileName (name_);
  
  fd = fopen (filename, "w");
  if (!fd) {
    fprintf (stderr, "Cannot write database table def file %s\n", filename);
    return RSVC_ERROR;
  }
  
  char* buffer;
  size_t len;

  if (table_.streamOut (&buffer, &len) != RSVC_SUCCESS) {
    fprintf (stderr, "Cannot stream table definition %s\n", filename);
    return RSVC_ERROR;
  }

  if (write (fileno (fd), buffer, len) != len) {
    fprintf (stderr, "Writing table def to %s file failed\n", filename);
    delete []buffer;
    return RSVC_ERROR;
  }

  fclose (fd);
  delete []buffer;
  return RSVC_SUCCESS;
}

int
rsvcTableDef::output (rsvcData& data)
{
  if (!defined_)
    return RSVC_ERROR;

  data = table_;

  return RSVC_SUCCESS;
}

int
rsvcTableDef::tableDefined (void) const
{
  return defined_;
}

char*
rsvcTableDef::name (void) const
{
  return name_;
}

int
rsvcTableDef::rowNameValid (char* name)
{
  if (!defined_)
    return RSVC_ERROR;

  void* data;
  if (table_.find (name, data) != RSVC_SUCCESS)
    return 0;
  return 1;
}

int
rsvcTableDef::typeConvertable (int type0, int type1)
{
  int st = 1;
  
  if (type0 >= RSVC_INVALID || type0 < RSVC_BYTE ||
      type1 >= RSVC_INVALID || type1 < RSVC_BYTE)
    return 0;

  switch (type0) {
  case RSVC_BYTE:
  case RSVC_INT16:
  case RSVC_UINT16:
  case RSVC_INT32:
  case RSVC_UINT32:
  case RSVC_FLOAT:
  case RSVC_DOUBLE:
    if (type1 == RSVC_STRING || type1 == RSVC_TIMESTAMP)
      st = 0;
    break;
  case RSVC_STRING:
    if (type1 != RSVC_STRING)
      st = 0;
    break;
  case RSVC_TIMESTAMP:
    if (type1 != RSVC_STRING)
      st = 0;
    break;
  default:
    st = 0;
  }
  return st;
}

int
rsvcTableDef::rowValid (char* name, int type)
{
  if (!defined_)
    return RSVC_ERROR;
  
  rsvcDataTypes datatype;
  datatype = table_.getType (name);

  if (rsvcTableDef::typeConvertable (type, datatype))
    return 1;
  return 0;
}

int
rsvcTableDef::dataValid (rsvcData& data)
{
  if (!defined_)
    return RSVC_ERROR;

  rsvcDataIterator ite0 (&data);
  int datatype;

  rsvcDataEntry *entry = 0;
  for (ite0.init (); !ite0; ++ite0) {
    entry = ite0 ();
    datatype = table_.getType (entry->tag_);
    if (strcmp (entry->tag_, keyname_) != 0) {  
      // do not need to check key name tag
      if (!rsvcTableDef::typeConvertable (datatype, entry->dataType_)) {
#ifdef _RSVC_DEBUG
	printf ("Error: table has type %d and data type %d incompatable for tag %s\n", 
		datatype, entry->dataType_, entry->tag_);
#endif
	return 0;
      }
    }
  }
  return 1;
}


int
rsvcTableDef::dataInsertable (rsvcData& data)
{
  if (!defined_)
    return RSVC_ERROR;

  rsvcDataIterator ite0 (&data);
  int datatype;

  // make sure tags in the data appears in the table
  rsvcDataEntry *entry = 0;
  for (ite0.init (); !ite0; ++ite0) {
    entry = ite0 ();
    datatype = table_.getType (entry->tag_);
    if (!rsvcTableDef::typeConvertable (datatype, entry->dataType_)) {
#ifdef _RSVC_DEBUG
      printf ("Error: tabke has type %d and data has type %d for tag %s\n",
	      datatype, entry->dataType_, entry->tag_);
#endif
      return 0;
    }
  }

  // make sure tags in the table appears in the data
  rsvcDataIterator ite1 (&table_);
  for (ite1.init (); !ite1; ++ite1) {
    entry = ite1 ();
    if (strcmp (entry->tag_, rsvcServerConfig::table ()) != 0 &&
	strcmp (entry->tag_, rsvcServerConfig::key ()) != 0 &&
	strcmp (entry->tag_, rsvcServerConfig::keyType ()) != 0 &&
	strcmp (entry->tag_, rsvcServerConfig::keyExp ()) != 0) {
      datatype = data.getType (entry->tag_);
      if (!rsvcTableDef::typeConvertable (datatype, entry->dataType_)) {
#ifdef _RSVC_DEBUG
	printf ("Error: data has type %d and table has type %d for tag %s\n",
		datatype, entry->dataType_, entry->tag_);
#endif
	return 0;
      }
    }
  }
  return 1;
}

char* 
rsvcTableDef::keyName (void) const
{
  return keyname_;
}

char*
rsvcTableDef::keyExp (void) const
{
  return keyexp_;
}

int
rsvcTableDef::keyType (void) const
{
  return keytype_;
}


char*
rsvcTableDef::tableDefFileName (char* tablename)
{
  size_t len;

  len = strlen (rsvcServerConfig::dbaseHome ()) 
    + strlen (tablename) + strlen (rsvcServerConfig::tableNameExt ()) + 1;

  char *fname = new char[len];
  if (!fname) {
    fprintf (stderr, "Cannot allocated memory for table def file name\n");
    exit (1);
  }
  strcpy (fname, rsvcServerConfig::dbaseHome ());
  strcat (fname, tablename);
  strcat (fname, rsvcServerConfig::tableNameExt ());
  return fname;
}

int
rsvcTableDef::parseKeyExp (char* exp)
{
  char attr0[RSVC_TAG_MAX_LEN];
  char attr1[RSVC_TAG_MAX_LEN];
  char* a0;
  char* a1;
  char* e = exp;
  char  op = 0;

  a0 = attr0;
  a1 = attr1;
  *a0 = 0;
  *a1 = 0;

  // parse the expression
  while (*e != '\0') {
    // skip white spaces
    while (*e == ' ' || *e == '\t')
      e++;

    // get first attribute
    while (*e != ' ' && *e != '\t' && *e != '+' && *e != '\0') {
      *a0 = *e;
      a0++; e++;
    }
    *a0 = '\0';

    // skip white spaces
    while (*e == ' ' || *e == '\t')
      e++;

    // get operation
    if (*e != '+' && *e != '\0') {
      fprintf (stderr, "Fatal error: syntax error for key expression %s\n",
	       exp);
      return RSVC_ERROR;
    }
    if (*e == '+') {
      op = *e;
      e++;
    }

    // skip white spaces
    while (*e == ' ' || *e == '\t')
      e++;

    // get second attribute
    while (*e != ' ' && *e != '\t' && *e != '\0') {
      *a1 = *e;
      a1++; e++;
    }
    *a1 = '\0';
  }

  a0 = attr0;
  a1 = attr1;
  if (*a0 == 0) {
    fprintf (stderr, "Fatal error: parsing key expression %s error\n",
	     exp);
    return RSVC_ERROR;
  }

  if (*a1 == 0  && op != 0) {
    fprintf (stderr, "Fatal error: parsing key expression %s error\n",
	     exp);
    return RSVC_ERROR;
  }
  else if (*a1 != 0 && op == 0) {
    fprintf (stderr, "Fatal error: parsing key expression %s error\n",
	     exp);
    return RSVC_ERROR;
  }
  else {
    // find whether attributes are indeed inside the table definition
    void *data;
    if (table_.find (attr0, data) != RSVC_SUCCESS) {
      fprintf (stderr, "Fatal error: attr0 %s is invalid\n", attr0);
      return RSVC_ERROR;
    }
  
    if (*a1 != 0) {
      if (table_.find (attr1, data) != RSVC_SUCCESS) {
	fprintf (stderr, "Fatal error: attr1 %s is invalid\n", attr1);
	return RSVC_ERROR;
      }
    }
  }


  if (*a1 == 0 && op == 0) {
    attr0_ = new char[strlen (a0) + 1];
    strcpy (attr0_, a0);
    attr1_ = 0;
    keyfunc_ = sameAttr;
    printf ("Same attribute function set\n");
  }
  else {
    attr0_ = new char[strlen (a0) + 1];
    strcpy (attr0_, a0);

    attr1_ = new char[strlen (a1) + 1];
    strcpy (attr1_, a1);

    switch (op) {
    case '+':
      keyfunc_ = attrPlus;
#ifdef _RSVC_DEBUG
      printf ("Add key functon set\n");
#endif
      break;
    }
  }
  return RSVC_SUCCESS;
}

int
rsvcTableDef::sameAttr (rsvcData& data, rsvcTableDef* t)
{
  void* tmp;

  // check whether this data has key tag in or not
  if (data.find (t->keyname_, tmp) == RSVC_SUCCESS)
    return RSVC_SUCCESS;

  // duplicate dataentry pointed by tag attr0_ with new tag keyname
  return data.dupWithTag (t->attr0_, t->keyname_);
}

int
rsvcTableDef::attrPlus (rsvcData& data, rsvcTableDef* t)
{
  void* tmp;

  // check whether this data has key tag in or not
  if (data.find (t->keyname_, tmp) == RSVC_SUCCESS)
    return RSVC_SUCCESS;
      
  rsvcDataTypes type0 = data.getType (t->attr0_);
  rsvcDataTypes type1 = data.getType (t->attr1_);

  if (type0 == RSVC_INVALID) {
    fprintf (stderr, "Fatal error: attr %s has invalid data type\n", t->attr0_);
    return RSVC_ERROR;
  }
  if (type1 == RSVC_INVALID) {
    fprintf (stderr, "Fatal error: attr %s has invalid data type\n", t->attr1_);
    return RSVC_ERROR;
  }

  if (type0 == RSVC_STRING && type1 != RSVC_STRING) {
    fprintf (stderr, "Fatal: Cannot calculate key from incompatable data types\n");
    return RSVC_ERROR;
  }
  else if (type0 != RSVC_STRING && type1 == RSVC_STRING) {
    fprintf (stderr, "Fatal: Cannot calculate key from incompatable data types\n");
    return RSVC_ERROR;
  }
  else if (type0 == RSVC_STRING && type1 == RSVC_STRING) {
    char sval0[512];
    char sval1[512];
    
    if (data.get (t->attr0_, sval0, sizeof (sval0)) != RSVC_SUCCESS) {
      fprintf (stderr, "Fatal: cannot find data with attr %s\n", t->attr0_);
      return RSVC_ERROR;
    }

    if (data.get (t->attr1_, sval1, sizeof (sval1)) != RSVC_SUCCESS) {
      fprintf (stderr, "Fatal: cannot find data with attr %s\n", t->attr1_);
      return RSVC_ERROR;
    }      
    
    char newsval[1024];
    sprintf (newsval, "%s+%s", sval0, sval1);
    data.insert (t->keyname_, newsval);
  }
  else {
    double dval0;
    double dval1;

    data.get (t->attr0_, &dval0);
    data.get (t->attr1_, &dval1);
    
    double newdval = dval0 + dval1;

    data.insert (t->keyname_, newdval);
  }

  return RSVC_SUCCESS;
}

int
rsvcTableDef::keyValue (rsvcData& data)
{
  if (keyfunc_) 
    return (*keyfunc_) (data, this);
  return RSVC_ERROR;
}

int
rsvcTableDef::hasKey  (rsvcData& data)
{
  if (defined_) {
    void* tmp;
    if (data.find (keyname_, tmp) == RSVC_SUCCESS)
      return 1;
  }
  return 0;
}

