/**********************************************************************
 *
 * z3950_to_gsdl.cpp --
 * Copyright (C) 2000  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.
 *
 *********************************************************************/

#include "z3950parser.h"
#include "z3950server.h"
#include "z3950_to_gsdl.h"

// converts a char* to uppercase, I wouldn't be
// writing this if I could find an existing
// function to do it
void toUpperString(char *&text)
{
  for (int i = 0; text[i] != '\0'; i++) {
    text[i] = toupper(text[i]);
  }
}


text_t gsdlCollection::getName() 
{
  return name;
}

bool gsdlCollection::z3950Capeable() const
{
  return z3950ready;
}


bool gsdlCollection::bib1_to_dc_mapping(text_t& filename)
{
    ifstream inFile;
    int input_Bib1;
    text_t input_DCES;
    text_t input_Short;

    char* filename_cstr = filename.getcstr();
    inFile.open(filename_cstr, ios::in);
    delete [] filename_cstr;

    if (!inFile) {
      return false;
    }

    const int BUFFERSIZE = 1024;
    char buffer[BUFFERSIZE];

    // read in pairs of values (mappings from Bib-1 to DCES)
    while (!inFile.eof()) {
      char input_DCES_tmp[BUFFERSIZE];

      inFile.getline(buffer, BUFFERSIZE, '\n');
      if (buffer[0] == '#') {
	// ignore comments (# ...)
	continue;
      }

      sscanf(buffer,"%d %s",&input_Bib1, input_DCES_tmp);

      input_DCES = input_DCES_tmp;

      Bib1_to_DCES[input_Bib1] = input_DCES;

      if (z3950_verbosity_>2) {
	cerr << "(" << input_Bib1 << "/" << input_DCES << ") ";
      }
    }

    if (z3950_verbosity_>2) {
      cerr << endl;
    }
    inFile.close();

    return true;
}


bool gsdlCollection::dc_to_short_mapping(text_t& filename, 
					 text_t& collectionName) 
{
    ifstream inFileBuild;
    text_t input_DCES;
    text_t input_Short;
  
    char* filename_cstr = filename.getcstr();
    inFileBuild.open(filename_cstr, ios::in);
    delete [] filename_cstr;

    if (!inFileBuild) {
      if (z3950_verbosity_>2) {
	cerr << "Warning: Unable to open: " << filename << endl;
	cerr << "         Collection " << collectionName << " might not be built" << endl;
      }
      return false;
    }

    const int BUFFERSIZE = 1024;
    char buffer[BUFFERSIZE];

    // find the indexfieldmap line
    do {
      inFileBuild >> buffer;
      if (inFileBuild.eof()) {
	if (z3950_verbosity_>1) {
	  cerr << "Warning: Did not find indexfieldmap in build.cfg for " 
	       << collectionName << endl;
	}
	return false;
      }
    } while (strcmp(buffer, "indexfieldmap") != 0);

    // at this stage, buffer contains "indexfieldmap",
    // if the file didn't contain it, would have returned above
    inFileBuild >> buffer;
    while (strstr(buffer, "->") != NULL) {
      // buffer contains a DCES->Short mapping
      input_DCES.clear();
      input_Short.clear();

      // copy in DCES
      int i;
      for (i = 0; buffer[i] != '-'; i++) {
	input_DCES.appendcarr(&(buffer[i]), 1);
      }

      // skip over "->"
      i+=2;

      // copy in Short
      for (; isalpha(buffer[i]); i++) {
	input_Short.appendcarr(&(buffer[i]), 1);
      }

      // add to map
      DCES_to_Short[input_DCES] = input_Short;

      if (z3950_verbosity_>1) {
	cerr << "<" << input_DCES << "/" << input_Short << "> ";
      }

      inFileBuild >> buffer;
    }

    inFileBuild.close();

    if (z3950_verbosity_>1) {
      cerr << endl;
    }

    return true;
}    




  // converts a Bib1 field number to a short index name,
  // returns true if a mapping existed, false otherwise
bool gsdlCollection::getFieldArg(int Bib1, text_t &Short) 
{
    if (Bib1_to_DCES.count(Bib1) < 1) return false;
    if (DCES_to_Short.count(Bib1_to_DCES[Bib1]) < 1) return false;
    Short = DCES_to_Short[Bib1_to_DCES[Bib1]];
    return true;
}
  
gsdlCollection::~gsdlCollection(void) 
{
    //cerr << "gsdlCollection destructor called" << endl;
}

gsdlCollection::gsdlCollection(void) 
{
    //cerr << "gsdlCollection void constructor called" << endl;
}
 
gsdlCollection::gsdlCollection(text_t collectionName, text_t gsdlhome) 
{

    //char *filename_cstr;
    //ifstream inFile;
    //int inFileSize = 0;
    z3950ready = true;

    // int input_Bib1;
    //text_t input_DCES;
    //text_t input_Short;

    name = collectionName;

    // read in the cfg file for Bib1_to_DCES
    text_t col_filename = gsdlhome;
    col_filename = filename_cat(gsdlhome, "collect", collectionName, "etc", "bib1-mapping.txt"); 

    if (!bib1_to_dc_mapping(col_filename)) {
      text_t site_filename = filename_cat(gsdlhome, "etc", "packages", "z3950", "bib1-mapping.txt");

      if (!bib1_to_dc_mapping(site_filename)) {
	cerr << "Unable to find bib1 attribute mapping for " 
	     << collectionName << endl;
	cerr << "Looked for:\n  " << col_filename << "\n  " 
	     << site_filename << endl;
	z3950ready = false;
	return;
      }
    }

    // read in the cfg file for mapping Dublin Core to Short (internal name)
    text_t build_filename = filename_cat(gsdlhome, "collect", collectionName, "index", "build.cfg");

    if (!dc_to_short_mapping(build_filename,collectionName)) {
      // explicitly check for error (in case in future more work is done after
      // this
      z3950ready = false;
      return;
    }

}


void z3950Server::openLogfile(text_t extension, ofstream &out)
{
    char *cfilename;
    text_t filename;

    filename = filename_cat(gsdlhome, extension);
    cfilename = filename.getcstr();

    out.open(cfilename);
    delete cfilename;
}

z3950Server::z3950Server(nullproto *protocol, text_t home)
{ 

    text_t dirname;
    char * cdirname;
    if (home.empty()) {
      cerr  << "Z3950Server Error: gsdlhome not set. \nPlease make sure"
	    << " you have a valid gsdlsite.cfg file in the directory \n"
	    << "you are running z3950server\n";
      cerr << "Exiting..."<<endl;
      exit(1);
    } 
    cerr << "Z3950Server: gsdlhome = " << home << endl;

    this->gsdlhome = home;
    //cerr << gsdlhome.getcstr() << endl;
    dirname = filename_cat(gsdlhome, "collect");
    cdirname = dirname.getcstr();
    if (!read_dir (cdirname, collections)) { 
      cerr << "Z3950Server Error: could not read directory "<<cdirname<<endl;
      cerr << "Exiting..."<<endl;
      exit (1);
    }
    delete cdirname;

    cerr << "Constructing set list server" << endl;
    this->protocol = protocol;

    // initialise Yaz stuff for z39.50 server 
}
  
bool z3950Server::initialise() 
{
      ofstream      logout;
      int           reply;
      comerror_t    error = noError;

      // openLogfile does a filename_cat with this path, changing 
      // the / appropriately.
      this->openLogfile("/etc/packages/z3950/z3950out.txt", logout);
      reply = protocol->init(error, logout);
      logout.close();
      return (reply != 0 ? 1 : 0);
}

void z3950Server::configure(const text_t &key, const text_tarray &cfgline, 
			    comerror_t &err)
{
      err = noError;

      // ****
      cerr << "Recieved " << key << " = ";
      for (int unsigned i=0; i<cfgline.size(); i++)
	{
	  cout << cfgline[i] << " ";
	}
      cout << endl;

      // DB // ****
      if (key=="gsdlhome")
	{  
	  text_tarray cfgline;
	  cfgline.push_back (gsdlhome);
	  cerr << "Changing gsdlhome to " << gsdlhome << endl;
	  protocol->configure(key, cfgline, err);
	}
      else if (key=="httpdomain")
	{
	  // Only let gsdlhome through !!!! // ****
	  cerr << "Supressing httpdomain" << endl;
	}
      else if (key=="httpprefix")
	{
	  cerr << "Supressing httpprefix" << endl;
	}
      else
	{ 
	  protocol->configure(key, cfgline,err);
	}
}

int z3950Server::run_z3950( int argc, char **argv ) 
{

    const int statserv_var =  statserv_main(argc, argv, bend_init, bend_close);
    cerr << "statserv_main returns: " << statserv_var << endl;
    return statserv_var;
}
