#include "command.h"
#include <regex.h>
#include <fstream.h>
#pragma implementation

static StringReHash command_constants;



CommandNode::CommandNode(Val_List* key, String* indata, CommandNode* nextnode){
  key_list = key;
  data = indata;
  next = nextnode;
}

CommandNode::~CommandNode(){
  delete key_list;
  delete data;
  delete next;
}

CommandList::CommandList(){
  clist = NULL;
}

CommandList::~CommandList(){
  delete clist;
}

String* CommandList::add (Val_List* key, String* indata){
  if (key && key->ok_command())
    {clist = new CommandNode (key, new String (*indata), clist);
     return indata;}
  else
    {return NULL;}
}

int CommandList::remove (String* pattern){
  CommandNode** clist_ptr, *templist;
  Val_List* vlist;
  for (clist_ptr = &clist ; *clist_ptr ;
       clist_ptr = &((*clist_ptr)->next))
    if (vlist = (*clist_ptr)->key_list->command_match (*pattern))
      {delete vlist;
       templist = *clist_ptr;
       *clist_ptr = templist->next;
       templist->next = NULL;
       delete templist;
       return 1;}
  return 0;
}

Value* list_lookup (Val_List* parse_list, String* pattern,
		    String** error_string = NULL){
  Val_List* retval=NULL;
  if ((!parse_list) || (!parse_list->ok_command()))
    {if (error_string && !(*error_string))
       *error_string = new String ("Invalid sentence passed to match-one");
     return new Value (E_INVALID_REGEXP, ERR);}
  retval = parse_list->command_match (*pattern);
  return new Value (retval);
}

void CommandList::purge (){
  delete clist;
  clist = NULL;
}

String* CommandList::lookup (String* pattern){
  CommandNode* templist;
  Val_List* vlist;
  for (templist = clist ; templist ; templist = templist->next)
    if (vlist = templist->key_list->command_match (*pattern))
      {delete vlist;
       return templist->data;}
  return NULL;
}

Val_List* CommandList::lookup_all (String* pattern){
  Val_List* retval = NULL;
  Val_List* vlist;
  CommandNode* traverser;
  for (traverser = clist ; traverser ; traverser = traverser->next)
    if (vlist = traverser->key_list->command_match (*pattern))
      {retval = new Val_List
	 (new Value
	  (new Val_List (new Value (new String (*traverser->data), SYM),
			 new Val_List (new Value (vlist)))),
	  retval);}
  return retval;
}

Value* CommandList::list_cmds(){
  Val_List* biglist = NULL, *outlist;
  CommandNode* traverser;
  for (traverser = clist ; traverser ; traverser = traverser->next)
    {outlist = new Val_List (new Value (new String (*traverser->data), SYM));
     outlist = new Val_List ((new Value (traverser->key_list->copy())),
			     outlist);
     biglist = new Val_List ((new Value (outlist)), biglist);}
  return new Value (biglist);
}


char* CommandList::pack_cmds (char* buf){
  CommandNode* traverser;
  long len;
  for (traverser = clist ; traverser ; traverser = traverser->next)
    {strcpy (buf , (char*)traverser->data->chars());
     buf += traverser->data->length() + 1;
     len = traverser->key_list->length();
     PACK_INT(buf, len);
     buf += sizeof (long);
     buf = traverser->key_list->pack_list (buf);}
  *buf = '\0';
  return ++buf;
}

char* CommandList::unpack (char* buf){
  String* tempstring;
  Val_List* templist;
  long len;
  delete clist;
  for (clist = NULL ; *buf ;)
    {tempstring = new String (buf);
     buf += tempstring->length() + 1;
     UNPACK_INT (buf, len);
     buf += sizeof (long);
     for (templist = NULL ; len ; len--)
       templist = new Val_List (unpack_value (&buf), templist);
     clist = new CommandNode (templist , tempstring , clist);}
  return buf+1;
}


void CommandList::dump_to_stdout(){
  CommandNode* traverser;
  Value* tempval;
  for (traverser = clist ; traverser ; traverser = traverser->next)
    {tempval = new Value (traverser->key_list->copy());
     tempval->tostr();
     cout << *tempval->str;
     tempval->release();
     cout << "\n" << *(traverser->data) << "\n";}
}


/****************************************************************
******** StringReHash implementation ****************************
*****************************************************************/

#define SSH_HASH(s) (((s)[0] + (s)[1]) % SSH_WIDTH)

StringPtrList::StringPtrList (String* inkey, void* indata,
			      StringPtrList* next_node = NULL){
  key = inkey;
  data = indata;
  next = next_node;
}

StringPtrList::~StringPtrList(){
  delete key;
  delete data;
  delete next;
}

StringPtrHash::StringPtrHash(){
  int i;
  for (i=0 ; i<SSH_WIDTH ; i++)
    slists[i] = NULL;
}

StringPtrHash::~StringPtrHash(){
  int i;
  for (i=0 ; i<SSH_WIDTH ; i++)
    delete slists[i];
}

void StringPtrHash::add (String* key, void* data){
  int index;
  char* stringkey;
  stringkey = (char*)key->chars();
  index = SSH_HASH(stringkey);
  slists[index] = new StringPtrList (key, data, slists[index]);
}

void* StringPtrHash::lookup (String* key){
  int index;
  char* stringkey;
  StringPtrList* sptr;
  stringkey = (char*)key->chars();
  index = SSH_HASH(stringkey);
  for (sptr = slists[index] ; sptr ; sptr=sptr->next)
    if (!fcompare(*key, *(sptr->key)))
      return sptr->data;
  return NULL;
}



void StringReHash::add (String* key, Regex* data){
  StringPtrHash::add (key, ((void*) data));
}

Regex* StringReHash::lookup (String* key){
  return (Regex*) StringPtrHash::lookup (key);
}


/****************************************************************
******** code for the command_constants *************************
*****************************************************************/

void sub_special (String& instr){
  instr.gsub ("\\\\" , "\1");
  instr.gsub ("\\n" , "\n");
  instr.gsub ("\\t" , "\t");
  instr.gsub ("\\\"" , "\"");
  instr.gsub ("\\" , "");
  instr.gsub ("\1" , "\\");
};


void initialize_command_constants(){
  int i;
  ifstream command_file ("COMMAND_CONSTANTS");
  String sym, regex;
  if (command_file.fail())
    {cerr << "COMMAND_CONSTANTS file not found\nUnable to initialize\n";
     exit(1);}
  for (i=0 ; readline(command_file, sym, ' ') ; i++)
    {readline(command_file, regex);
     sub_special (regex);
     command_constants.add (new String (sym),
			    new Regex ((char*)regex.chars()));}
  cout << i << " command constants found in COMMAND_CONSTANTS\n";
}

int is_command_constant (String* key){
  return (int)(command_constants.lookup (key));
}

String* match_command_constant (String* key, String& pattern){
  Regex* re;
  if (re = command_constants.lookup (key))
    if (pattern.contains(*re))
      return new String (pattern.through (*re));
  return NULL;
}

Regex* get_command_constant(String* key){
  return command_constants.lookup (key);
}