#ifndef _GRAMMAR_HH
#define _GRAMMAR_HH

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

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

using namespace std;

//enum PROP { INIT=0, FIN=1 };
//typedef bitset<16> PropSet;

const PropSet EmptyPropSet = PropSet();

const FlagSet EmptyFlagSet = FlagSet();

//====================================================================================================
// class Link
//====================================================================================================

struct Link
{
  Link(Role r, Flag dfplus="NULL", Flag dfminus="NULL") : role(r), dflagplus(dfplus), dflagminus(dfminus) { }
  Link(Role r, PropSet ps=EmptyPropSet, Flag hfp="NULL", Flag hfm="NULL", Flag dfp="NULL", Flag dfm="NULL")
    : role(r), props(ps), hflagplus(hfp), hflagminus(hfm), dflagplus(dfp), dflagminus(dfm) { }
  //Link(Role r) : role(r), dflagplus("NULL") { }

  Role role;
  Flag hflagplus;
  Flag hflagminus;
  Flag dflagplus;
  Flag dflagminus;
  PropSet props;

  bool operator<(const Link& l) const 
  {
    if(role < l.role) return true;
    if(hflagplus < l.hflagplus) return true;
    if(hflagminus < l.hflagminus) return true;
    if(dflagplus < l.dflagplus) return true;
    if(dflagminus < l.dflagminus) return true;
    if(props.to_ulong() < l.props.to_ulong()) return true;
    return false;
  }

};

typedef set<Link> Links;

//====================================================================================================
// class Grammar
//====================================================================================================

class Grammar
{

 public:

  static const int RESIZE_DELTA=16;

  Grammar() {} ;

  Roles&  connectable(Cat h, Cat d);
  Roles   connectable(Cat h, Cat d, FlagSet f, FlagSet df);

  list<const Link*> connectable2(Cat h, Cat d, FlagSet hfs, FlagSet dfs);

  bool    check_constr(NodeProp& hprop, NodeProp& dprop, int dir, Role role);
  bool    check_constr2(NodeProp& hprop, NodeProp& dprop, int dir, const Link& link);

  bool    check_longrel(Cat hcat, Cat dcat, LongRel rel);
  bool    is_sgl(Role r);
  RoleSet is_obl(Cat c);
 
  RoleSet& constr_include(Role r) { return include[r]; };
  RoleSet& constr_exclude(Role r) { return exclude[r]; };
  
  FlagSet initial_flags(Cat c) { return set[c]; }
  FlagSet pass_flags(Role r)   { return pass[r]; }

  list<Boubble*>    trigger_boubbles(Cat c, Role r, Dir d);

  bool    read(FILE* f);
  void    write(ostream& os);
  void    write(FILE* f);

private:

  RoleSet                      sgl;
  vector< RoleSet >            obl;      //[Cat]
  RoleSet                      left;
  RoleSet                      right;
  RoleSet                      init;
  RoleSet                      fin;
  FlagSet                      initf;
  FlagSet                      finf;

  vector< RoleSet >            lt;       //[Role]
  vector< RoleSet >            gt;       //[Role]

  vector< FlagSet >            set;      //[Cat]
  //  vector< FlagSet >            rset;     //[Role]
  vector< FlagSet >            pass;     //[Role]

  vector< vector< Roles > >    connect;  //[Cat][Cat]

  vector< vector< Links > >    connect1; //[Cat][Cat]

  vector< RoleSet >            include;  //[Role]
  vector< RoleSet >            exclude;  //[Role]

  vector< vector< LongRels > > longrel;  //[Cat][Cat]

  list< Boubble* >             boubbles;
  
  vector< vector< list<Boubble*> > >   uptrigger;//[Cat][Role]
  vector< vector< list<Boubble*> > >   dntrigger;//[Cat][Role]

  void add_category(const char* s);
  void add_type(const char* s);
  void add_flag(const char* s)                   { Flag::add(s); }
  void add_long(const char* l, const char* p)    { LongRel::add(l); boubbles.push_back( new Boubble(p,l) );
                                                                    boubbles.push_back( (new Boubble(p,l))->reversed() ); }
  void add_triggers(Cat h, Cat d, LongRel l);

  void set_sgl(Role r)                     { sgl.set(r); }
  void set_obl(Cat c, Role r)              { obl[c].set(r); }
  void set_left(Role r)                    { left.set(r); }
  void set_right(Role r)                   { right.set(r); }
  void set_init(Role r)                    { init.set(r); }
  void set_fin(Role r)                     { fin.set(r); }
  void set_initf(Flag f)                   { initf.set(f); }
  void set_finf(Flag f)                    { finf.set(f); }
  void set_order(Role r, Role s)           { lt[s].set(r); }

  //  void set_connect(Cat c, Cat d, Role r)   { connect[c][d].insert(r); }
  //  void set_connect(Cat c, Cat d, Flag f, Role r)   { connect1[c][d].insert(Link(r,f)); }

  void set_connect(Cat h, Flag hfp, Flag hfm, Cat d, Flag dfp, Flag dfm, Role r, PropSet ps )   { connect1[h][d].insert(Link(r,ps,hfp,hfm,dfp,dfm)); }

  void set_include(Role r, Role s)         { include[r].set(s); }
  void set_exclude(Role r, Role s)         { exclude[r].set(s); }
  void set_longrel(Cat c, Cat d, LongRel l){ longrel[c][d].insert(l); }
  void set_set(Cat c, Flag f)              { set[c].set(f); }
  void set_pass(Role r, Flag f)            { pass[r].set(f); }
  void set_lt(Role r, Role s);
  void compute_gt();
  void compute_triggers();
  bool contains_boubble(const list<Boubble*> boubble_list, Boubble* bp) const;

};

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

inline
Roles&  Grammar::connectable(Cat h, Cat d)
{
  return connect[h][d];
}

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

inline
Roles Grammar::connectable(Cat h, Cat d, FlagSet hfs, FlagSet dfs)  // ZBYT WOLNE!!!!!!!!!!!!!!!!!!!!!!!!!! (-> Roles&)
{
  Roles ret;
  for(Links::const_iterator l = connect1[h][d].begin(); l != connect1[h][d].end(); l++)
    if( (l->hflagplus==0 || hfs[l->hflagplus]) && (l->hflagminus==0 || !hfs[l->hflagminus]) )
      if( (l->dflagplus==0 || dfs[l->dflagplus]) && (l->dflagminus==0 || !dfs[l->dflagminus]) )
	ret.insert(l->role);
  return ret;
}

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

inline
list<const Link*> Grammar::connectable2(Cat h, Cat d, FlagSet hfs, FlagSet dfs)  // ZBYT WOLNE!!!!!!!!!!!!!!!!!!!!!!!!!! (-> Roles&)
{
  list<const Link*> ret;
  for(Links::const_iterator l = connect1[h][d].begin(); l != connect1[h][d].end(); l++)
    if( (l->hflagplus==0 || hfs[l->hflagplus]) && (l->hflagminus==0 || !hfs[l->hflagminus]) )
      if( (l->dflagplus==0 || dfs[l->dflagplus]) && (l->dflagminus==0 || !dfs[l->dflagminus]) )
	ret.push_back(&(*l));
  return ret;
}

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

// inline
// bool Grammar::check_constr(NodeProp& hprop, NodeProp& dprop, int dir, Role role)    // dir: 0-left 1-right
// {
//   return 
//     !hprop.forbidden[role] &&
//     ( dir==1 || !right[role] ) &&
//     ( dir==0 || !left[role]  ) &&
//     ( dir==1 || (hprop.attached&init).none() ) &&
//     ( dir==0 || (hprop.attached&fin).none() )
//     ;
// }

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

inline
bool Grammar::check_constr2(NodeProp& hprop, NodeProp& dprop, int dir, const Link& link)    // dir: 0-left 1-right
{
  return 
    !hprop.forbidden[link.role] &&
    ( dir==1 || (!right[link.role] && !link.props[Prop("RIGHT")]) ) &&  // ZREZYGNOWAĆ Z TABLICY right[<role>]
    ( dir==0 || (!left[link.role] && !link.props[Prop("LEFT")]) ) &&
    ( dir!=0 || !hprop.init_attached ) &&
    ( dir!=1 || !hprop.fin_attached )
    ;
}

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

inline
bool Grammar::check_longrel(Cat hcat, Cat dcat, LongRel rel)
{
  return longrel[hcat][dcat].find(rel) != longrel[hcat][dcat].end();
}

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

inline bool Grammar::is_sgl(Role r)
{
  return sgl[r];
}

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

inline RoleSet Grammar::is_obl(Cat c)
{
  return obl[c];
}

//====================================================================================================

#endif
