#include <config.h>
#include <graph/LogicalNode.h>
#include <functions/Function.h>
#include <graph/NodeError.h>
#include <graph/NodeNameTab.h>

#include <stdexcept>
#include <vector>
#include <string>
#include <math.h>

using std::vector;
using std::string;
using std::set;
using std::logic_error;

static Index mkDim(Function const *func, vector<Node*> const &parents)
{
  /* 
     Calculates dimension of logical node as a function of its
     parameters.
  */

  vector<SArray const *> parameters(parents.size());
  long j = 0;
  for (vector<Node*>::const_iterator p = parents.begin(); p != parents.end(); 
       ++p)
    {
      parameters[j++] = &((*p)->data);
    }
  if (!func->checkParameterLength(parameters)) {
    throw logic_error(string("Incorrect number of parameters for function ")
		       + func->name());
  }
  if (!func->checkParameterDim(parameters)) {
    throw logic_error(string("Non-conforming parameters for function ")
		       + func->name());
  }
  return func->dim(parameters);
}
  
LogicalNode::LogicalNode(Function const *function, 
			 vector<Node*> const &parameters)
  : DeterministicNode(mkDim(function,parameters)), _func(function)
{
  for (unsigned long j = 0; j < parameters.size(); ++j) {
    _parameters.push_back(&parameters[j]->data);
    addParent(parameters[j]);
  }

  if (_func->isDiscreteValued(_parameters)) {
    data.setDiscreteValued(true);
  }
}

LogicalNode::~LogicalNode()
{
}

string LogicalNode::name(NodeNameTab const &name_table) const
{
  string name = name_table.getName(this);
  if (!name.empty())
    return name;

  /*
   * We have to do a little work here. We can't recover the
   * parameter names from the vector "_parameters", because it is
   * a vector of SArrays. Conversely, the set of parents is not
   * stored in the right order.  By putting the two pieces of
   * data together, we can reconstruct the original vector of 
   * parameters.
  */
  vector<Node *> parameters;
  vector<SArray const *>::const_iterator p;
  for (p = _parameters.begin(); p != _parameters.end(); ++p) {
    set<Node*>::const_iterator q;
    for (q = parents().begin(); q != parents().end(); ++q) {
      Node *parent = (*q);
      if (*p == &parent->data) {
	parameters.push_back(parent);
	break;
      }
    }
  }

  string const &fname = _func->name();
  if (fname == "*" || fname == "/" || fname == "+" || fname == "-")
    {
      name.append("(");
      for (unsigned int i = 0; i < parameters.size(); ++i) {
	if (i > 0) {
	  name.append(fname);
	}
	name.append(parameters[i]->name(name_table));
      }
      name.append(")");
    }
  else if (fname == "NEG") 
    {
      name.append("(-");
      name.append(parameters[0]->name(name_table));
      name.append(")");
    }
  else
    {
      name.append(fname);
      name.append("(");
      for (unsigned int i = 0; i < parameters.size(); ++i) {
	if (i > 0)
	  name.append(",");
	name.append(parameters[i]->name(name_table));
      }
      name.append(")");
    }

  return name;
}

vector<SArray const *> const &LogicalNode::parameters() const
{
  return _parameters;
}

Function const* LogicalNode::function() const
{
  return _func;
}

void LogicalNode::forwardSample()
{
  if (!_func->checkParameterValue(_parameters)) {
    throw NodeError(this, "Invalid parameter values for LogicalNode");
  }
  _func->evaluate(data, _parameters);
}

LogicalNode *asLogical(Node *node)
{
  return dynamic_cast<LogicalNode*>(node);
}

bool isLogical(Node const *node)
{
  return dynamic_cast<LogicalNode const*>(node);
}


syntax highlighted by Code2HTML, v. 0.9.1