#include "List.h"
#include "command.h"
#include "tokentable.h"
#pragma implementation

//long vls=0;

Val_List::Val_List (Value* first_element, Val_List* cdr){
  elem = first_element;
  next = cdr;
//  cout<<(long)this<<" : "<<++vls<<"\n";
}

Val_List::~Val_List (){
//  cout<<(long)this<<" :    "<<--vls<<"\n";
  elem->release();
  delete next;
}

void Val_List::cat (Val_List* inlist){
  Val_List **temp;
  temp = &next;
  while (*temp)
    temp = &((*temp) -> next);
  (*temp) = inlist;
}

int Val_List::length (){
  int i;
  Val_List* temp;
  if (! this)
    return 0;
  for (temp = next , i=1 ; temp ; temp=temp->next , i++);
  return i;
}

Value* Val_List::nth (int i){
  Val_List* retval;
  if (!this)
    return NULL;
  for (retval = this ; i>1 ; i--)
    {if (retval->next)
       retval = retval->next;
     else
       return NULL;}
  return retval->elem;
}

Val_List* Val_List::insert (Value* someval , int pos){
  if (pos < 2 || !this)
    {return new Val_List (someval->grab(), this);}
  next = next->insert (someval , pos-1);
  return this;
}


char* Val_List::pack_list (char* buf){
  if (next)    /** yeah, i know it\'s inefficiently recursive. sue me. **/
    buf = next->pack_list (buf);
  return elem->pack_value (buf);
}

void Val_List::ensure_clean (){
  if (this)
    {elem = elem->ensure_clean();
     next->ensure_clean();}
}

Val_List* Val_List::copy (){
  Val_List *temp;
  if (!this)  return NULL;
  temp = new Val_List (elem->copy());
  temp->next = next->copy();
  return temp;
}

Val_List* Val_List::copy_some (int i){
  Val_List *temp, **tempptr, *index;
  for (index = this , temp = NULL , tempptr = &temp ; i ; i--)
    {*tempptr = new Val_List (index->elem->copy());
     tempptr = & ((*tempptr)->next);
     index = index->next;}
  return temp;
}

String* Val_List::tostr(){
  Val_List* listptr;
  String *temp = new String ("(");

  listptr = this;
  do
    {listptr->elem->tostr();
     (*temp) += *(listptr->elem->str);
     delete listptr->elem;
     listptr->elem = NULL;
     listptr = listptr->next;
     if (listptr)  (*temp) += " ";}
  while (listptr);
  (*temp) += ")";
  return temp;
}

int format_newline (String* ins, int nspc){
  int i;
  *ins += "\n";
  for (i=0 ; i<nspc ; i++)
    *ins += " ";
  return i+1;
}

#define FORMAT_CONCAT(s)             \
    {*temp += *(s);                  \
     length_so_far += (s)->length();}
#define FORMAT_S_CONCAT(s)            \
    {*temp += s;                      \
     length_so_far += strlen(s);}

//
// i take no great pride in this block of formatting code ... it's ugly,
// i guess because the whole process is fairly ugly.
// anyone to rewrite this cleanly gets a big cookie.
//
String* Val_List::format (int tab, int columns){
  Val_List* listptr;
  String *temp = new String ("(");
  String *lstr;
  int length_so_far = (tab + 1);

  if (!this)
    {FORMAT_S_CONCAT(")");
     return temp;}
  if (elem->get_type() == RESERVED)
    {switch (elem->reserved)
       {case QUOTE:
	 if (next)
	   {*temp = "'";
	    next->elem->tostr();
	    FORMAT_CONCAT(next->elem->str);
	    return temp;}
	 listptr = this;
	 break;
       case CALL:
	 if ((listptr = next) &&
	     (listptr->next) &&
	     (listptr->next->elem->get_type() == SYM) &&
	     ((listptr->elem->get_type() == OBJ) ||
	      (listptr->elem->get_type() == SYM)))
	   {listptr->elem->tostr();
	    FORMAT_CONCAT(listptr->elem->str);
	    FORMAT_S_CONCAT(":");
	    listptr->next->elem->format(tab+2, columns);
	    FORMAT_CONCAT(listptr->next->elem->str);
	    if (listptr = listptr->next->next)
	      FORMAT_S_CONCAT(" ");}
	 else
	   FORMAT_S_CONCAT ("call ");
	 break;
       default:
	 listptr = this;
	 break;}}
  else
    listptr = this;

  while (listptr)
    {switch (listptr->elem->get_type())
       {case LIST: case EXPR:
	 lstr = listptr->elem->list->format (tab+2 , columns);
	 if (listptr->elem->get_type() == LIST)
	   lstr->prepend ("'");
	 if ((lstr->length() + length_so_far + 1) > columns)
	   length_so_far += format_newline (temp, tab+2);
	 FORMAT_CONCAT(lstr);
	 delete lstr;
	 break;
       case STR:
	 if ((listptr->elem->str->length() + length_so_far + 3) > columns)
	   length_so_far += format_newline (temp, tab+2);	   
       default:
	 listptr->elem->format(tab+2 , columns);
	 if ((listptr->elem->str->length() + length_so_far + 1) > columns)
	   length_so_far += format_newline (temp, tab+2);
	 FORMAT_CONCAT(listptr->elem->str);}
     delete listptr->elem;
     listptr->elem = NULL;
     listptr = listptr->next;
     if (listptr)
       FORMAT_S_CONCAT(" ");
   }
  FORMAT_S_CONCAT(")");
  return temp;  
}

int Val_List::compare (Val_List* inlist){
  Val_List *ptr;
  ptr = this;
  for (ptr = this ; (ptr && inlist) ; ptr = ptr->next , inlist = inlist->next)
    {if (ptr->elem->compare (inlist->elem))
       return INT_MIN;}
  if (ptr)  return (-1);
  if (inlist)  return (1);
  return 0;
}

int Val_List::search (Value* key, Val_List* key_as_list){
  Val_List* complist = NULL;
  int retval;
  if (key->type == EXPR || key->type == LIST)
    return 0;
  if (! key_as_list)
    complist = (key_as_list = new Val_List (key->grab()));
  if (elem->type == LIST || elem->type == EXPR)
    {retval = elem->list->compare (key_as_list);
     if (retval == 1 || retval == 0)
       {delete complist;
        return 1;}}
  else
    {if (! elem->compare(key))
       {delete complist;
	return 1;}}
  retval = (next ? next->search (key, key_as_list) : 0);
  delete complist;
  if (retval)
    return retval + 1;
  return 0;
}


int Val_List::ok_command(){
  if (this)
    return (elem->ok_clause() && next->ok_command());
  return 1;
}

int Val_List::ok_clause(){
  Val_List* next_ptr;
  if (!this || (elem->get_type() != RESERVED))  return 0;
  switch (elem->reserved)
    {case BEFORE:
      for (next_ptr=next ; next_ptr ; next_ptr=next_ptr->next)
	if (next_ptr->elem->get_type() != STR)
	  return 0;
      return 1;
    case OR:
      for (next_ptr=next ; next_ptr ; next_ptr=next_ptr->next)
	if (! next_ptr->elem->ok_clause())
	  return 0;
      return 1;}
  return 0;
}

Val_List* Val_List::command_match (String& pattern){
  String* this_token;
  Val_List* rest_list;
//  cout << "pattern as passed to Value [" << pattern << "] = " <<
//    pattern.length() << "\n";
  if (this_token = elem->command_match (pattern))
    {if (next)
       {
//cout << "pattern before passing: ["  << pattern << "] = " << pattern.length() << "\n";
//cout << "this_token: [" << *this_token << "]\n";
	if (pattern.length() && this_token->length())
	  rest_list = next->command_match (pattern.after (*this_token));
	else
	  rest_list = next->command_match (pattern);
	if (rest_list)
	  {if (this_token->length() > 1 &&
	       this_token->elem(0) == '\"' &&
	       this_token->elem(this_token->length()-1) == '\"')
	     *this_token = (*this_token)(1, this_token->length()-2);
	   return new Val_List (new Value (this_token), rest_list);}
	else
	  {delete this_token;
	   return NULL;}}
     else
       {if ((! this_token->length() && pattern.length()) ||
	    (pattern.after(*this_token)).length())
	  {delete this_token;
	   return NULL;}
	else
	  return new Val_List (new Value (this_token));}}
  else
    return NULL;
}

String* Val_List::command_match_OR (String& pattern){
  String* this_token;
  if (!this)
    return NULL;
  if (this_token = elem->command_match (pattern))
    return this_token;
  return next->command_match_OR (pattern);
}