
#include <cstdio>

#include "grammar.hh"

//bool (*constraint[MAXCONSTRS])(int head, int dep);

//====================================================================================================
//inline !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
bool chk_type(const char* s) { return Role::index(s)>0; } // PRZENIEŚĆ DO Role
//----------------------------------------------------------------------------------------------------
//inline !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
bool chk_long(const char* s) { return LongRel::index(s)>0; } // jw
//----------------------------------------------------------------------------------------------------
//inline !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
bool chk_cat(const char* s) { return Cat::index(s)>0; } //jw
//----------------------------------------------------------------------------------------------------
//inline !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
bool chk_flag(const char* s) { return Flag::index(s)>0; } //jw
//====================================================================================================

void grammar_error(int lineno, string s="Grammar error.")
{
  fprintf(stderr,"%8d: %s Line ignored.\n",lineno,s.c_str());
}

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

void Grammar::add_category(const char* s)
{
  Cat::add(s);

  if(connect.size() <= Cat::count())
  {
    connect.resize(Cat::count()+RESIZE_DELTA);
    for(int i=0; i<connect.size(); ++i)
      if(connect[i].size() <= Cat::count()) connect[i].resize(Cat::count()+RESIZE_DELTA);
  }
  if(connect1.size() <= Cat::count())
  {
    connect1.resize(Cat::count()+RESIZE_DELTA);
    for(int i=0; i<=connect1.size(); ++i)
      if(connect1[i].size() <= Cat::count()) connect1[i].resize(Cat::count()+RESIZE_DELTA);
  }

  if(longrel.size() <= Cat::count())
  {
    longrel.resize(Cat::count()+RESIZE_DELTA);
    for(int i=0; i<longrel.size(); ++i)
      if(longrel[i].size() <= Cat::count()) longrel[i].resize(Cat::count()+RESIZE_DELTA);
  }

  if(uptrigger.size() <= Cat::count())
    uptrigger.resize(Cat::count()+RESIZE_DELTA);
  if(dntrigger.size() <= Cat::count())
    dntrigger.resize(Cat::count()+RESIZE_DELTA);
  
  if(obl.size() <= Cat::count()) obl.resize(Cat::count()+RESIZE_DELTA);
  if(set.size() <= Cat::count()) set.resize(Cat::count()+RESIZE_DELTA);
}

void Grammar::add_type(const char* s)
{  
  Role::add(s);

  if(lt.size() <= Role::count()) lt.resize(Role::count()+RESIZE_DELTA);
  if(gt.size() <= Role::count()) gt.resize(Role::count()+RESIZE_DELTA);
  if(pass.size() <= Role::count()) pass.resize(Role::count()+RESIZE_DELTA);
  if(include.size() <= Role::count()) include.resize(Role::count()+RESIZE_DELTA);
  if(exclude.size() <= Role::count()) exclude.resize(Role::count()+RESIZE_DELTA);
  for(int i=0; i<uptrigger.size(); i++)
    if(uptrigger[i].size() <= Role::count()) uptrigger[i].resize(Role::count()+RESIZE_DELTA);
  for(int i=0; i<dntrigger.size(); i++)
    if(dntrigger[i].size() <= Role::count()) dntrigger[i].resize(Role::count()+RESIZE_DELTA);
}

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

bool Grammar::contains_boubble(const list<Boubble*> boubble_list, Boubble* bp) const
{
  for(list<Boubble*>::const_iterator bi = boubble_list.begin(); bi != boubble_list.end(); bi++ )
    if(**bi == *bp) return true;
  return false;
}

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

void Grammar::add_triggers(Cat src, Cat dest, LongRel l)
{
  for(list<Boubble*>::const_iterator b=boubbles.begin(); b!=boubbles.end(); b++)
    if((*b)->rel() == l)
      {
	list<Boubble*>& boubble_list = ((*b)->dir()==UP) ? uptrigger[src][(*b)->next()] : dntrigger[src][(*b)->next()] ;
	if(!contains_boubble(boubble_list,*b))
	  boubble_list.push_back(*b);
      }
}

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

void Grammar::set_lt(Role s, Role t)
{
  lt[s].set(t);
  gt[t].set(s);
  if(s==0||(int)t==0)
    return;
  else
  {
    for(int i=0; i<Role::count(); ++i)
      if(lt[i][s])
	set_lt(i,t);
    for(int i=0; i<Role::count(); ++i)
      if(lt[t][i])
	set_lt(s,i);
  }
}  


void Grammar::compute_gt()
{
  for(Role s=0; s<Role::count(); ++s)
    for(Role t=0; t<Role::count(); ++t)
      if(lt[s][t])
	gt[t].set(s);
}


void Grammar::compute_triggers()
{
  //init uptrigger array
  uptrigger.resize(Cat::count());
  for(int i=0; i<uptrigger.size(); i++)
    uptrigger[i].resize(Role::count());
  //init dntrigger array
  dntrigger.resize(Cat::count());
  for(int i=0; i<dntrigger.size(); i++)
    dntrigger[i].resize(Role::count());

  // for(int c=0; c<Cat::count(); c++)
  //   for(int r=0; r<Role::count(); r++)
  //     for(list<Boubble>::const_iterator b=boubbles.begin(); b!=boubbles.end; b++)
  // 	if(b->dir()==UP && )
}

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

list<Boubble*> Grammar::trigger_boubbles(Cat c, Role r, Dir d)
{
  list<Boubble*> boubble_list = (d == UP) ? uptrigger[c][r] : dntrigger[c][r];
  list<Boubble*> ret;
  for(list<Boubble*>::iterator b = boubble_list.begin(); b != boubble_list.end(); ++b)
    ret.push_back((*b)->step(r,d));
  return ret;  
}

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

Flag parse_flags(const char* s, const char* v)
{
  char buf[16][17];
  int n=sscanf(s,"%[A-Z]%[+-]%[A-Z]%[+-]%[A-Z]%[+-]%[A-Z]%[+-]%[A-Z]%[+-]%[A-Z]%[+-]%[A-Z]%[+-]%[A-Z]%[+-]",
	       buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],buf[8],buf[9],buf[10],buf[11],buf[12],buf[13],buf[14],buf[15],buf[16]);
  for(int i=2; i<=n; i+=2)
    if(strcmp(buf[i-1],v)==0)
      return Flag(buf[i-2]);
  return Flag("NULL");
}


PropSet parse_props(const char* s)
{
  PropSet ret;
  char buf[8][17];
  int n=sscanf(s,"&%[A-Z]&%[A-Z]&%[A-Z]&%[A-Z]&%[A-Z]&%[A-Z]&%[A-Z]&%[A-Z]",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7]);
  for(int i=1; i<=n; i++)
    ret.set(Prop(buf[i-1]));
  return ret; 
  
}


bool Grammar::read(FILE* f)
{

  //>>> TU?

  Prop::add("INIT");
  Prop::add("FIN");

  //<<< TU?


  int lineno=0;
  char line[MAXLINE]; // line has the structure: key [arg1 [arg2 [arg3]]]
  char key[MAXLINE];
  char arg1[MAXLINE];
  char arg2[MAXLINE];
  char arg3[MAXLINE];
  char arg4[MAXLINE];

  while(fgets(line,MAXLINE,f))
    {
      lineno++;
      int fields=sscanf(line,"%s %s %s %s %s",key,arg1,arg2,arg3,arg4);
      
       if(fields<1 || key[0]=='#') continue; // skip empty lines and comments

      if(fields>1 && arg1[0] == '#') fields=1;
      if(fields>2 && arg2[0] == '#') fields=2;
      if(fields>3 && arg3[0] == '#') fields=3;
      if(fields>4 && arg4[0] == '#') fields=4;
      
      if     (strcmp(key,"CAT")==0 && fields==2)
	{
	  add_category(arg1);
	}
      else if(strcmp(key,"ROLE")==0 && fields==2)
	{
	  add_type(arg1);
	}
      else if(strcmp(key,"SGL")==0 && fields==2)
	{  
	  if(chk_type(arg1))
	    set_sgl(arg1);
	  else
	    grammar_error(lineno);
	}
      else if(strcmp(key,"LEFT")==0 && fields==2)
	{ 
	  if(chk_type(arg1))
	    set_left(arg1);
	  else
	    grammar_error(lineno);
	}
      else if(strcmp(key,"RIGHT")==0 && fields==2)
	{
	  if(chk_type(arg1))
	    set_right(arg1);
	  else
	    grammar_error(lineno);
	}
      else if(strcmp(key,"INITR")==0 && fields==2)
	{
	  if(chk_type(arg1))
	    set_init(arg1);
	  else
	    grammar_error(lineno);
	}
      else if(strcmp(key,"FINR")==0 && fields==2)
	{
	  if(chk_type(arg1))
	    set_fin(arg1);
	  else
	    grammar_error(lineno);
	}
      else if(strcmp(key,"INITF")==0 && fields==2)
	{
	  if(chk_flag(arg1))
	    set_initf(arg1);
	  else
	    grammar_error(lineno);
	}
      else if(strcmp(key,"FINF")==0 && fields==2)
	{
	  if(chk_flag(arg1))
	    set_finf(arg1);
	  else
	    grammar_error(lineno);
	}
      else if(strcmp(key,"REQ")==0 && fields==3)
	{
	  if( chk_cat(arg1) && chk_type(arg2) )
	    set_obl(arg1,arg2);
	  else
	    grammar_error(lineno);
	}
      else if(strcmp(key,"CONSTRE")==0 && fields==3)
	{
	  if( chk_type(arg1) && chk_type(arg2) )
	    set_exclude(arg1,arg2);
	  else
	    grammar_error(lineno);
	}
      else if(strcmp(key,"CONSTRI")==0 && fields==3)
	{
	  if( chk_type(arg1) && chk_type(arg2) )
	    set_include(arg1,arg2);
	  else
	    grammar_error(lineno);
	}
      else if(strcmp(key,"LONG")==0 &&  fields ==3)
	{
	    add_long(arg1,arg2);
	}
      else if(strcmp(key,"LINK")==0 && fields==4)
	{ 
	  char cat1[MAXLINE],flags1[MAXLINE],cat2[MAXLINE],flags2[MAXLINE],type[MAXLINE],props[MAXLINE];

	  if(sscanf(arg1,"%[^;];%s",cat1,flags1)==1) *flags1='\0'; 
	  if(sscanf(arg2,"%[^;];%s",cat2,flags2)==1) *flags2='\0'; 
	  if(sscanf(arg3,"%[^&]%s",type,props)==1) *props='\0';
	  
	  //	  printf("line=%s\n\tcat1=%s flags1=%s cat2=%s flags2=%s type=%s props=%s\n",line,cat1,flags1,cat2,flags2,type,props);

	  if( chk_cat(cat1) && chk_cat(cat2) && chk_type(type) )
	    set_connect(cat1,parse_flags(flags1,"+"),parse_flags(flags1,"-"),cat2,parse_flags(flags2,"+"),parse_flags(flags2,"-"),type,parse_props(props));
	  else if( chk_cat(cat1) && chk_cat(cat2) && chk_long(type) )
	    {
	      set_longrel(cat1,cat2,type);
	      add_triggers(cat1,cat2,type);
	    }
	  else
	      grammar_error(lineno);
	}
      // else if(strcmp(key,"LINK")==0 && fields==5)
      // 	{ 
      // 	  if( chk_cat(arg1) && chk_cat(arg2) && chk_type(arg4) )
      // 	    set_connect(arg1,arg2,arg3,arg4);
      // 	  else
      // 	      grammar_error(lineno);
      // 	}
      // FLAG DECLARATION
      else if(strcmp(key,"FLAG")==0 && fields==2)
	{ 
	  add_flag(arg1);
	}
      else if(strcmp(key,"SET")==0 && fields==3)
	{
	  if( chk_cat(arg1) && chk_flag(arg2) )
	    set_set(arg1,arg2);
	  else
	    grammar_error(lineno);
	}
      else if(strcmp(key,"PASS")==0 && fields==3)
	{
	  if( chk_type(arg1) && chk_flag(arg2) )
	    set_pass(arg1,arg2);
	  else
	    grammar_error(lineno);
	}
      
      else fprintf(stderr,"Statement not recognized in line %d. Ignored.\n", lineno);
    }
  
  //   compute_gt();
  
  return true;
  
}


void Grammar::write(ostream& os)
{
  for(Cat i=1; i<Cat::count(); ++i)
    os << "CAT\t" << i.str() << endl;

  for(Role i=1; i<Role::count(); ++i)
    os << "ROLE\t" << i.str() << endl;

  for(Role i=1; i<Role::count(); ++i)
    if(sgl.test(i))
      os << "SGL\t" << i.str() << endl;
  
  for(Role i=1; i<Role::count(); ++i)
    if(left.test(i))
      os << "LEFT\t" << i.str() << endl;

  for(Role i=1; i<Role::count(); ++i)
    if(right.test(i))
      os << "RIGHT\t" << i.str() << endl;

  for(Cat c=1; c<Cat::count(); ++c)
    for(Role r=1; r<Role::count(); ++r)
      if(obl[c].test(r))
	os << "REQ\t" << c.str() << "\t" << r.str() << endl;
  
  for(Cat c=1; c<Cat::count(); ++c)
    for(Cat d=1; d<Cat::count(); ++d)
      for(Links::const_iterator l = connect1[c][d].begin(); l != connect1[c][d].end(); l++)
	{
	  os << "LINK\t" << c.str();
	  if(l->hflagplus||l->hflagminus)
	    {
	      os << ";";
	      if(l->hflagplus) os << (l->hflagplus).str() << "+";
	      if(l->hflagminus) os << (l->hflagminus).str() << "-"; 
	    }
	  os << "\t" << d.str();
	  if(l->dflagplus||l->dflagminus)
	    {
	      os << ";";
	      if(l->dflagplus) os << (l->dflagplus).str() << "+";
	      if(l->dflagminus) os << (l->dflagminus).str() << "-"; 
	    }
	  os << "\t" << (l->role).str();
	  for(Prop p=0; p<Prop::count(); ++p)
	    if(l->props[p])
	      os << "&" << p.str();
	  os << endl;
	}

  for(LongRel i=1; i<LongRel::count(); ++i)
    os << "LONG\t" << i.str() << endl;

  for(list<Boubble*>::const_iterator b = boubbles.begin(); b != boubbles.end(); b++)
    os << "BOUBBLE\t" << **b << endl;

  for(Cat c=1; c<Cat::count(); ++c)
    for(Cat d=1; d<Cat::count(); ++d)
      for(LongRel l=1; l<LongRel::count(); ++l)
	if(longrel[c][d].count(l))
          os << "LLINK\t" << c.str() << "\t" << d.str() << "\t" << l.str() << endl;

  for(Cat c=1; c<Cat::count(); ++c)
    for(Flag f=1; f<Flag::count(); ++f)
      if(set[c].test(f))
	os << "SET\t" << c.str() << "\t" << f.str() << endl;

  for(Role r=1; r<Role::count(); ++r)
    for(Flag f=1; f<Flag::count(); ++f)
      if(pass[r].test(f))
	os << "PASS\t" << r.str() << "\t" << f.str() << endl;
  
  for(Role r=1; r<Role::count(); ++r)
    for(Role t=1; t<Role::count(); ++t)
      if(include[r].test(t))
	os << "CONSTRI\t" << r.str() << "\t" << t.str() << endl;
  
  for(Role r=1; r<Role::count(); ++r)
    for(Role t=1; t<Role::count(); ++t)
      if(exclude[r].test(t))
	os << "CONSTRE\t" << r.str() << "\t" << t.str() << endl;
  
  for(Cat c=1; c<Cat::count(); ++c)
    for(Role r=1; r<Role::count(); ++r)
      for(list<Boubble*>::const_iterator b=uptrigger[c][r].begin(); b!=uptrigger[c][r].end(); b++)
	os << "TRIGGER-UP\t" << c.str() << "\t" << r.str() << "\t" << (*b)->rel().str() << endl;

  for(Cat c=1; c<Cat::count(); ++c)
    for(Role r=1; r<Role::count(); ++r)
      for(list<Boubble*>::const_iterator b=dntrigger[c][r].begin(); b!=dntrigger[c][r].end(); b++)
	os << "TRIGGER-DN\t" << c.str() << "\t" << r.str() << "\t" << (*b)->rel().str() << endl;

  for(Flag i=1; i<Flag::count(); ++i)
    os << "FLAG\t" << i.str() << endl;
}









//================================= OLD

void Grammar::write(FILE* f)
{
  for(Cat i=1; i<Cat::count(); ++i)
    fprintf(f,"CAT\t%s\n",i.str());

  for(Role i=1; i<Role::count(); ++i)
    fprintf(f,"ROLE\t%s (%d)(%d)\n",i.str(),Role::index(i.str()),chk_type(i.str()));

  for(Role i=1; i<Role::count(); ++i)
    if(sgl.test(i)) fprintf(f,"SGL\t%s\n",i.str());
  
  for(Role i=1; i<Role::count(); ++i)
    if(left.test(i)) fprintf(f,"LEFT\t%s\n",i.str());

  for(Role i=1; i<Role::count(); ++i)
    if(right.test(i)) fprintf(f,"RIGHT\t%s\n",i.str());

  for(Cat c=1; c<Cat::count(); ++c)
    for(Role r=1; r<Role::count(); ++r)
      if(obl[c].test(r)) fprintf(f,"REQ\t%s\t%s\n",c.str(),r.str());
  
  for(Cat c=1; c<Cat::count(); ++c)
    for(Cat d=1; d<Cat::count(); ++d)
      for(Role t=1; t<Role::count(); ++t)
	if(connect[c][d].count(t))
          fprintf(f,"LINK\t%s\t%s\t%s\n",c.str(),d.str(),t.str());

  for(LongRel i=1; i<LongRel::count(); ++i)
    fprintf(f,"LONG\t%s\n",i.str());

  for(Cat c=1; c<Cat::count(); ++c)
    for(Cat d=1; d<Cat::count(); ++d)
      for(LongRel l=1; l<LongRel::count(); ++l)
	if(longrel[c][d].count(l))
          fprintf(f,"LLINK\t%s\t%s\t%s\n",c.str(),d.str(),l.str());

  for(Cat c=1; c<Cat::count(); ++c)
    for(Role r=1; r<Role::count(); ++r)
      for(list<Boubble*>::const_iterator b=uptrigger[c][r].begin(); b!=uptrigger[c][r].end(); b++)
	fprintf(f,"#TRIGGER\t%s\t%s\t%s\n",c.str(),r.str(),(*b)->rel().str());

  for(Cat c=1; c<Cat::count(); ++c)
    for(Role r=1; r<Role::count(); ++r)
      for(list<Boubble*>::const_iterator b=dntrigger[c][r].begin(); b!=dntrigger[c][r].end(); b++)
	fprintf(f,"#TRIGGER\t%s\t%s\t%s\n",c.str(),r.str(),(*b)->rel().str());

  for(Flag i=1; i<Flag::count(); ++i)
    fprintf(f,"FLAG\t%s\n",i.str());
}
