#include <config.h>
#include <model/BUGSModel.h>
#include <model/TraceMonitor.h>
#include <model/NodeArray.h>
#include <graph/DevianceNode.h>
#include <graph/StochasticNode.h>
#include <graph/Node.h>

#include <list>
#include <utility>
#include <stdexcept>
#include <fstream>

#include <Rmath.h>

using std::vector;
using std::ofstream;
using std::list;
using std::pair;
using std::string;
using std::runtime_error;

BUGSModel::BUGSModel()
  : Model()
{
}

SymTab &BUGSModel::symtab() 
{
  return _symtab;
}

TraceMonitor const *BUGSModel::setMonitor(string const &name, Range const &range, unsigned int thin)
{
  NodeArray *array = _symtab.getVariable(name);
  if (!array) {
     throw runtime_error(string("Unknown variable ") + name);
  }
  Node *node = array->getSubset(range);
  if (node == 0) {
    throw runtime_error(string("Cannot monitor undefined node ") +
			array->name() + print(range));
  }
  if (!graph().contains(node)) {
    addExtraNode(node); 
  }
  TraceMonitor const *monitor = Model::setMonitor(node, thin);
  BUGSMonitor bugs_monitor;
  bugs_monitor.array = array;
  bugs_monitor.range = new Range(range);
  bugs_monitor.monitor = monitor;
  _bugs_monitors.push_back(bugs_monitor);
  return monitor;
}

void BUGSModel::clearMonitor(string const &name, Range const &range)
{
    NodeArray *array = _symtab.getVariable(name);
    if (!array) {
	throw runtime_error(string("Unknown variable ") + print(range));
    }
    if (array->range().contains(range)) {
	Node const *node = array->getSubset(range);
	if(node) {
	    vector<BUGSMonitor>::iterator p = _bugs_monitors.begin();
	    for (; p != _bugs_monitors.end(); ++p) {
		if (p->monitor->node() == node) {
		    delete p->range;
		    _bugs_monitors.erase(p);
		    break;
		}
	    }
	    Model::clearMonitor(node);	  
	}
    }
    else {
	throw runtime_error(string("Requested invalid subset ") + name
			    + print(range));
    }
}

static void writeDouble(double x, std::ostream &out)
{
  if (x == JAGS_NA) {
    out << "NA";
  }
  /* FIXME
     This shouldn't happen. On the other hand, I'm only commenting this
     out to avoid problems on MacOs X
  
  else if (ISNAN(x)) {
    out << "NaN";
  }
  */
  else if (!R_FINITE(x)) {
    if (x > 0) {
      out << "Inf";
    }
    else {
      out << "-Inf";
    }
  }
  else {
    out << x;
  }
}

void BUGSModel::coda(vector<Node const*> const &nodes, ofstream &output, 
		     ofstream &index)
{
  /* 
     Dump monitor for given node in CODA format.

     CODA requires two files, the output file (output) and the index
     file (index)
  */
  long lineno = 0;
  for (unsigned int i = 0; i < nodes.size(); i++) {
    for (vector<BUGSMonitor>::const_iterator j = _bugs_monitors.begin();
	 j != _bugs_monitors.end(); ++j) 
      {
	Node const *node = nodes[i];
	if (j->monitor->node() == node) {
	  TraceMonitor const *monitor = j->monitor;
	  int nvar = node->data.length();
	  if (nvar != 1) {
	    /* Multivariate node */
	    for (long offset = 0; offset < nvar; ++offset) {
	      index << j->array->name() << print(j->range->leftIndex(offset))
		    << " "  
		    << lineno + 1 << "  "  << lineno + monitor->size() << '\n';
	      long iter = monitor->start();
	      for (int k = 0; k < monitor->size(); k++) {
		output << iter << "  ";
		writeDouble(monitor->values()[k * nvar + offset], output);
		output << '\n';
		iter += monitor->thin();
	      }
	      lineno += monitor->size();
	    }
	  }
	  else {
	    /* Univariate node */
	    index << _symtab.getName(node) << "  " << lineno + 1 << "  " 
		  << lineno + monitor->size() << '\n';
	    long iter = monitor->start();
	    for (int k = 0; k < monitor->size(); k++) {
	      output << iter << "  ";
	      writeDouble(monitor->values()[k], output);
	      output << '\n';
	      iter += monitor->thin();
	    }
	    lineno += monitor->size();
	  }
	}
      }
  }
}

void BUGSModel::addDevianceNode()
{
  NodeArray const *array = _symtab.getVariable("deviance");
  if (array)
    return; //Deviance already defined by user

  _symtab.addVariable("deviance", Index(1));
  NodeArray *deviance = _symtab.getVariable("deviance");
  vector<Node*> nodes;
  graph().getNodes(nodes);
  std::set<StochasticNode*> parameters;
  for (vector<Node*>::const_iterator p = nodes.begin(); p != nodes.end(); ++p)
    {
      if (isObserved(*p)) {
        StochasticNode *snode = dynamic_cast<StochasticNode*>(*p);
        if (snode)
          parameters.insert(snode);
      }
    }
  DevianceNode *dnode = new DevianceNode(parameters);
  deviance->insert(dnode, Index(1));
  addExtraNode(dnode);
}


syntax highlighted by Code2HTML, v. 0.9.1