/* globals.h
 *
 * $Id: globals.h,v 1.2 2011/10/16 22:32:49 gildea Exp $
 */

#ifndef GLOBALS_H
#define GLOBALS_H

#include <string>
#include <vector>
#include <map>
#include <ctime>
#include <cmath>
#include <list>
#include <stdlib.h>
#include <assert.h>

#include "ConfigFile.h"
#include "vocab.h"
#include "feature.h"

#ifndef INFINITY
extern "C" {
#define INFINITY (__builtin_inff())
}
#endif


/* WORDTYPE */
typedef int WORDTYPE;

extern FILE* logfile;

extern Vocab l1vocab;
extern Vocab l2vocab;

extern FeatureList weights;

class ITGGrammar;
extern ITGGrammar* grammar;

class NGram;
extern NGram* ngram;

string itos(int i);
string mytime(const time_t);

/* CONSTANTS representing config parameters, set in main */
extern double LMWEIGHT;
extern double LOGRATIOBEAM;
extern int MAXKBUFSIZE;

/* convenience functions */
string G_STRING(const string&);
int    G_INT(const string&);
bool   G_BOOL(const string&);
double G_DOUBLE(const string&);
/* #define GLOBAL_INT(x)    Globals::Get<int>("x") */
/* #define GLOBAL_STRING(x) Globals::Get<string>("x") */
/* #define GLOBAL_DOUBLE(x) Globals::Get<double>("x") */
/* #define GLOBAL_BOOL(x)   Globals::Get<bool>("x") */

/* class Globals
 *
 * This class is responsible for setting parameters for the program,
 * and making them accessible via a single, global instance.  It can
 * read the parameters from a configuration file and/or take them from
 * the command line.  Command-line parameters override values found in
 * the configuration file.
 */
class Globals {
 public:
  // called once, initializes the object
  static void Load(int, char**);   
  // returns the global instance
  static Globals* Instance();
  // allows direct retrieval of parameter values
  template<class T> static T Get(const char* key);
  template<class T> static T Get(const string& key);
  // check whether key exists
  bool has_key(const char* key) const {
    return params->ConfigFile::keyExists(key) && get<string>(key) != "";
  }
  // directly check if a key exists
  static bool Has_key(const char * key) {
    return _instance->has_key(key);
  }

  // Exception
  struct load_error {
    string text;
    load_error( const string& text_ = string() )
      : text(text_) {}
  };


 protected:
  // private constructor prevents multiple instantiations
  Globals(int,char**);
 private:
  // the instance
  static Globals* _instance;
  // holds the parameters
  ConfigFile *params;
  // complete list of parameters
  map<string, int> list;
  // dependencies for each mode
  map<string, vector<string> > deps;
  // finds keys matching the argument
  vector<string> find_keys(string);

 public:
  // static Globals::Get chains to this
  template<class T> T get(const char* key) const;

  // write the configuration file to an output stream
  friend ostream& operator<<( ostream&, const Globals & );
};

template<class T>
inline T Globals::get(const char *key) const
{ 
  T val;
  try {
    val = params->ConfigFile::read<T>(string(key));
  } catch (ConfigFile::key_not_found knf) {
    cerr << "* FATAL: no entry for key = " << knf.key << endl;
    exit(1);
  }

  return val;
}

template<class T>
inline T Globals::Get(const string& key)
{ 
  return _instance->get<T>(key.data());
}

template<class T>
inline T Globals::Get(const char *key)
{ 
  return _instance->get<T>(key);
}

template<class T>
string Xtos(T i)
{
  stringstream s;
  s << i;
  return s.str();
}

/* holds K of something, sorted from high to low */
template<class X> struct TopK {
  list<X> topK;
  unsigned k;

  TopK(unsigned ik = 1) : k(ik) { }

  typedef typename list<X>::iterator iterator;

  iterator begin() { return topK.begin(); }

  iterator end() { return topK.end(); }

  int size() { return topK.size(); }

  string str() {
    stringstream rev;
    typename list<X>::iterator i;
    for (i = topK.begin(); i != topK.end(); i++)
      rev << (rev.str().length() ? " " : "") << *i;
    return rev.str();
  }

  void clear() { topK.clear(); }

  void insert(const X& val) {
    typename list<X>::iterator iter = topK.begin();
    unsigned i;
    for (i=0; iter != topK.end(); iter++, i++) {
      if (val == *iter)
        return;
      if (val > *iter)
        break;
    }
    if (i < k)
      topK.insert(iter, val);

    if (topK.size() > k) {
      iter = topK.end();  iter--;
      topK.erase(iter);
    }
  }

  X max() { return topK.front(); }
  X min() { 
    if (topK.size() < k)  // if the list isn't filled up, there is no min
      return -INFINITY;
    return topK.back();
  }
};


ostream& operator<<(ostream&, const WORDTYPE*);

/* constructs a string phrase from a WORDTYPE*, optionally
 * translating each id to a word first */
string transliterate(WORDTYPE*, Vocab* v = NULL);

/* tokenizes a string */
vector<string> split(const string&, const string& delim = string(" "));

void print_header(const string&, const char symbol = '=');
bool is_comment_or_empty_line(string& line);

#endif

