#include "CWriteOml.h"
#include <iostream>
#include <vector>
#include <map>
#include <string.h>
#include "oml2/omlc.h"

//#include <boost/lexical_cast.hpp>
#include <boost/asio/ip/host_name.hpp>

#define DBG_OUT(x) std::cerr << #x << " = " << x << std::endl

CWriteOml::CWriteOml()
{
  bReady = FALSE;
  _HostName = boost::asio::ip::host_name();
}

CWriteOml::CWriteOml(std::string oml_expid, std::string db_filename, std::string server_name)
{
  bReady = FALSE;
  _HostName = boost::asio::ip::host_name();
  init(oml_expid, db_filename, server_name);
}    

CWriteOml::~CWriteOml()
{
  omlc_close();
}


void CWriteOml::init(std::string oml_expid, std::string db_filename, std::string server_name)
{
  _db_filename = db_filename;
  _server_name = server_name;

  int argc;
  const char** argv;
  std::vector<char*> arg_vector;

  arg_vector.push_back((char*)"./spectrum");
  arg_vector.push_back((char*)"--oml-id");       // the option for sender
  arg_vector.push_back((char*)oml_expid.c_str());
  arg_vector.push_back((char*)"--oml-domain");   // the option for storage database filename
  arg_vector.push_back((char*)db_filename.c_str());
  arg_vector.push_back((char*)"--oml-collect");  // the option for storage
  arg_vector.push_back((char*)server_name.c_str());
  argv = (const char**)&arg_vector[0];

  argc = arg_vector.size();

  int result = omlc_init ("_mp_", &argc, argv, NULL);
  if (result == -1) {
    std::cerr << "Could not initialize OML\n";
    exit (1);
  }

}

void CWriteOml::register_mp(std::string str_variable, OmlValueT oml_value_type)
{
  _oml_mps.push_back( std::make_pair(str_variable, oml_value_type) );

}


void CWriteOml::start()
{
  int result;

  _MeasurementPoints = _oml_mps.size();

  mp_def = new OmlMPDef [(sizeof(OmlMPDef) * (_MeasurementPoints + 1) )];

  // define measurement points
  unsigned int idx;
  for (idx = 0; idx < _MeasurementPoints; ++idx)
    createMeasurementPoint(&mp_def[idx], _oml_mps.at(idx).first, (OmlValueT)_oml_mps.at(idx).second);
  createMeasurementPoint(&mp_def[idx],      "NULL",        (OmlValueT)0);

  _mp_handle = omlc_add_mp (_db_filename.c_str(), mp_def); // using db_filename as tag name for measurement point

  if (_mp_handle == NULL) {
    std::cerr << "Error: could not register Measurement Point \"data\"";
    exit (1);
  }

  result = omlc_start();
  if (result == -1) {
    std::cerr << "Error starting up OML measurement streams\n";
    exit (1);
  }

  // allocate memory measurement points
  _values = (OmlValueU*) malloc(sizeof(OmlValueU) * _MeasurementPoints);
  memset((void*)_values, 0, sizeof(OmlValueU) * _MeasurementPoints );

  // create oml key <==> (type,value) mapping
  _KTVMap.clear();
  for (unsigned int idx = 0; idx < _MeasurementPoints; ++idx) {
    std::pair<OmlValueT, OmlValueU*> TV (_oml_mps.at(idx).second, (OmlValueU*)&_values[idx] );
    
    std::pair<std::string, std::pair<OmlValueT, OmlValueU*> > KTV( _oml_mps.at(idx).first, TV );
    _KTVMap.insert( KTV );
  }

  bReady = TRUE;
}


void CWriteOml::set_mp(std::string key_str, void* val_ptr)
{

  _KTVMapIter = _KTVMap.find(key_str);
  if (_KTVMapIter == _KTVMap.end()) {
    std::cerr << "Warn: " << __func__ << "::" << key_str << " not found" << std::endl;
    return;  // key not found so return and do nothing
  }

  //key found to look at type are call appropriate oml intrinsic function
  std::pair<OmlValueT, OmlValueU*> TV = _KTVMapIter->second;
  switch( TV.first ) {
  case OML_INT32_VALUE :
    omlc_set_int32   ( *(TV.second), (int32_t) (*((int32_t*)val_ptr)));
    break;

  case OML_UINT32_VALUE :
    omlc_set_uint32   ( *(TV.second), (uint32_t) (*((uint32_t*)val_ptr)));
    break;

  case OML_INT64_VALUE :
    omlc_set_int64   ( *(TV.second), (int64_t) (*((int64_t*)val_ptr)));
    break;
  case OML_UINT64_VALUE :
    omlc_set_uint64   ( *(TV.second), (uint64_t) (*((uint64_t*)val_ptr)));
    break;
  case OML_DOUBLE_VALUE :
    omlc_set_double   ( *(TV.second), (double) (*((double*)val_ptr)));
    break;
  case OML_STRING_VALUE :
    omlc_set_string( *(TV.second), (char*)val_ptr);
    break;
    // add other cases here
  default :
    std::cerr << "Warn: " << __func__ << "::" << "unknow OML type, value: " << TV.first << " , " << TV.second << std::endl;
    break;
  }

  return;
}

void CWriteOml::set_mp_blob(std::string key_str, void* val_ptr, unsigned int omlblob_bytes)
{

  _KTVMapIter = _KTVMap.find(key_str);
  if (_KTVMapIter == _KTVMap.end()) {
    std::cerr << key_str << " not found" << std::endl;
    return;  // key not found so return and do nothing
  }

  //key found to look at type are call appropriate oml intrinsic function
  std::pair<OmlValueT, OmlValueU*> TV = _KTVMapIter->second;
  switch( TV.first ) {
  case OML_BLOB_VALUE :
    // val_ptr points to a check on memory of size omlblob_bytes
    omlc_set_blob ( *(TV.second) , (char *)val_ptr, omlblob_bytes);
    break;
    // add other cases here
  default :
    std::cerr << "OML - unrecognizeg type, value: " << TV.first << " , " << TV.second << std::endl;
    break;
  }

  return;
}


void CWriteOml::createMeasurementPoint(OmlMPDef* pOmlMPDef, std::string str, OmlValueT type)
{
  char* cptr;
  if (str == "NULL") {
    pOmlMPDef->name = NULL;
    pOmlMPDef->param_types = type;
  }
  else {
    cptr = new char[str.size()+1];
    strcpy (cptr, str.c_str());
    pOmlMPDef->name = cptr;
    pOmlMPDef->param_types = type;
  }
}

void CWriteOml::insert()
{
  omlc_inject (_mp_handle, _values);
}

void CWriteOml::stop()
{
  omlc_close();
  free(_values);
}
