/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 
/* vim: set ts=2 et sw=2 tw=80: */
/*
** Copyright (c) 2013 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
**
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
*/

/* Force assert() to always work... */
#if defined(NDEBUG)
#undef NDEBUG
#define DEBUG 1
#endif

#include <assert.h>
#include <stdlib.h> /* atexit() */
#include <string.h> /* strlen() */
#include "fossil/fossil.h"
#include "fsl_internal.h" /* only for testing */

#define MARKER(pfexp)                                               \
  do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__);   \
    printf pfexp;                                                   \
  } while(0)

#define VERBOSE(pfexp)                          \
  if(App.verbose) do { \
    printf("VERBOSE: %s:%d:%s():\t",__FILE__,__LINE__,__func__);   \
    printf pfexp;                                                   \
  } while(0)

static struct _App {
  fsl_cx * f;
  char const * name;
  char const * dbFile;
  char verbose;
  char traceSql;
  char const * checkoutDir;
} App = {
NULL/*f*/,
NULL/*name*/,
NULL/*dbFile*/,
0/*verbose*/,
0/*traceSql*/,
NULL/*checkoutDir*/
};


static int test_buffer_0(){
  fsl_buffer buf = fsl_buffer_empty;
  int rc;
  fsl_cx * f = App.f;
  fsl_size_t sz, szOrig;
  char const * infile = 0
    ? __FILE__
    : "fsl_buffer.c";
  assert(f);
  rc = fsl_buffer_fill_from_filename(&buf, infile);
  assert(!rc);
  sz = szOrig = buf.used;
  rc = fsl_buffer_compress( &buf, &buf );
  assert(!rc);
  assert(buf.used < sz);
  MARKER(("Compressed [%s]. Size: %"FSL_SIZE_T_PFMT
          " => %"FSL_SIZE_T_PFMT"\n", infile, szOrig, buf.used));
  sz = buf.used;
  rc = fsl_buffer_uncompress(&buf, &buf);
  assert(!rc);
  MARKER(("Uncompressed [%s]. Size: %"FSL_SIZE_T_PFMT
          " => %"FSL_SIZE_T_PFMT"\n", infile, sz, buf.used));
  assert(szOrig == buf.used);

  if(0){
    fsl_buffer sql = fsl_buffer_empty;
    fsl_buffer_reset(&buf);
    rc = fsl_buffer_appendf(&buf, "this isn't a quoted value.");

    fsl_buffer_appendf(&sql,"/*%%b*/ SELECT x FROM a WHERE a=%b\n",
                      &buf);
    fsl_output(f, sql.mem, sql.used );
    fsl_buffer_reset(&sql);
    fsl_buffer_appendf(&sql,"/*%%B*/ SELECT x FROM a WHERE a=%B\n",
                       &buf);
    fsl_output(f, sql.mem, sql.used );
    rc = fsl_buffer_reserve(&sql, 0);
    assert(!rc);
  }
  
  rc = fsl_buffer_reserve(&buf, 0);
  assert(!rc);


  
  return rc;  
}

static int test0(){
  fsl_cx * f = NULL;
  int rc;
#if 1
  fsl_init_param init = fsl_init_param_empty;
  init.output = fsl_outputer_FILE;
  init.output.state.state = stdout;
  init.config.traceSql = App.traceSql;
  rc = fsl_cx_init( &f, &init );
#elif 0
  fsl_init_param init = fsl_init_param_default;
  init.output.state.state = stdout;
  init.config.traceSql = App.traceSql;
  rc = fsl_cx_init( &f, &init );
#else
  rc = fsl_cx_init( &f, NULL );
#endif
  assert(!rc);
  App.f = f;
  VERBOSE(("Initialized fsl @%p\n", (void const *)f));

  if(App.checkoutDir){
    fsl_buffer dir = fsl_buffer_empty;
    char const * dirName;
    rc = fsl_file_canonical_name( ".", &dir, 0);
    assert(!rc);
    dirName = (char const *)fsl_buffer_cstr(&dir);
    VERBOSE(("Trying to open checkout from [%s]...\n",
             dirName));
    rc = fsl_cx_checkout_open(f, dirName, fsl_buffer_size(&dir));
    VERBOSE(("Checkout-open RC=%d (%s)\n", rc, fsl_rc_cstr(rc)));
    if(rc){
      char const * msg;
      int code = fsl_cx_err_get(f, &msg, NULL);
      MARKER(("Checkout open failed. rc=%d: %s\n", code, msg));
    }
    if(!rc){
      VERBOSE(("Checkout DB name: %s\n",
               fsl_buffer_cstr(&f->fileCkout)));
      if(f->fileRepo.used){
        VERBOSE(("Opened repo db: %s\n", fsl_buffer_cstr(&f->fileRepo)));
      }
    }
    else{
      if(!fsl_cx_err_get(f,NULL,NULL)){
        rc = fsl_cx_err_set(f, rc, "Opening of checkout under "
                            "[%s] failed with code %d (%s).",
                            dirName, rc, fsl_rc_cstr(rc));
      }
    }
    fsl_buffer_reserve(&dir, 0);
    if(rc) return rc;
  }
  
  if(App.dbFile){
    VERBOSE(("Trying to open repo db file [%s]...\n", App.dbFile));
    rc = fsl_cx_repo_open_db( f, App.dbFile, 0 );
    if(rc) return rc;
    VERBOSE(("Opened db file [%s]\n", App.dbFile));
  }

  return rc;
}

static void my_atexit(){
  if(App.f){
    int rc = 0;
    fsl_cx * f = App.f;
    App.f = NULL;
    VERBOSE(("Finalizing fsl @%p\n", (void const *)f));
    
    if(0 &&
       fsl_cx_db_checkout(f)){
      /* For testing/demo only: this is implicit
         when we call fsl_cx_finalize().
      */
      rc = fsl_cx_checkout_close(f, 1);
      assert(0==rc);
      VERBOSE(("Closed checkout/repo db(s).\n"));
    }

    rc = fsl_cx_finalize( f );
    assert(!rc);
  }
}

static int process_argv( int argc, char * const * argv ){
  int i;
  for( i = 1; i < argc; ++i ){
    char const * arg = argv[i];
#define ARG(F) if(0==strcmp(F,arg))
    ARG("-r") {
      App.dbFile = argv[++i];
      continue;
    }
    ARG("-v") {
      App.verbose = 1;
      continue;
    }
    ARG("-c"){
      App.checkoutDir = ".";
      continue;
    }
    ARG("-S") {
      App.traceSql = 1;
    }
    else if('-' == *arg){
      MARKER(("Unknown flag: %s\n", arg));
      return FSL_RC_MISUSE;
    }
#undef ARG
  }
  return 0;
}

int main(int argc, char * const * argv ){
  int rc;
  rc = process_argv( argc, argv );
  if(rc) goto end;
  atexit( my_atexit );
  rc = test0();
  if(1 && !rc) rc = test_buffer_0();

  end:
  MARKER(("Done! rc=%d (%s)\n", rc, fsl_rc_cstr(rc)));
  if(rc && App.f){
    char const * msg = NULL;
    int errRc = fsl_cx_err_get( App.f, &msg, NULL );
    if(msg){
      MARKER(("Fossil says: error code #%d (%s): %s\n",
              errRc, fsl_rc_cstr(errRc), msg));
    }
  }
  return rc;
}

#undef MARKER
#undef VERBOSE
