#include <strstream.h>
#include <iomanip.h>
#include "cccc_utl.h"
#include "cccc.h"
#include "cccc_db.h"
#include "cccc_ast.h"
#include "cccc_tok.h"
#include "AParser.h"
#include "ATokPtr.h"

#define DEBUG_EXTENT_STREAMS 1

#define FS "@"
#define RS "\n"

static ofstream cmod("cccc_mod.ext");
static ofstream cmem("cccc_mem.ext");
static ofstream cuse("cccc_use.ext");

CCCC_String ParseUtility::stack_rules[MAX_STACK_DEPTH];
int         ParseUtility::stack_tokenline[MAX_STACK_DEPTH];
CCCC_String ParseUtility::stack_tokentext[MAX_STACK_DEPTH];
int         ParseUtility::stack_depth;
// insertion and extraction functions intended to support enumerations

void insert_enum(ostream& os, int e, char *e_chars) 
{ 
  if( (e>=0) && (e<strlen(e_chars)) )
  {
    os << e_chars[e]; 
  }
  else
    os << '?';
}

void extract_enum(istream& is, int& e, char *e_chars, int bad_value=0) {
  char c, *eptr=e_chars;
  is >> c;
  e=bad_value;
  while(*eptr != 0 && *eptr!=c) { eptr++; }
  if(*eptr==c) { 
    e=eptr-e_chars; 
  } else { 
    cerr << "bad enum character -"
	 << " got " << c
	 << " expected [" << e_chars << "]" << endl;
  } 
}


ostream& operator<<(ostream& os, AugmentedBool ab) {
  insert_enum(os,ab,"FT?X*");
  return os;
}

istream& operator>>(istream& is, AugmentedBool& ab) {
  extract_enum(is,(int&)ab,"FT?X*",abINVALID);
  return is;
}

ostream& operator<<(ostream& os, Visibility v) {
  insert_enum(os,v,"0123?X*");
  return os;
}

istream& operator>>(istream& is, Visibility& v) {
  extract_enum(is,(int&)v,"0123?X*",vINVALID);
  return is;
}

ostream& operator<<(ostream& os, UseType ut) {
  insert_enum(os,ut,"DdIHhPpVvTtiR?X*");
  return os;
}

istream& operator>>(istream& is, UseType& ut) {
  extract_enum(is,(int&)ut,"DdIHhPpVvTtiR?X*",utINVALID);
  return is;
}

CCCC_String& ParseUtility::lookahead_text(int n)
{
  static CCCC_String retval;
  retval="";
  int i;
  for(i=1; i<=n; i++)
  {
    if(parser->LT(i) != NULL)
    {
      retval=retval+parser->LT(i)->getText();
      retval=retval+" ";
    }
  }
  return retval;
}

void ParseUtility::resynchronize(
	int initial_nesting, SetWordType *resync_token_class, 
	ANTLRTokenPtr& resync_token)
{
  // the interface for resynchronisation is as follows:
  // the caller supplies a nesting level at which the resynchronisation must 
  // occur, and a token class containing all of the tokens which can 
  // be accepted to delimit the resynchronisation
  // this function will scan until it finds that it is at the correct level and
  // the next token of lookahead is in the resynchronisation set
  // it will then accept as many tokens from the resynchronisation set as
  // are available, consolidating the text of the tokens accepted
  // as the text associated with the last token
  CCCC_String resync_text="...";
  int resynchronising=1;
  while(resynchronising)
  {
      parser->consumeUntil(resync_token_class);
      if( 
	(MY_TOK(parser->LT(1))->getNestingLevel() > initial_nesting) &&
	(parser->LT(2) != NULL)
	)
      {
	parser->consume();
      }
      else
      {
	// we are ready to resynchronise
	resynchronising=0;
      }
  }

  // we now consume a succession of tokens from the resynchronisation token
  // class until we come across a token which is not in the set, or the
  // nesting level changes
  resync_token=parser->LT(1);
  while(
    parser->set_el(parser->LT(1)->getType(),resync_token_class) &&
    ( MY_TOK(parser->LT(1))->getNestingLevel() == initial_nesting) 
    )
  {
    resync_text=resync_text+parser->LT(1)->getText();
    resync_text=resync_text+" ";
    resync_token=parser->LT(1);
    resync_token->setText(resync_text);
    parser->consume();
  }
}


ParseUtility::ParseUtility() { }

void ParseUtility::reset(ANTLRParser *parser) {
  int i;
  for(i=0;i<pssLAST;i++) { set_string(PSString(i),""); }
  for(i=0;i<psfLAST;i++) { flag[i]='?'; }
  flag[psfLAST]='\0';
  trace_depth=0;
  stack_depth=0;
  this->parser=(ANTLR_Assisted_Parser*)parser;
}

  
ParseUtility::ParseUtility(const ParseUtility& ps) {
  *this=ps;
}

ParseUtility& ParseUtility::operator=(const ParseUtility& ps) {
  int i;
  for(i=0;i<pssLAST;i++) { 
    PSString pss=PSString(i);
    set_string(pss,ps.get_string(pss)); 
  }
  for(i=0;i<psfLAST;i++) { 
    flag[i]=ps.flag[i];
  }
  trace_depth=ps.trace_depth;
  stack_depth=ps.stack_depth;
  return *this;
}

ParseUtility::~ParseUtility() {}
  
const CCCC_String& ParseUtility::get_string(PSString pss) const { 
  return string[pss]; 
}
void  ParseUtility::set_string(PSString pss, const CCCC_String& value) { 
  string[pss]=value; 
}

int ParseUtility::get_flag(PSFlag psf) const {
  return int(flag[psf]); 
}

void ParseUtility::set_flag(PSFlag psf, int value) { 
  flag[psf]=value;
}

void ParseUtility::set_flag(Visibility value) { 
  MAKE_STRSTREAM(ofstr);
  ofstr << value;
  flag[psfVISIBILITY]=(ofstr.str())[0];
  RELEASE_STRSTREAM(ofstr);
}

void ParseUtility::dump(ostream& os, PSVerbosity psv){
  switch(psv) {
    case psvSILENT: 
      // do nothing
    break;

    case psvQUIET:
      // single line summary output
      os << "Signature: ";
      int i;
      for(i=0;i<pssLAST; i++) {
        CCCC_String sep;
        if(i==pssMODULE) { sep="::"; } else { sep=" "; }
        if(string[i]!="") { os << string[i] << sep; } 
      }
      os << "  Attributes: ";
      for(i=0; i<psfLAST; i++) {
        os<<flag[i];
      }
      os << endl;
    break;

    case psvLOUD:
      // verbose format
      os << "ModType:           " << string[pssMODTYPE] << endl;
      os << "Module:            " << string[pssMODULE] << endl;
      os << "Type:              " << string[pssITYPE] << endl;
      os << "Member:            " << string[pssMEMBER] << endl;
      os << "Params:            " << string[pssPARAMS] << endl;
      os << "Other attributes:" 
         << "  def: " << flag[psfVISIBILITY]
         << "  cst: " << flag[psfCONST]
         << "  stc: " << flag[psfSTATIC]
         << "  ext: " << flag[psfEXTERN]
         << "  vir: " << flag[psfVIRTUAL] << endl;
    break;

    default:
      cerr << "ParseUtility::dump() - illegal verbosity flag" << endl;
  }
}

void ParseUtility::insert_extent(ostream& os, AST* ast, UseType ut) {
  int line=0;
  if(ast!=NULL) line=ast->nStartLine();

  os << string[pssFILE] << "@"
     << line << "@" 
     << string[pssDESCRIPTION] << "@"
     << flags() << "@";

  if(ast!=NULL)
  {
    // we add one to the LOC count as the last line counts, even though
    // the line separator occurs after the parser considers the
    // extent to have closed
    os << "LOC:" << ast->getCount(tcCODELINES) + 1
       << " COM:" << ast->getCount(tcCOMLINES) 
       << " MVG:" << ast->getCount(tcMCCABES_VG)
       << " TOK:" << ast->getCount(tcTOKENS);
  }
  os << "@" << flag[psfVISIBILITY] << ut << ends;
}


  
void ParseUtility::record_module_extent(AST* ast, UseType ut ) {
    MAKE_STRSTREAM(str);
    str << string[pssMODULE] << "@" 
        << string[pssMODTYPE] << "@";
    insert_extent(str,ast,ut);

#ifdef DEBUG_EXTENT_STREAMS
if(DebugMask & EXTENT )
{
    str.freeze(0);
    cmod << str.str() << endl;
}
#endif
    prj->add_module(CONVERT_STRSTREAM(str));
    RELEASE_STRSTREAM(str);
}

void ParseUtility::record_function_extent(AST* ast, UseType ut ) {
    MAKE_STRSTREAM(str);
    str << string[pssMODULE] << "@" 
        << "" << "@"
        << string[pssMEMBER] << "@"
        << string[pssITYPE] << "@"
        << string[pssPARAMS] << "@"; 
    insert_extent(str,ast,ut);
#ifdef DEBUG_EXTENT_STREAMS
if(DebugMask & EXTENT )
{
    cmem << str.str() << endl;
}
#endif
    prj->add_member(CONVERT_STRSTREAM(str));
    RELEASE_STRSTREAM(str);
}

void ParseUtility::record_userel_extent(AST* ast, UseType ut) {
    // if pssMEMBER is not blank, we want to append it to pssDESCRIPTION
    if(strlen(string[pssMEMBER])>0)
    {
      MAKE_STRSTREAM(relstr);
      relstr << string[pssDESCRIPTION] 
	     << " [" << string[pssMEMBER] << "]" << ends;
      string[pssDESCRIPTION]=relstr.str();
      RELEASE_STRSTREAM(relstr);
    }

    // string[pssUTYPE] holds the name of the type of a parameter or return
    // value of a function, or the type of a member variable
    // if the unqualified type is blank, we map it to void
    // this is correct for parameters, wrong for return types (should be int)
    // but who really cares, as both are builtin types
    if(strlen(string[pssUTYPE])==0)
    {
      string[pssUTYPE]="void";
    }

    MAKE_STRSTREAM(str);
    str << string[pssMODULE] << "@" 
        << string[pssMEMBER] << "@"
        << string[pssUTYPE] << "@";
    insert_extent(str,ast,ut);
#ifdef DEBUG_EXTENT_STREAMS
if(DebugMask & EXTENT )
{
    cuse << str.str() << endl;
}
#endif
    prj->add_userel(CONVERT_STRSTREAM(str));
    RELEASE_STRSTREAM(str);
}

void ParseUtility::record_rejected_extent(AST* ast) {
  set_string(pssDESCRIPTION,ast->canonical_name());
  MAKE_STRSTREAM(str);
  insert_extent(str,ast,utREJECTED);
  prj->add_rejected_extent(CONVERT_STRSTREAM(str));
  RELEASE_STRSTREAM(str);
}

static void toktrace(ANTLRAbstractToken *tok)
{
  // at the LHS we put out information about the current token
  if(tok != NULL)
  {
    DbgMsg(PARSER,cerr,
	   setw(6) << tok->getLine() 
	           << setw(4) << tok->getType()
	           << setiosflags(ios::left) << resetiosflags(ios::right) 
	           << setw(20) << tok->getText()
    );
  }
  else
  {
    DbgMsg(PARSER,cerr,setw(30)<<"");
  }
}

enum InOrOut { IN, OUT };

static void rectrace(
  const char *rulename, const char *dir_indic, int guessing, 
  ANTLRAbstractToken *tok)
{
  static int trace_depth=0;
  if(guessing)
  {
    DbgMsg(PARSER,cerr,
	   setw(trace_depth*4+1) << "" << dir_indic 
                                 << "?" << rulename << endl);
  }
  else
  {
    trace_depth=((ANTLRToken*) tok)->getNestingLevel();
    DbgMsg(PARSER,cerr,
	   setw(trace_depth*4)<< "" << dir_indic << rulename << endl);
  }
}

void ParseUtility::tracein(
  const char *rulename, int guessing, ANTLRAbstractToken *tok)
{
  if(guessing == 0)
  {
    stack_tokentext[stack_depth]=tok->getText();
    stack_tokenline[stack_depth]=tok->getLine();
    stack_rules[stack_depth]=rulename;
    stack_depth++;
  }

  // first put out the token details
  toktrace(tok);
  
  // then the indented recognition trace
  rectrace(rulename,"-> ",guessing,tok);
}

void ParseUtility::traceout(
  const char *rulename, int guessing, ANTLRAbstractToken *tok)
{
  if(guessing == 0)
  {
    stack_depth--;
    // some error checking...
    if(stack_depth<0)
    {
      cerr << "ParseUtility::traceout negative stack depth - "
	   << "exiting from rule " << rulename 
	   << " at " << tok->getText() << " on line " << tok->getLine() 
	   << endl;
    }
    else if(strcmp(rulename,stack_rules[stack_depth])!=0)
    {
      cerr << "ParseUtility::traceout rule name mismatch - "
	   << rulename << "!=" << stack_rules[stack_depth] << endl;
    }
    stack_tokentext[stack_depth]="";
    stack_tokenline[stack_depth]=0;
    stack_rules[stack_depth]="";
  }
  // first put out the token details
  toktrace(tok);
  rectrace(rulename,"<- ",guessing,tok);
}
    
  
void ParseUtility::syn(ANTLRAbstractToken *tok)
{
  cerr << "Syntax error at ";
  if(tok != NULL)
  {
    cerr << tok->getText() << " on line " << tok->getLine() << endl;
  }
  else
  {
    cerr << "NULL token" << endl;
  }

  cerr << "Parser context:" << endl;
  for(int i=stack_depth-1; i>=0; i--)
  {
    cerr << "Trying to match " << stack_rules[i]
	 << " at '" << stack_tokentext[i]
	 << "' on line " << stack_tokenline[i] << endl;
  }
  cerr << endl;
}








