// $Id: hash.cpp,v 1.14 2008-03-13 04:45:05 Rob_Andre Exp $

/**
 * API allowing to access variables by name. This is achieved by filling
 * in a C++ hash table, which can grow at will. One application of key_access
 * is the setting and getting of input/outp parameters in driver codes 
 * *without* the repetive need to modify the code API as the number of 
 * control parameters grow. 
 * 
 * A. Pletzer.
 *
 * R. Andre, using map class now until unordered_map becomes standard.
 */
#include <string.h>
#include <iostream>
#include <fstream>

#define IOS_BASE ios_base

#include <map>
#include <vector>
#include <algorithm>

using namespace std;

template<class T> class print {
public:
  print(ostream &os) : _os(os) {}
  void operator()(T p){
    _os.precision(12);
    _os.setf(ios::scientific);
    _os << ' ' << p.first << '=' << p.second << endl;
  }
  ostream &_os;
};

class print_bool {
public:
  print_bool(ostream &os) : _os(os) {}
  void operator()(pair<string, bool> p){
    char val = 'F';
    if(p.second) val = 'T';
    _os << ' ' << p.first << '=' << val << endl;
  }
  ostream &_os;
};

class print_bool_array {
public:
  print_bool_array(ostream &os) : _os(os) {}
  void operator()(pair<string, vector<bool> > p){
    _os << ' ' << p.first << "=";
    for(size_t i=0; i<p.second.size(); ++i){
      char val = 'F';
      if(p.second[i]) val = 'T';
      _os << val << ' ';
    }
    _os << endl;    
  }
  ostream &_os;
};

template<class T> class print_array {
public:
  print_array(ostream &os) : _os(os) {}
  void operator()(T p){
    _os << ' ' << p.first << "=";
    for(size_t i=0; i<p.second.size(); ++i)
      _os << p.second[i] << ' ';
    _os << endl;
  }
  ostream &_os;
};


extern "C" {
  // list of has table placeholders (one for each type)
  map<string, double > kaData_double; 
  map<string, vector<double> > kaData_double_array; 

  map<string, int > kaData_int; 
  map<string, vector<int> > kaData_int_array; 

  map<string, bool > kaData_bool; 
  map<string, vector<bool> > kaData_bool_array; 

#include "fpreproc/f77name.h"

  /**
   * initialize (or reinitialize) hash_table
   */
  void F77NAME(key2_init)(){
    kaData_double.clear();
    kaData_double_array.clear();

    kaData_int.clear();
    kaData_int_array.clear();

    kaData_bool.clear();
    kaData_bool_array.clear();
  }

  /**
   * Setters
   */
  void F77NAME(c_key2double_set)(char *var_name, double *val){
    kaData_double[var_name] = *val;
  }

  void F77NAME(c_key2doublearray_set)(char *var_name, double *val, int* nsize){
    kaData_double_array[var_name].reserve(*nsize);
    for(size_t i=0; i<*nsize; ++i) {
      kaData_double_array[var_name].push_back( *(val+i) );
    }
  }

  void F77NAME(c_key2int_set)(char *var_name, int *val){
    kaData_int[var_name] = *val;
  }

  void F77NAME(c_key2intarray_set)(char *var_name, int *val, int* nsize){
    kaData_int_array[var_name].reserve(*nsize);
    for(size_t i=0; i<*nsize; ++i) {
      kaData_int_array[var_name].push_back( *(val+i) );
    }
  }

  void F77NAME(c_key2bool_set)(char *var_name, int* val){
    bool v = false;
    if(*val!=0) v = true;
    kaData_bool[var_name] = v;
  }

  void F77NAME(c_key2boolarray_set)(char *var_name, int *val, int* nsize){
    kaData_bool_array[var_name].reserve(*nsize);
    for(size_t i=0; i<*nsize; ++i) {
      bool v = false;
      if(*(val+i)!=0) v = true;
      kaData_bool_array[var_name].push_back(v);
    }
  }

  /**
   * Getters
   */
  void F77NAME(c_key2double_get)(char *var_name, double *val){
    if(kaData_double.find(string(var_name)) != kaData_double.end())
      *val = kaData_double[var_name];
  }

  void F77NAME(c_key2doublearray_get)(char *var_name, double *val, int* nsize){
    if(kaData_double_array.find(string(var_name)) != kaData_double_array.end()){
      for(size_t i=0; i<*nsize; ++i){
	*(val+i) = kaData_double_array[var_name][i];
      }
    }
  }

  void F77NAME(c_key2int_get)(char *var_name, int *val){
    if(kaData_int.find(string(var_name)) != kaData_int.end()){
      *val = kaData_int[var_name];
    }
  }

  void F77NAME(c_key2intarray_get)(char *var_name, int *val, int* nsize){
    if(kaData_int_array.find(string(var_name)) != kaData_int_array.end()){
      for(size_t i=0; i<*nsize; ++i){
	*(val+i) = kaData_int_array[var_name][i];
      }
    }
  }

  void F77NAME(c_key2bool_get)(char *var_name, int *val){
    if(kaData_bool.find(string(var_name)) != kaData_bool.end())
      *val = 0;
    bool v = kaData_bool[var_name];
    if(v) *val = 1;
  }

  void F77NAME(c_key2boolarray_get)(char *var_name, int *val, int* nsize){
    if(kaData_bool_array.find(string(var_name)) != kaData_bool_array.end()){
      for(size_t i=0; i<*nsize; ++i){
	*(val+i) = 0;
	bool v = kaData_bool_array[var_name][i];
	if(v) *(val+i) = 1;
      }
    }
  }

  /**
   * Print
   */

  void F77NAME(key2print)(){
    for_each(kaData_int.begin(), kaData_int.end(), 
	     print<pair<string,int> >(cout));
    for_each(kaData_int_array.begin(), kaData_int_array.end(), 
	     print_array<pair<string,vector<int> > >(cout));
    for_each(kaData_double.begin(), kaData_double.end(), 
	     print<pair<string,double> >(cout));
    for_each(kaData_double_array.begin(), kaData_double_array.end(), 
	     print_array<pair<string,vector<double> > >(cout));
    for_each(kaData_bool.begin(), kaData_bool.end(), 
	     print_bool(cout));
    for_each(kaData_bool_array.begin(), kaData_bool_array.end(), 
	     print_bool_array(cout));
  }

  /**
   * Cleanup
   */
  void F77NAME(key2_free)(){
    key2_init_();
  }
    
  /**
   * Convert table into fortran namelist 
   */
  void F77NAME(c_key2write_namelist)(char *filename, char *namelist, char *m){
    // add termination character
    size_t n1 = strlen(filename) ;
    size_t n2 = strlen(namelist) ;
    if (n1>512) n1=512 ;
    if (n2>512) n2=512 ;

    char fname[n1+1], nmlst[n2+1];
    strncpy(fname, filename, n1);
    strncpy(nmlst, namelist, n2);
    fname[n1] = '\0';
    nmlst[n2] = '\0';
    IOS_BASE::openmode mode = IOS_BASE::out;
    if(*m=='a') {
      mode =  IOS_BASE::app; // append
    }
    ofstream file(fname, mode);
    file << " &" << nmlst << endl;
    for_each(kaData_int.begin(), kaData_int.end(), 
	     print<pair<string,int> >(file));
    for_each(kaData_int_array.begin(), kaData_int_array.end(), 
	     print_array<pair<string,vector<int> > >(file));
    for_each(kaData_double.begin(), kaData_double.end(), 
	     print<pair<string,double> >(file));
    for_each(kaData_double_array.begin(), kaData_double_array.end(), 
	     print_array<pair<string,vector<double> > >(file));
    for_each(kaData_bool.begin(), kaData_bool.end(), 
	     print_bool(file));
    for_each(kaData_bool_array.begin(), kaData_bool_array.end(), 
	     print_bool_array(file));
    file << " /\n";
    file.close();
  }

}


