//============================================================================
//
// This file is part of the Shapes Generative Model Library (SGML) project.
//
// Copyright (c) 2010-2014 - Evangelos Kalogerakis, Siddhartha Chaudhuri (authors of the code) / Stanford University, UMass-Amherst
//
// SGML is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// SGML is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with SGML.  If not, see <http://www.gnu.org/licenses/>.
//
//============================================================================


#ifndef __SNL_Node_hpp__
#define __SNL_Node_hpp__

#include "Common.hpp"
#include <sstream>
#include <string>
#include <vector>

namespace SNL {

/** %Node of a probabilistic graphical model, representing a random variable in the model. */
class SNL_API Node
{
  private:
    typedef std::vector<Node *> NodeArray;

  public:
    typedef NodeArray::const_iterator NodeConstIterator;  ///< Iterator over nodes.
    typedef NodeArray::iterator NodeIterator;  ///< Iterator over nodes.

    /** Destructor. */
    virtual ~Node()
    {
      clearParents();
      clearChildren();
    }

    Node(int _type, long _id): type(_type), id(_id) { }
    
    /** Does the node represent a variable that takes discrete values? */
    virtual bool isDiscrete() const = 0;

    /** Does the node represent a variable that takes continuous values? */
    bool isContinuous() const { return !isDiscrete(); }

    /** Get the type of the node. */
    virtual int getType() const = 0;

    /** Get the ID of the node. */
    virtual long getID() const = 0;
    
    /** Get the string description of the node. */
    virtual std::string getName() const = 0;    

    /** Get the number of dimensions of the values taken by this variable. */
    virtual size_t dim() const = 0;

    /**
     * Get the probability assigned to this node for a particular value by the inference procedure. If the node represents a
     * discrete variable, the argument is rounded to the nearest integer.
     */
    //virtual double prob(std::vector<double> const & value) const = 0;

    /** Get the number of parent nodes of this node. */
    long numParents() const { return (long)parents.size(); }

    /** Get an iterator to the beginning of the set of parents. */
    NodeConstIterator parentsBegin() const { return parents.begin(); }

    /** Get an iterator to the beginning of the set of parents. */
    NodeIterator parentsBegin() { return parents.begin(); }

    /** Get an iterator to the end of the set of parents. */
    NodeConstIterator parentsEnd() const { return parents.end(); }

    /** Get an iterator to the end of the set of parents. */
    NodeIterator parentsEnd() { return parents.end(); }

    /** Get the number of children nodes of this node. */
    long numChildren() const { return (long)children.size(); }

    /** Get an iterator to the beginning of the set of children. */
    NodeConstIterator childrenBegin() const { return children.begin(); }

    /** Get an iterator to the beginning of the set of children. */
    NodeIterator childrenBegin() { return children.begin(); }

    /** Get an iterator to the end of the set of children. */
    NodeConstIterator childrenEnd() const { return children.end(); }

    /** Get an iterator to the end of the set of children. */
    NodeIterator childrenEnd() { return children.end(); }

    /** Check if this node is a parent of another node. */
    bool isParentOf(Node const * node) const
    {
      for (size_t i = 0; i < children.size(); ++i)
        if (children[i] == node)
          return true;

      return false;
    }

    /** Check if this node is a child of another node. */
    bool isChildOf(Node const * node) const
    {
      for (size_t i = 0; i < parents.size(); ++i)
        if (parents[i] == node)
          return true;

      return false;
    }

    /** Add a parent node. */
    void addParent(Node * parent)
    {
      if (!isChildOf(parent))
      {
        parents.push_back(parent);
        parent->children.push_back(this);
      }
    }

    /** Remove a parent node. */
    void removeParent(Node * parent)
    {
      for (size_t i = 0; i < parents.size(); ++i)
        if (parents[i] == parent)
        {
          parents.erase(parents.begin() + i);
          parent->removeChildOneWay(this);
          return;
        }
    }

    /** Remove all parents of the node. */
    void clearParents()
    {
      for (size_t i = 0; i < parents.size(); ++i)
        parents[i]->removeChildOneWay(this);

      parents.clear();
    }

    /** Remove all children of the node. */
    void clearChildren()
    {
      for (size_t i = 0; i < children.size(); ++i)
        children[i]->removeParentOneWay(this);

      children.clear();
    }

    /** Add a child node. */
    void addChild(Node * child)
    {
      if (!isParentOf(child))
      {
        children.push_back(child);
        child->parents.push_back(this);
      }
    }

    /** Remove a child node, given a pointer to it. Assumes duplicates do not exist. */
    void removeChild(Node * child)
    {
      for (size_t i = 0; i < children.size(); ++i)
        if (children[i] == child)
        {
          children.erase(children.begin() + i);
          child->removeParentOneWay(this);
          return;
        }
    }
    
  public:
    int type;
    long id;
    NodeArray parents;   ///< The set of parents of the node.
    NodeArray children;  ///< The set of children of the node.

  private:
    void removeParentOneWay(Node * parent)
    {
      for (size_t i = 0; i < parents.size(); ++i)
        if (parents[i] == parent)
        {
          parents.erase(parents.begin() + i);
          return;
        }
    }

    void removeChildOneWay(Node * child)
    {
      for (size_t i = 0; i < children.size(); ++i)
        if (children[i] == child)
        {
          children.erase(children.begin() + i);
          return;
        }
    }

}; // class Node

} // namespace SNL

#endif
