/**********************************************************************
 *
 * db2txt.cpp -- 
 * Copyright (C) 1999  The New Zealand Digital Library Project
 *
 * A component of the Greenstone digital library software
 * from the New Zealand Digital Library Project at the
 * University of Waikato, New Zealand.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *********************************************************************/

#ifdef _MSC_VER
#include "autoconf.h"
#include "systems.h"
#include "gdbmconst.h"
#include "gdbm.h"

#else
#include <gdbm.h>
#include <errno.h>
#endif

#include "gsdlconf.h"
#include "text_t.h"
#include <stdio.h>
#include <stdlib.h>
#include <cstring>

// for sorting
#if defined(GSDL_USE_OBJECTSPACE)
#  include <ospace\std\vector>
#  include <ospace\std\algorithm>
#elif defined(GSDL_USE_STL_H)
#  include <vector.h>
#  if defined(GSDL_USE_ALGO_H)
#    include <algo.h>
#  else
#    include <algorithm.h>
#  endif
#else
#  include <vector>
#  include <algorithm>
#endif


#if defined(GSDL_USE_OBJECTSPACE)
#include <ospace\std\iostream>
#elif defined(GSDL_USE_IOS_H)
#include <iostream.h>
#else
#include <iostream>
#endif

// use the standard namespace
#if !defined (GSDL_NAMESPACE_BROKEN)
#if defined(GSDL_USE_OBJECTSPACE)
using namespace ospace::std;
#else
using namespace std;
#endif
#endif

#ifdef _GSDL_FLOCK_ENABLED
// John Thompson's lock() and unlock() needed in parallel building
// parked in separate file to help readability of main code
#include "filelock.cpp"
#endif

void print_usage (char *program_name) {
  cerr << "usage: " << program_name << " [options] database-name" << endl << endl;
  cerr << "options:" << endl;
  cerr << " -sort        sort the keys to the database before output" << endl << endl;
}

void print_entry(datum& key, datum& value)
{
  int i;
  cout << "[";
  for (i = 0; i < key.dsize; ++i) {
    cout << key.dptr[i];
  }
  cout << "]" << endl;


  for (i = 0; i < value.dsize; ++i) {
    cout << value.dptr[i];
  }
  cout << endl << "----------------------------------------------------------------------" << endl; 
}


int main (int argc, char *argv[]) {
  int block_size = 0, i;
  GDBM_FILE dbf;
  datum key, value, nextkey;
      
  // sanity check
  if (argc != 2 && argc != 3) {
    print_usage (argv[0]);
    exit (0);
  }
  
  char *dbname;
  int sort = 0;

  if (argc == 3) {
    if (strcmp (argv[1], "-sort") == 0) {
      sort = 1;
      dbname = argv[2];
    } else {
      cerr << argv[1] << " is not a valid option." << endl << endl;
      print_usage (argv[0]);
      exit (0);
    }
  } else dbname = argv[1];

  vector<text_t> key_list;

  // open the database
#ifdef _MSC_VER
  dbf = gdbm_open (dbname, block_size, GDBM_READER, 00664, NULL, 0);
#else
  dbf = gdbm_open  (dbname, block_size, GDBM_READER, 00664, NULL);
#endif
  if (dbf == NULL) {
    cerr << argv[0] << ": Couldn't open '" << dbname << "'" << endl;
    cerr << "  " << gdbm_strerror(gdbm_errno) << endl;
    cerr << "  gdbm errno = " << gdbm_errno << endl;
#ifndef _MSC_VER
    cerr << "  OS errno = " << errno << endl;
    perror("gdbm_open failed: ");
#endif

    exit (-1);
  }

  key = gdbm_firstkey (dbf);
  while (key.dptr != NULL) {

    value = gdbm_fetch (dbf, key);
    
    if(sort) { // store in vector, will sort and then print later
      text_t key_str;
      for (i = 0; i < key.dsize; ++i) {
	key_str.push_back ((unsigned char)key.dptr[i]);
      }
      key_list.push_back(key_str); // STL makes a copy on push_back

    } else {
      print_entry(key, value);
    }

    free(value.dptr);

    /* get next key */
    nextkey = gdbm_nextkey (dbf, key);

    /* free old key's dptr, otherwise causes memory leak */
    free(key.dptr); 
    
    /* can now safely copy content of nextkey into key */
    key = nextkey;
  }

  if(sort) {    
    std::sort(key_list.begin(), key_list.end());
    
    vector<text_t>::const_iterator this_key = key_list.begin();
    vector<text_t>::const_iterator end_key = key_list.end();
    while (this_key != end_key) {
      const text_t& key_str = *this_key;
      char* key_cstr = key_str.getcstr();

      key.dsize = strlen(key_cstr);
      key.dptr = key_cstr;

      value = gdbm_fetch (dbf, key);

      print_entry(key,value); // print in sorted order now

      free(value.dptr);      
    
      this_key++;
    }
  }
  
  
  gdbm_close (dbf);
  return 0;
}
