/* --------------------------------------------------- */
/* Source-file STORE 1.0, (C) Wabnig Harald, 25.5.2002 */
/* --------------------------------------------------- */

/* ------------------------------------------------- */
/* Standard-functions to handle large binary objects */
/* ------------------------------------------------- */

/* memory allocation strategy when reading blobj */
#define BLOBJ_MIN    10000
#define BLOBJ_MORE   50000

static int BLOBJ_Sort_ItemNumber (BLOBJ * pBlobj1, BLOBJ * pBlobj2)
{
  if (pBlobj1->fsItemNumber > pBlobj2->fsItemNumber) return 1;
  if (pBlobj1->fsItemNumber < pBlobj2->fsItemNumber) return -1;
  return 0;
}

int BLOBJ_Read(DBHANDLE * pDBHANDLE, TFSurrogat fszFK_Id, TFShort fsFK_Table, 
               char * data, int max, char ** pdata, int * pmax, int * psize)
{
  MBlobj_Data MDBlobj;
  MBLOBJ MBlobj;
  BLOBJ Blobj, * pBlobj;
  
  Blobj_Data * pDBlobj;
  int iRc = TRUE;
  COMPR Compr;
  int i, size = 0;

  MBlobj_Data_Init(&MDBlobj); 
  MBLOBJ_Init(&MBlobj); BLOBJ_Init(&Blobj);

  COMPR_Init (&Compr, SIZE_FBinary, 100000, 1000000, 100000, 1000000);

  COMPR_FmtBinFix (&Compr);

  COPY_FSurrogat(Blobj.fszFK_Id, fszFK_Id);
  COPY_FShort(Blobj.fsFK_Table, fsFK_Table);
  if (! DB_Begin(pDB)) {
    DB_Print_Error_String();
    fprintf(stderr, "DB_Begin error!");
    return FALSE;
  }
  if (! BLOBJ_Select_FK(&Blobj, pDBHANDLE)) {
    fprintf(stderr, "(BLOBJ_Read) Error in BLOBJ_Select_FK\n");
    return FALSE;
  } else {
    while (BLOBJ_Fetch_FK(&Blobj, pDBHANDLE)) {
      MBLOBJ_Add(&MBlobj, &Blobj);
    }
    if (Blobj.fcCompressed == '1') 
      COMPR_FmtCompVar (&Compr);
  } 
  if (! DB_Commit(pDB)) {
    DB_Print_Error_String();
    fprintf(stderr, "DB_Commit error!");
    exit(1);
  }
  MBLOBJ_SetCmpFunc(&MBlobj, BLOBJ_Sort_ItemNumber);
  MBLOBJ_Sort(&MBlobj);
  for (i = 0; i < MBlobj.iCount; i++) {
    pBlobj = MBLOBJ_GetPtr(&MBlobj, i);
    COMPR_AddBin (&Compr, pBlobj->fszComprData);
  }
  MBLOBJ_Free(&MBlobj);

  COMPR_Reset (&Compr);
  if (COMPR_GetId(&Compr) != Blobj_Data_TABLENR) {
    fprintf(stderr, "(BLOBJ_Read) Corrupted BLOBJ data-header\n");
    COMPR_Free(&Compr);
    return FALSE;
  }
  if (! COMPR_Read (&Compr, &MDBlobj)) {
    fprintf(stderr, "(BLOBJ_Read) Corrupted BLOBJ data-content\n");
    COMPR_Free(&Compr);
    return FALSE;
  }
  for (i = 0; i < MDBlobj.iCount; i++) {
    pDBlobj = MBlobj_Data_GetPtr(&MDBlobj, i);
    size = DATA_Add(size, BINARY_DATA(pDBlobj->fszBinData), BINARY_LEN(pDBlobj->fszBinData), 
                    data, max, pdata, pmax, BLOBJ_MIN, BLOBJ_MORE, NULL, NULL);
  }
  *psize = size;
  MBlobj_Data_Free(&MDBlobj);

  COMPR_Free(&Compr);

  return iRc;
}

int BLOBJ_Write(DBHANDLE * pDBHANDLE, TFSurrogat fszFK_Id, TFShort fsFK_Table, 
                int compressed, char * data, int size) 
{
  MBlobj_Data MDBlobj;
  Blobj_Data DBlobj;
  BLOBJ Blobj;
  
  COMPR Compr;
  short int sItemNumber;
  int iRc = TRUE; 
  int idx = 0, iCount;

  MBlobj_Data_Init(&MDBlobj);
  Blobj_Data_Init(&DBlobj);
  BLOBJ_Init(&Blobj);

  COPY_FSurrogat(Blobj.fszFK_Id, fszFK_Id);
  COPY_FShort(Blobj.fsFK_Table, fsFK_Table);
  if (! BLOBJ_Delete_FK(&Blobj, &iCount, pDB)) {
    DB_Print_Error_String();
    fprintf(stderr, "BLOBJ_Delete_FK error!");
    return FALSE;
  }

  COMPR_Init (&Compr, SIZE_FBinary, 100000, 1000000, 100000, 1000000);

  if (compressed) {
    COMPR_FmtCompVar (&Compr);
    Blobj.fcCompressed = '1';
  } else
    COMPR_FmtBinFix (&Compr);

  while (idx < size) {
    if (idx + SIZE_FBinary > size) {
      int rsize = size - idx;
      memcpy(BINARY_DATA(DBlobj.fszBinData), &data[idx], rsize);
      BINARY_LEN(DBlobj.fszBinData) = rsize;
      idx = size;
    } else {
      memcpy(BINARY_DATA(DBlobj.fszBinData), &data[idx], SIZE_FBinary);
      BINARY_LEN(DBlobj.fszBinData) = SIZE_FBinary;
      idx += SIZE_FBinary;
    }
    MBlobj_Data_Add(&MDBlobj, &DBlobj);
  }

  COMPR_Write (&Compr, &MDBlobj);
  MBlobj_Data_Free(&MDBlobj);

  COMPR_Reset (&Compr);

  sItemNumber = 1;
  COPY_FSurrogat(Blobj.fszFK_Id, fszFK_Id);
  COPY_FShort(Blobj.fsFK_Table, fsFK_Table);
  while (COMPR_GetBin (&Compr, &Blobj.fszComprData)) {
    Blobj.fsItemNumber = sItemNumber++;
    if (! BLOBJ_Insert (&Blobj, pDBHANDLE)) {
      fprintf(stderr, "(BLOBJ_Write) Error in BLOBJ_Insert\n");
      COMPR_Free(&Compr);
      if (! DB_RollBack(pDB)) {
        fprintf(stderr, "DB_RollBack error!");
        exit(1);
      }
      return FALSE;
    }
  } 

  COMPR_Free(&Compr);

  return iRc;
}

/* ------------------------------------------ */
/* Functions for reading and changing strings */
/* ------------------------------------------ */

#define UML_AE    142
#define UML_ae    132
#define UML_UE    154
#define UML_ue    129
#define UML_OE    153
#define UML_oe    148
#define UML_ss    225

static int char_valid(char * fmt, unsigned char ch)
{
  int i = 0;

  if (fmt[0] == '-') {
    if (ch == '-')
      return TRUE;
    i++;
  }
  while (fmt[i] != '\0') {
    if (ch == fmt[i])
      return TRUE;
    if (fmt[i] == 'ä' && ch == UML_ae) 
      return TRUE;
    if (fmt[i] == 'Ä' && ch == UML_AE) 
      return TRUE;
    if (fmt[i] == 'ö' && ch == UML_oe) 
      return TRUE;
    if (fmt[i] == 'Ö' && ch == UML_OE) 
      return TRUE;
    if (fmt[i] == 'ü' && ch == UML_ue) 
      return TRUE;
    if (fmt[i] == 'Ü' && ch == UML_UE) 
      return TRUE;
    if (fmt[i] == 'ß' && ch == UML_ss) 
      return TRUE;
    if (fmt[i + 1] == '-' && fmt[i + 2] != '\0') {
      if (ch > fmt[i] && ch <= fmt[i + 2])
        return TRUE;
      i++; i++;
    }
    i++;
  }
  return FALSE;
}

static int scan_string(char * fmt, char * strg)
{
  unsigned char ch;
  int i = 0;

  while ((ch = getchar()) != '\n') {
    if (! char_valid(fmt, ch)) {
      fprintf(stderr, "character '%c' is not valid!\n", ch);
      while (getchar() != '\n');
      return -1;
    }
    strg[i++] = ch;
  }
  strg[i] = '\0';
  return i; 
}

static int read_string(char * prompt, char * strg, int len, int min)
{
  do {
    if (prompt[0] != '\0')
      fprintf(stdout, "%s", prompt);
    else 
      fprintf(stdout, "# ");
    if (scan_string("-+a-zA-Z/ _0-9äöüÄÖÜß*@.(),;\\'%=<>", strg) <= 0) {
      if (min == 0) {
        strg[0] = '\0'; break;
      }
      continue;
    }
    if ((int)strlen(strg) > len) {
      fprintf(stdout, "string too long (max = %d)!\n", len);
      continue;
    }
    if ((int)strlen(strg) < min) {
      fprintf(stdout, "string too small (min = %d)!\n", min);
      continue;
    }
    if (strcmp(strg, "q") == 0) {
      return FALSE;
    }
    break;
  } while (1 == 1);
  return TRUE;
}

static int change_string(char * prompt, char * ostrg, char * strg, int len)
{
  do {
    if (prompt[0] != '\0')
      fprintf(stdout, "%s", prompt);
    else 
      fprintf(stdout, "# ");
    if (ostrg[0] != '\0')
      fprintf(stdout, "(%s) ", ostrg);
    if (! scan_string("-+a-zA-Z/ _0-9äöüÄÖÜß*@.(),;\\'%=<>", strg) < 0) {
      continue;
    }
    if ((int)strlen(strg) > len) {
      fprintf(stdout, "string too long (max = %d)!\n", len);
      continue;
    }
    if (strcmp(strg, "q") == 0) {
      return FALSE;
    }
    if (strg[0] == '\0') {
      strcpy(strg, ostrg);
    }
    break;
  } while (1 == 1);
  return TRUE;
}

