/**********************************************************************
 *
 * display.h -- Context sensitive macro language
 * 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.
 *
 *********************************************************************/


#ifndef DISPLAY_H
#define DISPLAY_H

#include "gsdlconf.h"
#include "text_t.h"


#if defined(GSDL_USE_OBJECTSPACE)
#  include <ospace\std\vector>
#  include <ospace\std\list>
#  include <ospace\std\set>
#  include <ospace\std\map>
#  include <ospace\std\algorithm>
#elif defined(GSDL_USE_STL_H)
#  include <vector.h>
#  include <list.h>
#  include <set.h>
#  include <map.h>
#  if defined(GSDL_USE_ALGO_H)
#    include <algo.h>
#  else
#    include <algorithm.h>
#  endif
#else
#  include <vector>
#  include <list>
#  include <set>
#  include <map>
#  include <algorithm>
#endif

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

#define unistream ifstream
#else
#  include <iostream>
#  include <fstream>

typedef std::basic_ifstream<unsigned char> unistream;

#endif


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



// MAXRECURSIONDEPTH is a cutoff to catch
// cyclic macros (a includes b and b includes a)
#define MAXRECURSIONDEPTH 30

// class prototypes
class parammacros_t;
class currentmacros_t;

// a few supporting types
// fileelement isn't finished yet, I have to find out
// more about getting information about files
struct fileinfoelement 
{
  int otherinfo;
};

// macro value structure
struct mvalue
{
  text_t filename;
  text_t value;
};

inline bool operator==(const fileinfoelement &x, const fileinfoelement &y) {
  return (x.otherinfo==y.otherinfo);
}

inline bool operator<(const fileinfoelement &x, const fileinfoelement &y) {
  return (x.otherinfo<y.otherinfo);
}

typedef map<text_t, fileinfoelement, lttext_t> fileinfomap;


typedef map<text_t, text_t, lttext_t> paramhashtype;
void splitparams (const text_t &paramstring, paramhashtype &paramhash);


struct paramspec 
{
  double spec;
  text_t param;
};

inline bool operator==(const paramspec& x, const paramspec& y) 
{
  return ((x.spec == y.spec) && (x.param == y.param));
}

inline bool operator!=(const paramspec& x, const paramspec& y) 
{
  return ((x.spec != y.spec) || (x.param != y.param));
}

// note: paramspec is sorted by reverse spec and then param

inline bool operator<(const paramspec& x, const paramspec& y) 
{
  return ((x.spec > y.spec) ||
	  ((x.spec == y.spec) && (x.param < y.param)));
}

inline bool operator>(const paramspec& x, const paramspec& y) 
{
  return ((x.spec < y.spec) ||
	  ((x.spec == y.spec) && (x.param > y.param)));
}

typedef vector<paramspec> paramspeclist;



// NOTE: when macros are used within text they should have
// underscores '_' on both sides, however, in calls to
// setdefaultmacro and setmacro they should not have the
// underscores. So you might have
//
// setmacro ("aname", "query", " some text ");
// expandstring ("_aname_", expandedtext);
//
// because the input to expandstring is a block of text, not
// a macroname

class displayclass 
{
public:
  static text_t defaultpackage;

  displayclass ();
  ~displayclass ();
  
  // isdefaultmacro sees if there is an entry in the list of
  // default macros with the given package and macro name
  // returns 0 if no macros in the package or in the global package
  //           were found
  //         1 if no macros in the package were found but a macro
  //           in the global package was found
  //         2 if a macro in the given package was found
  int isdefaultmacro (const text_t &package, const text_t &macroname);

  // setdefaultmacro adds an entry to the list of default macros 
  // returns 0 if there was no error,
  //        -1 if it redefined a macro
  //        -2 if it hid a Global macro
  //        -3 if it redefined a macro and hid a Global macro
  //        -4 if no macroname was supplied
  int setdefaultmacro (const text_t &package, 
		       const text_t &macroname, 
		       const text_t &params, 
		       const text_t &macrovalue);
  
  // loads a default macro file (if it isn't already loaded)
  // returns 0 if didn't need to load the file (it was already loaded)
  //         1 if was (re)loaded
  //        -1 an error occurred while trying to load the file
  int loaddefaultmacros (const text_t &thisfilename);


  // overrides (or sets) a macro for the current page.
  // returns 0 if there was no error,
  //        -1 if it redefined a macro
  //        -4 if no macroname was supplied
  int setcollectionmacro (const text_t &package, 
			  const text_t &macroname, 
			  text_t params, 
			  const text_t &macrovalue);

  // loads a collection specific macro file 
  // returns 0 if didn't need to load the file (it was already loaded)
  //         1 if was (re)loaded
  //        -1 an error occurred while trying to load the file
  int loadcollectionmacros (const text_t &thisfilename);

  // unloads all default macros
  void unloaddefaultmacros ();

  // unloads all collection specific macros
  void unloadcollectionmacros ();
  
  // prepares to create a page: deletes all macros set with
  // 'setmacro', checks all the default macro files to see
  // if any need reloading, and sets the page parameters.
  void openpage (const text_t &thispageparams, 
		 const text_t &thisprecedence);
  
  // changes the parameters for the current page.
  void setpageparams (text_t thispageparams, 
		      text_t thisprecedence);
  
  // overrides (or sets) a macro for the current page.
  // returns 0 if there was no error,
  //        -1 if it redefined a macro
  //        -4 if no macroname was supplied
  int setmacro (const text_t &macroname, 
		const text_t &package, 
		const text_t &macrovalue);
  
// havemacro sees if there is an entry in the list of macros or
// default macros with the given package and macro name
// returns 0 if no macros in the package or in the global package
//           were found
//         1 if no macros in the package were found but a macro
//           in the global package was found
//         2 if a macro in the given package was found
//         4 if no macros in the package were found but a macro
//           in the global package was found in default macros
//         8 if a macro in the given package was found in default
  int havemacro(const text_t &package, const text_t &macroname);

  int setmacroif (const text_t &macro_to_set_and_test, 
		  const text_t &macro_to_set_if_macro_not_set,
		  const text_t &macroname, 
		  const text_t& package);
  
  inline void expandstring (const text_t &inputtext, text_t &outputtext) { expandstring(defaultpackage, inputtext, outputtext); }

  void expandstring (const text_t &package, const text_t &inputtext,
		     text_t &outputtext, int recursiondepth = 0);

  // these functions are not ment to be used directly, they are used to permit 
  // concise stream output like:
  // cout << text_t2ascii << display << "_amacro_" << "_anothermacro_";
  void setconvertclass (outconvertclass *theoutc) {outc = theoutc;}
  outconvertclass *getconvertclass () {return outc;}

  // say where any error logging goes, this can be NULL
  // if no error logging is desired - returns previous value
  ostream *setlogout (ostream *thelogout);

  // debug stuff
  void printdefaultmacros ();
  void printallparams ();

protected:
  // special variables to permit trickly output
  outconvertclass *outc;

  // general variables
  text_tset        allparams;  // possible parameter combinations
  parammacros_t *defaultmacros;
  parammacros_t *collectionmacros;
  fileinfomap     *defaultfiles;
  
  // variables to do with a page expansion
  text_t params;
  text_t precedence;
  paramspeclist *orderparamlist; // ordered by specificness
  currentmacros_t *currentmacros; // only macros set by setmacro

  // logging variable
  ostream *logout;

  // reloads any default macro files which have changed
  // returns 0 no errors occurred
  //        -1 an error occurred while trying to load one of the files
  int checkdefaultmacrofiles ();

  int loadparammacros (parammacros_t* macrotable, const text_t &thisfilename);

  int setparammacro (parammacros_t* macrotable,
		     const text_t &package, 
		     const text_t &macroname, 
		     text_t params, const text_t &filename,
		     const text_t &macrovalue); 

  int setdefaultmacro (const text_t &package, 
		       const text_t &macroname, 
		       text_t params, const text_t &filename,
		       const text_t &macrovalue); 

  int setcollectionmacro(const text_t &package, 
			 const text_t &macroname, 
			 text_t params, 
			 const text_t &filename,
			 const text_t &macrovalue); 
  
  // evaluates a boolean expression
  bool boolexpr (const text_t &package, const text_t &expr, 
		 int recursiondepth);

  mvalue* macro_find (parammacros_t* macrotable, 
		      const text_t &packagename,
		      const text_t &macroname);

  // (recursively) expand out a macro
  // returns true  if the macro was found
  //         false if the macro was not found
  bool macro (const text_t &macroname, 
	      const text_t &macropackage, 
	      const text_t &macroparam, text_t &outputtext,
	      int recursiondepth);
};


displayclass &operator<< (outconvertclass &theoutc, displayclass &display);
displayclass &operator<< (displayclass &display, const text_t &t);

#endif
