// $Id: gen_driver.hpp 14 2004-05-06 08:42:57Z conrado $
// (C) Conrado Martinez Parra, Diciembre, 2003

#ifndef _GEN_DRIVER_HPP
#define _GEN_DRIVER_HPP

#include <map>
#include <string>
#include <iostream>
#include <fstream>
#include <vector>

#include <eda/util>
#include <eda/mem_din>

using util::nat;

typedef void* DataStructure;

template <typename T>
DataStructure _initcopy(DataStructure p) {
  return static_cast<void*>(new T(*(static_cast<T*>(p))));
}

template <typename T>
void _copy(DataStructure dest, DataStructure orig) {
  *static_cast<T*>(dest) = *static_cast<T*>(orig);
}
  
template <typename T>
void _destroy(DataStructure p) {
  delete static_cast<T*>(p);
}

template <typename T>
void _defctor(T*& p) {
  p = new T();
}

class gen_driver {
  public : 

  typedef void (*DriverFunction)(gen_driver&); 
  typedef bool (*MemTestFunction)(gen_driver&);
  typedef bool (*CheckFunction)(const string&);
  typedef void (*BuildFunct)(void*&);

  gen_driver(bool echoing = true, istream& is = std::cin, 
                                  ostream& os = std::cout);
  ~gen_driver();

  void add_call(const string& fname, DriverFunction f, 
                const string& applies_to = "*",
                const string& type_args = "",
		const string& helpmsg = "");

  void add_check(const string& tid, CheckFunction f);

  template <typename T>
  void add_memory_test(MemTestFunction tmf, const string& _class = "") {
    string _c = _class;
    if (_c == "")
      _c = T::nom_mod;
    _tm[_c].push_back(tmf);
  }

  template <typename T>
  void install_type() {
    string _class = T::nom_mod;
    _copyctor[_class] = _initcopy<T>;
    _assignment[_class] = _copy<T>; 
    _dtor[_class] = _destroy<T>;
  }

  template <typename T>
  void install_std_type(const string& _class) {
    _copyctor[_class] = _initcopy<T>;
    _assignment[_class] = _copy<T>; 
    _dtor[_class] = _destroy<T>;
  }

  template <typename T>
  bool generic_memtest(void (*_build)(T*&), const string& _msg, 
		       const string _class = "") {
    string theclass = (_class == "") ? T::nom_mod : _class;
    return generic_memtest(reinterpret_cast<BuildFunct>(_build),
			   theclass,_msg);
  }

  template <typename T>
  bool copyctor_memtest(void (*_build)(T*&), const string& _class = "") {
    string theclass = (_class == "") ? T::nom_mod : _class;
    return copyctor_memtest(reinterpret_cast<BuildFunct>(_build),theclass);
  }

  template <typename T>
  bool assgn_memtest(void (*_build1)(T*&), void (*_build2)(T*&),
		     const string& _class = "") {
    string theclass = (_class == "") ? T::nom_mod : _class;
    return assgn_memtest(reinterpret_cast<BuildFunct>(_build1),
			 reinterpret_cast<BuildFunct>(_build2),
			 theclass);
  }

  template <typename T>
  T* object(const string& obj_name) {
    if (_obj.find(obj_name) == _obj.end())
      throw error(nom_mod, NoObj, NoObjMsg);
    return static_cast<T*>(_obj[obj_name]);
  }

  template <typename T>
  T* object(int i) {
    if (_obj.find(_il->args(i)) == _obj.end())
      throw error(nom_mod, NoObj, NoObjMsg);
    return static_cast<T*>(_obj[_il->args(i)]);
  }

  template <typename T> 
  T* object() {
    return static_cast<T*>(_obj[_il->object()]);
  }

  string object_type(const string& obj_name);
  string object_type();
  string object_type(int i);
  bool has_type(int i);

  string args(int i);
  nat nargs();

  void go();
  
  istream& get_istream() const { return _isdriv; };
  ostream& get_ostream() const { 
    if (_echoing) 
      return _osdriv; 
    else 
      if (_null_stream == NULL)
	_null_stream = new ofstream("/dev/null");
    return *_null_stream;
  };

  bool echoing() const { return _echoing; };

  void init();
  void copy();
  void initcopy();
  void destroy();
  void test_memory();
  void set_memory();
  void print_memory();
  void echo();
  void load();
  void apply();
  void help(string op = "");
  void select_curr_obj();
  void list_objects();
  void curr_obj_name();
  void applies_to();
  void echo_output();
  void echo_input();
  void timer_on();
  void timer_off();

  // gestion de errores
  static const char nom_mod[] = "gen_driver";

  static const int NoOp          = 1;
  static const int WrongNumArgs  = 2;
  static const int WrongTypeArgs = 3;
  static const int NoObj         = 4;
  static const int WrongTypeObj  = 5;
  static const int NoFile        = 6;

  static const char NoOpMsg[] = "Operacion incorrecta";
  static const char WrongNumArgsMsg[] = "Num. argumentos incorrecto";
  static const char WrongTypeArgsMsg[] = "Tipo argumentos incorrecto";
  static const char NoObjMsg[] = "Objeto inexistente";
  static const char WrongTypeObjMsg[] = "Operacion no aplicable sobre objeto";
  static const char NoFileMsg[] = "No se puede abrir el fichero";

private :
  
  class input_line {
  public :      
    input_line(ostream& echo_ostream = std::cout) : 
      _echo_stream(echo_ostream) {};

    input_line& operator=(const input_line& L) {
      if(this != &L) {
	_args = L._args;
	_orig = L._orig;
	_object = L._object;
	_op = L._op;
      }
      return *this;
    }
    
    util::nat nargs() const { return static_cast<util::nat>(_args.size()); };
    string args(int i) const throw(error);
    void push_back(const input_line& L);
    void push_back(const string& s);
    
    string object() const            { return _object; }
    string op() const                { return _op; }
    void set_object(const string& s) { _object = s; };
    void set_op(const string& s)     { _op = s; };
    void shift_args(int i);
    
    static void echo_on()   { input_line::_echoing = true; };
    static void echo_off()  { input_line::_echoing = false; };
    static bool echoing()   { return input_line::_echoing; }; 
    
    friend istream& operator>>(istream& is, input_line& L);
    friend ostream& operator<<(ostream& os, const input_line& L);
    
    const static int ArgNoExiste       = 8;
    const static char ArgNoExisteMsg[] = "Argumento inexistente";
    const static char nom_mod[]        = "gen_driver::input_line";
    
  private : 
    static bool _echoing;  
    static const int MAX_LINE_LENGTH = 20000;
    ostream& _echo_stream;
    vector<string> _args;
    vector<string> _orig;
    string _object;
    string _op;
  };

  typedef void* (*CopyCtorFunction)(void*);
  typedef void  (*AsgnFunction)(void*, void*);
  typedef void (*DtorFunction)(void*);

  typedef map<string, DriverFunction> DriverFunctTbl;
  typedef map<string, CheckFunction>  CheckFunctTbl;
  typedef map<string, DataStructure> ObjTbl;
  typedef map<string, string> TypeTbl;
  typedef map<string, string> AppliesToTbl;
  typedef map<string, string> HelpTbl;
  typedef map<string, vector<string> > ArgTypeTbl;
  
  typedef map<string, CopyCtorFunction> CopyCtorTbl;
  typedef map<string, AsgnFunction> AsgnTbl;
  typedef map<string, DtorFunction> DtorTbl;
  typedef map<string, vector<MemTestFunction> > MemTestTbl;

  DriverFunctTbl _funct;
  CheckFunctTbl _check;
  HelpTbl _help;
  ArgTypeTbl _type_args;
  ObjTbl _obj;
  TypeTbl _type;
  AppliesToTbl _applies_to;

  CopyCtorTbl _copyctor;
  AsgnTbl _assignment;
  DtorTbl _dtor;
  MemTestTbl _tm;

  string _curr_obj;
  input_line* _il;

  bool _echoing;
  istream& _isdriv;
  ostream& _osdriv;

  static ofstream* _null_stream;

  void process_operation(input_line* L);     
  string build_help_message(const string& op);
  bool generic_memtest(BuildFunct b, const string& cl, const string& msg);
  bool copyctor_memtest(BuildFunct b, const string& cl);
  bool assgn_memtest(BuildFunct b1, BuildFunct b2, const string& cl);

  // copy-ctor y asignacion privadas
  gen_driver(const gen_driver& G): _isdriv(G._isdriv), _osdriv(G._osdriv) {};
  gen_driver& operator=(const gen_driver&) { return *this; }
};

template <typename T>
bool test_defctor(gen_driver& d) {
  return d.generic_memtest(_defctor<T>, "default ctor");
}
#endif


