/*------------------------------------------------------------------------
   (C) Copyright Marxmeier Software AG, 2021

   btest3k.c - Eloquence database binary object test (3k)
   $Id$

   Redistribution and use in source and binary forms, with or without 
   modification, are permitted provided that the following conditions 
   are met:

    * Redistributions of source code must retain the above copyright 
      notice, this list of conditions and the following disclaimer.

    * Neither the name of the Eloquence product nor Marxmeier Software AG 
      or the names of its contributors may be used to endorse or 
      promote products derived from this software without specific 
      prior written permission.

   This program is distributed in the hope that it is useful, but 
   WITHOUT ANY WARRANTY.  No author or distributor accepts 
   responsibility to anyone for the consequences of using it or for
   whether it serves any particular purpose or works at all, unless
   he says so in writing.

   We kindly ask to submit any changes to support@marxmeier.com so 
   they can be folded back into the original source.

   Edit History:
   15.06.2021, initial release

------------------------------------------------------------------------*/

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#if !defined(_WIN32)
#include <unistd.h>
#endif

#include "image3k.h"
#include "eqdbobj.h"

const char *argv0;
const char *db_name;

#define DB_NAME_SIZE 256

typedef struct {
   short int DbId;
   char DbName[DB_NAME_SIZE];
} IMAGEDBNAME;

static void dbstat(const char *str, int16_t *status);
static int read_file_obj(const char *fname);
static int write_file_obj(const char *fname);

int main(int argc, char **argv)
{
   const char *set, *fname, *op;
   unsigned int id;
   short int status[10];
   short int mode, cc, arg_i, v_mode, z_mode;
   IMAGEDBNAME db;

   if(argc > 1 && !strcmp(argv[1], "-version")) {
      fprintf(stderr, "btest3k 2021-06-11 (build %s)\n", __DATE__);
      fprintf(stderr, "Library: %s / %s\n", dbobj__version_tag, dbobj__patchlevel);
      return 2;
   }

   if(argc < 5) {
usage:
      fprintf(stderr, "usage: %s [opt] db set id {op} [file]\n", argv[0]);
      fprintf(stderr, " opt: -v[vv] -z{0..9}\n");
      fprintf(stderr, " op: add|del|upd|get|store\n");
      return 2;
   }
   
   argv0 = argv[0];
   
   v_mode = 0;
   z_mode = -1;
   
   arg_i = 1;
   while(arg_i < argc) {
      if(!strcmp(argv[arg_i], "-v")) {
         v_mode++;
         arg_i++;
      }
      else if(!strcmp(argv[arg_i], "-vv")) {
         v_mode += 2;
         arg_i++;
      }
      else if(!strcmp(argv[arg_i], "-vvv")) {
         v_mode += 3;
         arg_i++;
      }
      else if(!strncmp(argv[arg_i], "-z", 2)) {
         z_mode = atoi(argv[arg_i]+2);
         arg_i++;
      }
      else
         break;
   }

   if(arg_i+3 >= argc)
      goto usage;
   
   db_name = argv[arg_i++];
   set = argv[arg_i++];

   id = atoi(argv[arg_i++]);
   if(!id)
      goto usage;

   op = argv[arg_i++];
   if(!strcmp(op, "del")) {
      if(arg_i < argc)
         goto usage;
      fname = NULL;
   }
   else {
      if(arg_i == argc)
         goto usage;
      fname = argv[arg_i++];
   }

   /* open database */

   memset(&db.DbId, ' ', 2);
   memcpy(db.DbName, db_name, strlen(db_name));
   db.DbName[strlen(db_name)] = ';';

   mode = !strcmp(op, "get") ? 5 : 1;
   DBOPEN(&db, ";", &mode, status);
   dbstat("open", status);

   /* eqdbobj functions */

   mode = 1;            /* verbose */
   dbobjctrl(&mode, &cc, &v_mode, NULL);
   if(z_mode != -1) {
      mode = 2;        /* compression level */
      dbobjctrl(&mode, &cc, &z_mode, NULL);
   }

   if(!strcmp(op, "get"))
   {
      unsigned int len;

      dbobjfetch(&db, set, &cc, &id, &len);
      if(cc) {
         fprintf(stderr, "dbobjfetch: FAILED %d\n", cc);
         goto failed;
      }

      if(write_file_obj(fname)) {
         fprintf(stderr, "write_file_obj: FAILED\n");
         goto failed;
      }
   }

   else if(!strcmp(op, "del"))
   {
      dbobjdelete(&db, set, &cc, &id);
      if(cc) {
         fprintf(stderr, "dbobjdelete: FAILED %d\n", cc);
         goto failed;
      }
   }

   else if(!strcmp(op, "add") || !strcmp(op, "upd") || !strcmp(op, "store"))
   {
      if(read_file_obj(fname)) {
         fprintf(stderr, "read_file_obj: FAILED\n");
         goto failed;
      }

      if(!strcmp(op, "add"))
         mode = 1;
      else if(!strcmp(op, "upd"))
         mode = 2;
      else
         mode = 3;
      dbobjstore(&db, set, &mode, &cc, &id);
      if(cc) {
         fprintf(stderr, "dbobjstore: FAILED %d\n", cc);
         goto failed;
      }
   }

   else
      goto usage;

   /* done */

   mode = 1;
   DBCLOSE(&db, ";", &mode, status);
   dbstat("close", status);
   
   return 0;

failed:
   return 1;
}


/*

   utility functions

*/

static void dbstat(const char *str, int16_t *status)
{
   char tmp[80];
   short int textlen;

   if(status[0] == 0)
      return;

   fprintf(stderr,
         "Image status #%d while %s", status[0], str);
   fprintf(stderr, "\n");

   /* DBEXPLAIN(status); */
   DBERROR(status, tmp, &textlen);
   fprintf(stderr, "%.*s\n", textlen, tmp);

   fprintf(stderr, "%s: Unexpected database status on %s\n", argv0, db_name);
   exit(1);
}


/*

   eqdbobj api file i/o

*/

static int read_file_obj(const char *fname)
{
   FILE *fp;
   char data[8192];
   unsigned int nbytes;
   int16_t cc, mode;

   fp = fopen(fname, "rb");
   if(!fp) {
      perror(fname);
      goto failed;
   }

   mode = 1;
   while((nbytes = fread(data, 1, sizeof(data), fp)))
   {
      dbobjputdata(&mode, &cc, data, &nbytes);
      if(cc) {
         fprintf(stderr, "dbobjputdata: FAILED %d\n", cc);
         goto failed;
      }
      mode = 2;
   }

   fclose(fp);
   return 0;

failed:
   mode = 0;
   dbobjputdata(&mode, &cc, NULL, 0);   /* cleanup */
   if(fp)
      fclose(fp);
   return -1;
}

static int write_file_obj(const char *fname)
{
   FILE *fp;
   char buf[8192];
   unsigned int len, buf_sz;
   int16_t cc, mode;

   fp = fopen(fname, "w+b");
   if(!fp) {
      perror(fname);
      goto failed;
   }

   mode = 1;
   buf_sz = sizeof(buf);
   for(;;)
   {
      dbobjgetdata(&mode, &cc, buf, &buf_sz, &len);
      if(cc || !len)
         break;

      if(fwrite(buf, len, 1, fp) != 1) {
         perror("fwrite");
         goto failed;
      }
   }

   if(cc) {
      fprintf(stderr, "dbobjgetdata: FAILED %d\n", cc);
      goto failed;
   }

   if(!fclose(fp))
      return 0;
      
   perror("fclose");
   fp = NULL;

failed:
   mode = 0;
   dbobjgetdata(&mode, &cc, NULL, 0, NULL);   /* cleanup */
   if(fp)
      fclose(fp);
   unlink(fname);
   return -1;
}
