/**********************************************************************
 *
 * cgiargs.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.
 *
 *********************************************************************/

#include "cgiargs.h"
#include "gsdlunicode.h"



void cgiarg_t::clear () {
  value.clear();
  source = program_arg;
  fileupload.clear();
}

cgiarg_t &cgiarg_t::operator=(const cgiarg_t &x) {
  value=x.value;
  source=x.source;
  return *this;
}


bool operator==(const cgiarg_t &x, const cgiarg_t &y) {
  return ((x.value == y.value) && (x.source == y.source));
}

bool operator<(const cgiarg_t &x, const cgiarg_t &y) {
  return ((x.value < y.value) ||
	  ((x.value == y.value) && (x.source == y.source)));
}


void fileupload_t::clear() {
  name.clear();
  type.clear();
  size = 0;
  tmp_name.clear();
}

ostream &operator<<(ostream &outs, const fileupload_t &fu) {
  utf8outconvertclass text_t2utf8;
  outs << "*** fileupload\n";
  
  outs << text_t2utf8 << "name=" << fu.name << ", type="<< fu.type<<", size="<<fu.size<<", tmp_name="<<fu.tmp_name<<"\n";
  return outs;
}



// constructors
cgiargsclass::cgiargsclass () {
}


// this returns NULL if there isn't an entry with a value of
// 'key' already defined

void cgiargsclass::setarg (const text_t &key, const text_t &value,
			   cgiarg_t::source_t source) {
  cgiarg_t temparg;
  temparg.value = value;
  temparg.source = source;
  args[key] = temparg;
}

void cgiargsclass::setdefaultarg (const text_t &key, const text_t &value,
				  cgiarg_t::source_t source) {
  if (getarg(key) == NULL) setarg (key, value, source);
}

void cgiargsclass::setintarg (const text_t &key, int value,
			      cgiarg_t::source_t source) {
  setarg (key, value, source);
}

void cgiargsclass::setdefaultintarg (const text_t &key, int value,
				     cgiarg_t::source_t source) {
  if (getarg(key) == NULL) setintarg (key, value, source);
}

void cgiargsclass::setcarg (const text_t &key, unsigned short c,
			    cgiarg_t::source_t source) {
  text_t t; 
  t.push_back(c);
  setarg(key,t, source);
}

void cgiargsclass::setdefaultcarg (const text_t &key, unsigned short c,
				   cgiarg_t::source_t source) {
  if (getarg(key) == NULL) setcarg (key, c, source);
}

void cgiargsclass::setargfile (const text_t &key, const fileupload_t &fileupload) {
  iterator here = args.find (key);
  if (here != args.end()) {
    (*here).second.fileupload = fileupload;
  }
}

text_t *cgiargsclass::getarg (const text_t &key) {
  iterator here = args.find (key);
  if (here == args.end()) return NULL;

  return &((*here).second.value);
}

int cgiargsclass::getintarg (const text_t &key) {
  text_t *text = getarg (key);
  if (text == NULL) return 0;
  return text->getint();
}

fileupload_t *cgiargsclass::getargfile (const text_t &key) {
  iterator here = args.find (key);
  if (here == args.end()) return NULL;
  return &((*here).second.fileupload);
}




// stream operators to print cgi arguments for debugging purposes
ostream &operator<<(ostream &outs, const cgiargsclass &args) {
  utf8outconvertclass text_t2utf8;
  cgiargsclass::const_iterator here = args.begin ();
  cgiargsclass::const_iterator end = args.end ();

  outs << "*** cgiargsclass\n";
  
  while (here != end) {
    outs << text_t2utf8 << " \"" << (*here).first << "\"=\"" << 
      (*here).second.value << "\"\n";
    ++here;
  }
  outs << "\n";

  return outs;
}



cgiarginfo::cgiarginfo () {
  multiplechar = false;
  multiplevalue = false;
  fileupload = false;
  defaultstatus = weak;
  argdefault.clear();
  savedarginfo = can;
}


bool operator==(const cgiarginfo &x, const cgiarginfo &y) {
  return ((x.shortname == y.shortname) &&
	  (x.longname == y.longname) &&
	  (x.multiplechar == y.multiplechar) &&
	  (x.multiplevalue == y.multiplevalue) &&
	  (x.fileupload == y.fileupload) &&
	  (x.defaultstatus == y.defaultstatus) &&
	  (x.argdefault == y.argdefault) &&
	  (x.savedarginfo == y.savedarginfo));
}

bool operator<(const cgiarginfo &x, const cgiarginfo &y) {
  return ((x.shortname < y.shortname) ||
	  ((x.shortname == y.shortname) &&
	   ((x.longname < y.longname) ||
	    ((x.longname == y.longname) &&
	     ((x.multiplevalue < y.multiplevalue) ||
	      ((x.multiplevalue == y.multiplevalue) &&
	       ((x.fileupload == y.fileupload) &&
		((x.fileupload < y.fileupload) ||
		 ((x.multiplechar < y.multiplechar) ||
		  ((x.multiplechar == y.multiplechar) &&
		   ((x.defaultstatus < y.defaultstatus) ||
		    ((x.defaultstatus == y.defaultstatus) &&
		     ((x.argdefault < y.argdefault) ||
		      ((x.argdefault == y.argdefault) &&
		       ((x.savedarginfo < y.savedarginfo))))))))))))))));
}


// constructor
cgiargsinfoclass::cgiargsinfoclass () {
}

// addarginfo will combine the information with the present
// information. If name clashes were detected then the information
// will be written to logout (or stderr) and addarginfo will return false. No
// processing with the arguments should be done if this happens
// as the results will be meaningless.
bool cgiargsinfoclass::addarginfo (ostream *logout, cgiarginfo &info) {

  // if info.fileupload is set certain other fields are implicitly
  // overridden
  if (info.fileupload) {
    info.multiplechar = false;
    info.defaultstatus = cgiarginfo::weak;
    info.argdefault = "";
    info.savedarginfo = cgiarginfo::mustnot;
  }
  cgiarginfo *orginfo = getarginfo (info.shortname);
  if (orginfo == NULL) {
    argsinfo[info.shortname] = info;
    return true; // no clashes
  }

  if (orginfo->longname != info.longname) {
    outconvertclass text_t2ascii;
    if (logout != NULL) {
      (*logout) << text_t2ascii << "Error: cgi argument name clash for argument \""
		<< info.shortname << "\".\nOne long name was\n  \"" << orginfo->longname 
		<< "\"\nand the other was\n  \"" << info.longname << "\".\n\n";
    } else {
      cerr << text_t2ascii << "Error: cgi argument name clash for argument \""
	   << info.shortname << "\".\nOne long name was\n  \"" << orginfo->longname 
	   << "\"\nand the other was\n  \"" << info.longname << "\".\n\n";
    }
    return false; // found a clash
  }

  if (orginfo->multiplevalue != info.multiplevalue) {
    outconvertclass text_t2ascii;
    if (logout != NULL) {
      (*logout) << text_t2ascii << "Error: cgi argument \"" << info.shortname 
		<< "\" was given as being a single value option\n"
		<< "and a multiple value option.\n\n";
    } else {
      cerr << text_t2ascii << "Error: cgi argument \"" << info.shortname 
		<< "\" was given as being a single value option\n"
		<< "and a multiple value option.\n\n";
    }
    return false; // found a clash
  }

  if (orginfo->fileupload != info.fileupload) {
    outconvertclass text_t2ascii;
    if (logout != NULL) {
      (*logout) << text_t2ascii << "Error: cgi argument \"" << info.shortname 
		<< "\" was given as being a file upload argument\n"
		<< "and a non file upload argument.\n\n";
    } else {
      cerr << text_t2ascii << "Error: cgi argument \"" << info.shortname 
	   << "\" was given as being a file upload argument\n"
	   << "and a non file upload argument.\n\n";
    }
    return false; // found a clash
  }

  if (orginfo->multiplechar != info.multiplechar) {
    outconvertclass text_t2ascii;
    if (logout != NULL) {
      (*logout) << text_t2ascii << "Error: cgi argument \"" << info.shortname 
		<< "\" was given as being a single character option\n"
		<< "and a multiple character option.\n\n";
    } else {
      cerr << text_t2ascii << "Error: cgi argument \"" << info.shortname 
	   << "\" was given as being a single character option\n"
	   << "and a multiple character option.\n\n";
    } 
    return false; // found a clash
  }
  
  if (!info.multiplechar && info.argdefault.size() > 1) {
    outconvertclass text_t2ascii;
    if (logout != NULL) {
      (*logout) << text_t2ascii << "Error: cgi argument \"" << info.shortname
		<< "\" was defined as being a single character option\n"
		<< "but a multiple character default was given.\n\n";
    } else {
      cerr << text_t2ascii << "Error: cgi argument \"" << info.shortname
	   << "\" was defined as being a single character option\n"
	   << "but a multiple character default was given.\n\n";
    }
    return false;  // found a problem
  }

  // make sure there is no clashes in the savedarginfo
  if ((orginfo->savedarginfo==cgiarginfo::mustnot && 
       info.savedarginfo==cgiarginfo::must) ||
      (orginfo->savedarginfo==cgiarginfo::must && 
       info.savedarginfo==cgiarginfo::mustnot)) {
    outconvertclass text_t2ascii;
    if (logout != NULL) {
      (*logout) << text_t2ascii << "Error: it was specified that cgi argument \"" 
		<< info.shortname << "\" should be saved in the state\n"
		<< "information and that it should not be save in the state information.\n\n";
    } else {
      cerr << text_t2ascii << "Error: it was specified that cgi argument \"" 
	   << info.shortname << "\" should be saved in the state\n"
	   << "information and that it should not be save in the state information.\n\n";
    }
    return false; // found a clash
  }
  // the only time orginfo->savedarginfo can change is when it is set
  // to "can"
  if (orginfo->savedarginfo == cgiarginfo::can) 
    orginfo->savedarginfo = info.savedarginfo;


  if (orginfo->defaultstatus > info.defaultstatus) {
    return true;
  }
  orginfo->defaultstatus = info.defaultstatus;
  orginfo->argdefault = info.argdefault;

  return true;
}

bool cgiargsinfoclass::addarginfo (ostream *logout, cgiargsinfoclass &info) {
  
  iterator here = info.begin ();
  iterator end = info.end ();

  while (here != end) {
    if (!addarginfo (NULL, (*here).second)) {
      cerr << "returning false\n";
      return false;
    }
    ++here;
  }
  
  return true; // made it, no clashes
}

bool cgiargsinfoclass::addarginfo (ostream *logout, const text_t& argshortname, const text_tmap& mapinfo)
{
  cgiarginfo *arginfo = getarginfo (argshortname);
  if (arginfo == NULL) {
    arginfo = &(argsinfo[argshortname]);
    arginfo->shortname = argshortname;
    arginfo->defaultstatus = cgiarginfo::config;
  }
  text_tmap::const_iterator thisInfo = mapinfo.begin();
  text_tmap::const_iterator endInfo = mapinfo.end();
  while (thisInfo != endInfo) {
    if (thisInfo->first == "longname") arginfo->longname = thisInfo->second;
    else if (thisInfo->first == "multiplechar") arginfo->multiplechar = (thisInfo->second == "true");
    else if (thisInfo->first == "defaultstatus") {
      if (thisInfo->second == "none") arginfo->defaultstatus = cgiarginfo::none;
      else if (thisInfo->second == "weak") arginfo->defaultstatus = cgiarginfo::weak;
      else if (thisInfo->second == "good") arginfo->defaultstatus = cgiarginfo::good;
      else if (thisInfo->second == "config") arginfo->defaultstatus = cgiarginfo::config;
      else if (thisInfo->second == "imperative") arginfo->defaultstatus = cgiarginfo::imperative;
    }
    else if (thisInfo->first == "argdefault") {
      arginfo->argdefault = thisInfo->second;
    }
    else if (thisInfo->first == "savedarginfo") {
      if (thisInfo->second == "mustnot") arginfo->savedarginfo = cgiarginfo::mustnot;
      else if (thisInfo->second == "can") arginfo->savedarginfo = cgiarginfo::can;
      else if (thisInfo->second == "must") arginfo->savedarginfo = cgiarginfo::must;
    } else if (logout != NULL) {
      (*logout) << ("Invalid argument for cgiarg for " + argshortname + " (" + thisInfo->first + ")\n");
    }
    ++thisInfo;
  }
  return true;
}

bool cgiargsinfoclass::addarginfo (ostream *logout, confcgiarg_tmap& info)
{
  confcgiarg_tmap::const_iterator thisArg = info.begin();
  confcgiarg_tmap::const_iterator endArg = info.end();
  while (thisArg != endArg) {
    if (!addarginfo(logout, thisArg->first, thisArg->second)) return false;
    ++thisArg;
  }
  return true;
}

cgiarginfo *cgiargsinfoclass::getarginfo (const text_t &key) {
  iterator here = argsinfo.find (key);
  if (here == argsinfo.end()) return NULL;

  return &((*here).second);
}

const cgiarginfo *cgiargsinfoclass::getarginfo (const text_t &key) const {
  const_iterator here = argsinfo.find (key);
  if (here == argsinfo.end()) return NULL;

  return &((*here).second);
}

// stream operators to print cgiarginfo for debugging purposes
ostream &operator<<(ostream &outs, const cgiargsinfoclass &argsinfo) {
  utf8outconvertclass text_t2utf8;
  cgiargsinfoclass::const_iterator here = argsinfo.begin ();
  cgiargsinfoclass::const_iterator end = argsinfo.end ();

  outs << "*** cgiargsinfoclass\n";
  
  while (here != end) {
    outs << text_t2utf8 << (*here).first << ", ";
    ++here;
  }
  outs << "\n";

  return outs;
}
