// condition.cxx - Declarations and inline methods for property conditions.
//
// Written by David Megginson, started 2000.
// CLO May 2003 - Split out condition specific code.
//
// This file is in the Public Domain, and comes with no warranty.
//
// $Id: condition.cxx,v 1.9 2008/07/28 07:52:14 ehofman Exp $

#ifdef HAVE_CONFIG_H
#  include <simgear/compiler.h>
#endif

// #include <iostream>

#include <simgear/structure/exception.hxx>

#include "props.hxx"
#include "condition.hxx"

using std::istream;
using std::ostream;




////////////////////////////////////////////////////////////////////////
// Implementation of SGCondition.
////////////////////////////////////////////////////////////////////////

SGCondition::SGCondition ()
{
}

SGCondition::~SGCondition ()
{
}



////////////////////////////////////////////////////////////////////////
// Implementation of SGPropertyCondition.
////////////////////////////////////////////////////////////////////////

SGPropertyCondition::SGPropertyCondition ( SGPropertyNode *prop_root,
                                           const char *propname )
    : _node( prop_root->getNode(propname, true) )
{
}

SGPropertyCondition::~SGPropertyCondition ()
{
}



////////////////////////////////////////////////////////////////////////
// Implementation of SGNotCondition.
////////////////////////////////////////////////////////////////////////

SGNotCondition::SGNotCondition (SGCondition * condition)
  : _condition(condition)
{
}

SGNotCondition::~SGNotCondition ()
{
}

bool
SGNotCondition::test () const
{
  return !(_condition->test());
}



////////////////////////////////////////////////////////////////////////
// Implementation of SGAndCondition.
////////////////////////////////////////////////////////////////////////

SGAndCondition::SGAndCondition ()
{
}

SGAndCondition::~SGAndCondition ()
{
}

bool
SGAndCondition::test () const
{
  int nConditions = _conditions.size();
  for (int i = 0; i < nConditions; i++) {
    if (!_conditions[i]->test())
      return false;
  }
  return true;
}

void
SGAndCondition::addCondition (SGCondition * condition)
{
  _conditions.push_back(condition);
}



////////////////////////////////////////////////////////////////////////
// Implementation of SGOrCondition.
////////////////////////////////////////////////////////////////////////

SGOrCondition::SGOrCondition ()
{
}

SGOrCondition::~SGOrCondition ()
{
}

bool
SGOrCondition::test () const
{
  int nConditions = _conditions.size();
  for (int i = 0; i < nConditions; i++) {
    if (_conditions[i]->test())
      return true;
  }
  return false;
}

void
SGOrCondition::addCondition (SGCondition * condition)
{
  _conditions.push_back(condition);
}



////////////////////////////////////////////////////////////////////////
// Implementation of SGComparisonCondition.
////////////////////////////////////////////////////////////////////////

static int
doComparison (const SGPropertyNode * left, const SGPropertyNode *right)
{
  switch (left->getType()) {
  case SGPropertyNode::BOOL: {
    bool v1 = left->getBoolValue();
    bool v2 = right->getBoolValue();
    if (v1 < v2)
      return SGComparisonCondition::LESS_THAN;
    else if (v1 > v2)
      return SGComparisonCondition::GREATER_THAN;
    else
      return SGComparisonCondition::EQUALS;
    break;
  }
  case SGPropertyNode::INT: {
    int v1 = left->getIntValue();
    int v2 = right->getIntValue();
    if (v1 < v2)
      return SGComparisonCondition::LESS_THAN;
    else if (v1 > v2)
      return SGComparisonCondition::GREATER_THAN;
    else
      return SGComparisonCondition::EQUALS;
    break;
  }
  case SGPropertyNode::LONG: {
    long v1 = left->getLongValue();
    long v2 = right->getLongValue();
    if (v1 < v2)
      return SGComparisonCondition::LESS_THAN;
    else if (v1 > v2)
      return SGComparisonCondition::GREATER_THAN;
    else
      return SGComparisonCondition::EQUALS;
    break;
  }
  case SGPropertyNode::FLOAT: {
    float v1 = left->getFloatValue();
    float v2 = right->getFloatValue();
    if (v1 < v2)
      return SGComparisonCondition::LESS_THAN;
    else if (v1 > v2)
      return SGComparisonCondition::GREATER_THAN;
    else
      return SGComparisonCondition::EQUALS;
    break;
  }
  case SGPropertyNode::DOUBLE: {
    double v1 = left->getDoubleValue();
    double v2 = right->getDoubleValue();
    if (v1 < v2)
      return SGComparisonCondition::LESS_THAN;
    else if (v1 > v2)
      return SGComparisonCondition::GREATER_THAN;
    else
      return SGComparisonCondition::EQUALS;
    break;
  }
  case SGPropertyNode::STRING: 
  case SGPropertyNode::NONE:
  case SGPropertyNode::UNSPECIFIED: {
    string v1 = left->getStringValue();
    string v2 = right->getStringValue();
    if (v1 < v2)
      return SGComparisonCondition::LESS_THAN;
    else if (v1 > v2)
      return SGComparisonCondition::GREATER_THAN;
    else
      return SGComparisonCondition::EQUALS;
    break;
  }
  }
  throw sg_exception("condition: unrecognized node type in comparison");
  return 0;
}


SGComparisonCondition::SGComparisonCondition (Type type, bool reverse)
  : _type(type),
    _reverse(reverse),
    _left_property(0),
    _right_property(0),
    _right_value(0)
{
}

SGComparisonCondition::~SGComparisonCondition ()
{
}

bool
SGComparisonCondition::test () const
{
				// Always fail if incompletely specified
  if (_left_property == 0 ||
      (_right_property == 0 && _right_value == 0))
    return false;

				// Get LESS_THAN, EQUALS, or GREATER_THAN
  int cmp =
    doComparison(_left_property,
		 (_right_property != 0 ? _right_property : _right_value));
  if (!_reverse)
    return (cmp == _type);
  else
    return (cmp != _type);
}

void
SGComparisonCondition::setLeftProperty( SGPropertyNode *prop_root,
                                        const char * propname )
{
  _left_property = prop_root->getNode(propname, true);
}

void
SGComparisonCondition::setRightProperty( SGPropertyNode *prop_root,
                                         const char * propname )
{
  _right_value = 0;
  _right_property = prop_root->getNode(propname, true);
}

void
SGComparisonCondition::setRightValue (const SGPropertyNode *node)
{
  _right_property = 0;
  _right_value = new SGPropertyNode(*node);
}



////////////////////////////////////////////////////////////////////////
// Read a condition and use it if necessary.
////////////////////////////////////////////////////////////////////////

                                // Forward declaration
static SGCondition * readCondition( SGPropertyNode *prop_root,
                                    const SGPropertyNode *node );

static SGCondition *
readPropertyCondition( SGPropertyNode *prop_root,
                       const SGPropertyNode *node )
{
  return new SGPropertyCondition( prop_root, node->getStringValue() );
}

static SGCondition *
readNotCondition( SGPropertyNode *prop_root, const SGPropertyNode *node )
{
  int nChildren = node->nChildren();
  for (int i = 0; i < nChildren; i++) {
    const SGPropertyNode * child = node->getChild(i);
    SGCondition * condition = readCondition(prop_root, child);
    if (condition != 0)
      return new SGNotCondition(condition);
  }
  SG_LOG(SG_COCKPIT, SG_ALERT, "empty 'not' condition");
  return 0;
}

static SGCondition *
readAndConditions( SGPropertyNode *prop_root, const SGPropertyNode *node )
{
  SGAndCondition * andCondition = new SGAndCondition;
  int nChildren = node->nChildren();
  for (int i = 0; i < nChildren; i++) {
    const SGPropertyNode * child = node->getChild(i);
    SGCondition * condition = readCondition(prop_root, child);
    if (condition != 0)
      andCondition->addCondition(condition);
  }
  return andCondition;
}

static SGCondition *
readOrConditions( SGPropertyNode *prop_root, const SGPropertyNode *node )
{
  SGOrCondition * orCondition = new SGOrCondition;
  int nChildren = node->nChildren();
  for (int i = 0; i < nChildren; i++) {
    const SGPropertyNode * child = node->getChild(i);
    SGCondition * condition = readCondition(prop_root, child);
    if (condition != 0)
      orCondition->addCondition(condition);
  }
  return orCondition;
}

static SGCondition *
readComparison( SGPropertyNode *prop_root,
                const SGPropertyNode *node,
                SGComparisonCondition::Type type,
		bool reverse)
{
  SGComparisonCondition * condition = new SGComparisonCondition(type, reverse);
  condition->setLeftProperty(prop_root, node->getStringValue("property[0]"));
  if (node->hasValue("property[1]"))
    condition->setRightProperty(prop_root, node->getStringValue("property[1]"));
  else if (node->hasValue("value"))
    condition->setRightValue(node->getChild("value", 0));
  else
    throw sg_exception("condition: comparison without property[1] or value");

  return condition;
}

static SGCondition *
readCondition( SGPropertyNode *prop_root, const SGPropertyNode *node )
{
  const string &name = node->getName();
  if (name == "property")
    return readPropertyCondition(prop_root, node);
  else if (name == "not")
    return readNotCondition(prop_root, node);
  else if (name == "and")
    return readAndConditions(prop_root, node);
  else if (name == "or")
    return readOrConditions(prop_root, node);
  else if (name == "less-than")
    return readComparison(prop_root, node, SGComparisonCondition::LESS_THAN,
                          false);
  else if (name == "less-than-equals")
    return readComparison(prop_root, node, SGComparisonCondition::GREATER_THAN,
                          true);
  else if (name == "greater-than")
    return readComparison(prop_root, node, SGComparisonCondition::GREATER_THAN,
                          false);
  else if (name == "greater-than-equals")
    return readComparison(prop_root, node, SGComparisonCondition::LESS_THAN,
                          true);
  else if (name == "equals")
    return readComparison(prop_root, node, SGComparisonCondition::EQUALS,
                          false);
  else if (name == "not-equals")
    return readComparison(prop_root, node, SGComparisonCondition::EQUALS, true);
  else
    return 0;
}



////////////////////////////////////////////////////////////////////////
// Implementation of SGConditional.
////////////////////////////////////////////////////////////////////////

SGConditional::SGConditional ()
  : _condition (0)
{
}

SGConditional::~SGConditional ()
{
}

void
SGConditional::setCondition (SGCondition * condition)
{
  _condition = condition;
}

bool
SGConditional::test () const
{
  return ((_condition == 0) || _condition->test());
}



// The top-level is always an implicit 'and' group
SGCondition *
sgReadCondition( SGPropertyNode *prop_root, const SGPropertyNode *node )
{
  return readAndConditions(prop_root, node);
}


// end of condition.cxx
