// -*- Mode: C++ -*-

#include "fuchlig.h"

bool isklammerauf (const char *c)
{
  return *c=='(' && !c[1];
}

bool isklammerzu (const char *c)
{
  return *c==')' && !c[1];
}

bool isvar (longchar i)
{
  return (i>=varstart) && (i<=varend);
}

constraint *interpretconstr (FILE *f, char *t)
{
  if (t[0]=='\\' || t[0]>='0' && t[0]<='9' || t[0]=='-' || t[0]=='+') {
    int i= getcharacter (t);
    if (isvar(i)) {
      constraint *k= constr.index(i);
      if (k) return k;
      EW(Error, "variable has no contraint and thus can't be used.");
    }
    else
      return new cequ (i);
  }
  if (isklammerauf(t)) {
    nextword (f, t);
    if (!strcasecmp (t, "and")) {
      constraint *lauf= NULL;
      forever {
        nextword (f, t);
        if (isklammerzu(t)) break; //!
        if (!lauf) lauf= interpretconstr (f, t);
        else
          lauf= new cand (lauf, interpretconstr (f, t));
      }
      if (!lauf) lauf= new ctrue;
      return lauf;
    }
    else
    if (!strcasecmp (t, "or")) {
      constraint *lauf= NULL;
      forever {
        nextword (f, t);
        if (isklammerzu(t)) break; //!
        if (!lauf) lauf= interpretconstr (f, t);
        else
          lauf= new cor (lauf, interpretconstr (f, t));
      }
      if (!lauf) lauf= new cfalse;
      return lauf;
    }
    else
    if (!strcasecmp (t, "range")) {
      int l= nextint (f);
      int u= nextint (f);
      nextword (f, t);
      if (!isklammerzu(t))
        EW(Error, ") expected in range-constraint");
      return new crange (l, u);
    }
    else
    if (!strcasecmp (t, "not")) {
      constraint *l= nextconstraint (f);
      nextword (f, t);
      if (!isklammerzu (t))
        EW(Error, ") expected in not-constraint");
      l= new cnot (l);
      return l;
    }
    else
      EW(Error, "`%s' is illegal. Constraints must only consist of either "
                "`and', `or', `range' or `not'-rules.", t);
  }
  EW(Error, "`%s' is illegal. Constraints must either be character constants "
            "or compound constraints of the form (FUNCTOR C1 ... C2) with "
            "FUNCTOR being one of `and', `or', or `not' and Cn being "
            "constraints or `range' with C1, C2 being character constants.", t);
}

constraint *nextconstraint(FILE *f)
{
  tokenstr t;
  nextword (f, t);
  return interpretconstr (f, t);
}


void readconstraint (FILE *f)
{
  longchar i= nextint (f);
  if (!isvar(i))
    EW(Error, "%d is not declared as a variable.  Please use a `var'"
                "-command", i);
  constraint *a= nextconstraint(f);
  DEBUG(4,"readconstraint: "<<i<<": "<<a);
  if (constr[i]) {
    EW(Warning, "redefining constraint for %d", i);
    delete constr[i];
  }
  constr[i]= a;
}

int replaceonce (int &start)
{
  DEBUG(15, "replaceonce 1: `"<<replacebuff<<"'");
  for (int i=start; i<anzreplace; i++) {
    if (replace[i]) {
      int result=
        lreplacestr (replacebuff,
                    replace[i]->input,
                    replace[i]->output,
                    constr,
                    isvar);
      DEBUG(15, "replaceonce 2: r"<<i<<": `"<<replacebuff<<"'");
      if (result!=1) return result;
    }
    else {
      DEBUG(15, "replaceonce 3: cut");
      start= i+1;
    }
  }
  return 1;
}

void replaceall ()
{
  int result;
  int i= 0;
  int start= 0;
  while ((result=replaceonce(start))==0 && i<maxreplace) i++;
  if (i>=maxreplace)
    EW(error, "unterminated replacement loop.  This might either be "
              "caused by infinite recurrences or by too small a maximum "
              "for the test causing this error.  The current maximum "
              "is %d.  It can be changed using the command line option "
              "-maxreplace", maxreplace);
  if (result==2)
    EW(error, "uninstantiated variables on right side of match");
}

void insertfuch_aux3 (longchar c)
{
//  if (c==NONEXIST) insertfuch_aux2 (0);
//  else insertfuch_aux2 (c);
//  return;
  // NYI:
  if (c==NONEXIST) {
    *prepl= 0;
    DEBUG(11, "insertfuch_aux3: got word `"<<replacebuff<<"'");
    replaceall();
    DEBUG(11, "insertfuch_aux3: storing word `"<<replacebuff<<"'");
    for (const longchar *p= replacebuff; *p; p++)
      insertfuch_aux2 (*p);
    insertfuch_aux2 (0); // wichtig: 0, nicht NONEXIST.
                         // Aus irgendeinem Grund.
    prepl= replacebuff;
  }
  else {
    if (prepl>=preplend)
      EW(error, "capacity exceeded. replace buffer has size %d", MAXWORDLEN);
    *prepl++= c;
  }
}

void readreplace (FILE *f)
{
  int i;
  int result;
  preliginfo *p;
  longchar *a;

  if (anzreplace>=MAXREPLACE)
    EW (Error, "capacity exceeded. %d replace entries are maximum", MAXREPLACE);

  p= replace[anzreplace]= new preliginfo;

  p->input= a= dupnextstring (f, MAXSTRINGLEN, isend, &i, &result);
  if (result==2) {
    if (lstrlen (a)!=0)
      EW (Error, "a replace-end statement must not contain strings.");
    free (a);
    replace[anzreplace]= NULL; /* Markierung: hier ist eine Schranke */
  }
  else {
    p->inputlen= lstrlen (p->input);
    if (p->inputlen==0)
      EW (Error, "left side string of a replace rule must not be empty");
    p->output= dupnextstring (f, MAXSTRINGLEN, isdotend, &i);
    p->cut= lstrlen (p->output);
  }

  anzreplace++;
}

void showreplace ()
{
  cout<<"Variables range from "<<varstart<<" to "<<varend<<"."<<endl;
  cout<<"Constraints for variables:"<<endl;
  for (int i=varstart; i<=varend; i++)
    if (constr[i])
      cout<<"Variable "<<i<<" must fulfill "<<constr[i]<<endl;
  cout<<"end"<<endl<<endl;
  cout<<"Replace-Table:"<<endl;
  for (int i=0; i<anzreplace; i++) {
    cout<<i<<": ";
    if (replace[i])
      cout<<"`"<<replace[i]->input<<"', length "
          <<replace[i]->inputlen<<" -> `"
          <<replace[i]->output<<"'";
    else
      cout<<"cut!";
    cout<<endl;
  }
  cout<<"end"<<endl<<endl;
}

