#ifndef _SGRAPH_HH
#define _SGRAPH_HH

#include <stdio.h>

#include <list>
#include <vector>
#include <bitset>

#include "const.hh"
#include "mgraph.hh"
#include "thesymbols.hh"
#include "boubble.hh"


using namespace std;

//====================================================================================================
// CLASS Arc
//====================================================================================================
struct Arc
{
  int dst;
  Role role;
  int headanc;
  int depanc;
 
  Arc(int d, Role r, int ha, int da) : dst(d), role(r), headanc(ha), depanc(da) {};
};

//====================================================================================================
// CLASS NodeProp
//====================================================================================================

struct NodeProp
{
  NodeProp();
  NodeProp(const NodeProp& p);
  ~NodeProp();

  bool operator==(const NodeProp& p);
  NodeProp& operator=(const NodeProp& p);

  void clear_boubbles();
  void merge_boubbles(list<Boubble*> new_boubbles);

  void copy(const NodeProp& p);
  void clear();

  RoleSet required;
  RoleSet forbidden;
  RoleSet attached;

  bool init_attached;
  bool fin_attached;
  
  FlagSet flags;

  list<Boubble*>   boubbles;
};

//----------------------------------------------------------------------------------------------------

inline
bool NodeProp::operator==(const NodeProp& p)
{
  if(required != p.required) return false;
  if(forbidden != p.forbidden) return false;
  if(attached != p.attached) return false;
  if(flags != p.flags) return false;
  if(init_attached != p.init_attached) return false;
  if(fin_attached != p.fin_attached) return false;
  
  list<Boubble*>::const_iterator b1 = p.boubbles.begin();
  for(list<Boubble*>::const_iterator b = boubbles.begin(); b != boubbles.end(); b++)
    {
      if(b1 == p.boubbles.end())
	return false;
      if(!(**b == **b1))
	return false;
    }
  if(b1 != p.boubbles.end())
    return false;
  
  return true;
}

//----------------------------------------------------------------------------------------------------

inline
void NodeProp::clear_boubbles()
{
  for(list<Boubble*>::iterator b = boubbles.begin(); b!=boubbles.end(); b++)
    delete *b;
  boubbles.clear();
}

//----------------------------------------------------------------------------------------------------

inline
void NodeProp::merge_boubbles(list<Boubble*> new_boubbles)
{
  boubbles.merge(new_boubbles);
}

//----------------------------------------------------------------------------------------------------

inline
void NodeProp::copy(const NodeProp& p)
{
  required=p.required;
  forbidden=p.forbidden;
  attached=p.attached;
  flags=p.flags;
  init_attached=p.init_attached;
  fin_attached=p.fin_attached;
  for(list<Boubble*>::const_iterator b = p.boubbles.begin(); b!=p.boubbles.end(); b++)
    boubbles.push_back(new Boubble(**b));
}

//----------------------------------------------------------------------------------------------------

inline
NodeProp::~NodeProp()
{
  clear_boubbles();
}
//----------------------------------------------------------------------------------------------------

inline
NodeProp::NodeProp()
{
  clear();
}

//----------------------------------------------------------------------------------------------------

inline
NodeProp::NodeProp(const NodeProp& p)
{
  copy(p);
}

//----------------------------------------------------------------------------------------------------

inline
NodeProp& NodeProp::operator=(const NodeProp& p)
{
  clear();
  copy(p);
  return *this;
}

//----------------------------------------------------------------------------------------------------

inline
void NodeProp::clear()
{
  required.reset();
  forbidden.reset();
  attached.reset();
  init_attached=false;
  fin_attached=false;
  clear_boubbles();
}

//====================================================================================================
// CLASS SNode
//====================================================================================================

struct SNode
{
  
  int mnode;

  NodeProp prop;

  bitset<MAXNODES> LV;
  bitset<MAXNODES> LH;
  bitset<MAXNODES> LD;
  bool in_LH;

  vector<Arc> heads;
  vector<Arc> deps;

  void clear();
  bool saturated();
};

//----------------------------------------------------------------------------------------------------
inline
void SNode::clear()
{ prop.clear(), LV.reset(), LD.reset(), LH.reset(), heads.clear(), deps.clear(); }
//----------------------------------------------------------------------------------------------------
inline
bool SNode::saturated()
{ return prop.required.none(); }

//====================================================================================================
// SGraph CLASS
//====================================================================================================

class SGraph
{
public:

  enum Output { HEADS=1, DEPS=2, SETS=4, CONSTRAINTS=8, BOUBBLES=16 };

  SGraph(MGraph& mg) : mgraph(mg)               { clear(); }

  SNode& operator[](const int i)                { return nodes[i]; }

  void clear()                                  { nodes.clear(); }
  int  add_base_snode(int mnodeind);
  int  clone(int ancind, NodeProp newprop);
  void update_left(int headind, int depind);
  void update_right(int headind, int depind);
  bool visible(int left, int right);
  bool saturated(int node);

  Cat  cat(int i) const { return mgraph[nodes[i].mnode].cat; }    
  char* form(int i) const { return mgraph[nodes[i].mnode].form; }

  int print_node(FILE* f, int n, unsigned int info);
  int print_node_debug(FILE* f, const char* pref, int n, int anc);

  void print_arc(FILE* f, int left, int right, Role role, int dir); // 0 - left, 1 - right

  //private:

  int size()              {return nodes.size(); }

private:

  MGraph& mgraph;
  
  vector<SNode> nodes;

  int lastnodeind()       { return nodes.size()-1; }
  SNode& makenewnode()    { nodes.push_back(SNode()); nodes.back().clear(); return nodes.back(); }

  int sprint_node(char* buf, int n, int anc, unsigned int info);
  int sprint_node_debug(char* buf, const char* pref, int n, int anc);
};

//----------------------------------------------------------------------------------------------------

inline bool SGraph::visible(int left, int right)
{
  return nodes[right].LV[left];
}

//----------------------------------------------------------------------------------------------------

inline bool SGraph::saturated(int node)
{
  return nodes[node].saturated();
}

//----------------------------------------------------------------------------------------------------

#endif
