#include <std.h>
#include <String.h>
#include <iostream.h>
#include "SymTable.h"

int init_offset(type kind) {

  switch (kind) {
    case CLASS_S      : 
    case PROCESS_S    :
    case COROUTINE_S  :
    case SIGNAL_S     :
    case FUNCTION_S   :
    case PROCEDURE_S  :
    case BLOCK_S      :
    case HANDLERS_S   : break; 
   };

 return 1; 

};

int update_offset(type kind) {

  switch (kind) {
    case CLASS_S      : 
    case PROCESS_S    :
    case COROUTINE_S  :
    case SIGNAL_S     :
    case FUNCTION_S   :
    case PROCEDURE_S  :
    case HANDLERS_S   : 
    case VARIABLE_S   :
    case CONSTANT_S   : break; 
   };

 return 1; 

};

void list::add_elem(entry* e) {

  list_elem*  ThisElement;

  ThisElement       = new list_elem(e);
  ThisElement->next = end_ptr;
  end_ptr           = ThisElement;

};


entry* list::find_virt(String id)
{
  list_elem* ThisElement;

  for( ThisElement = end_ptr;
      (ThisElement != NULL) && ( ThisElement->info_ptr->id != id);
       ThisElement = ThisElement->next
     );

  if ( ThisElement )
    return ThisElement->info_ptr;
  else
    return NULL;
};


void spec_list::add(String id)
{

  end_ptr=new spec_elem(id,end_ptr);

};

bool spec_list::present(String id) {

  spec_elem*  temp;

  temp=end_ptr;
  while ( temp != NULL && temp->info != id ) 
    temp=temp->next;
  if (temp != NULL) 
    return TRUE;
  else
    return FALSE;

};


void pref_list::add_pref(entry* e,sharing sh)
{
  list* temp;

  end_ptr = new pref_list_elem(e,sh,end_ptr);

  if ((temp=((c_class*)(e->rest))->my_symtab->formal_param_list) != NULL) {
    if (symtab->last->formal_param_list == NULL)
      symtab->last->formal_param_list=new list;
    symtab->last->formal_param_list->end_ptr=temp->end_ptr;
  };

};


entry::entry(String ii,type kk,int oo,spec ss,packet* pp)
{
  id      = ii;
  kind    = kk;
  offset  = oo;
  visible = ss;
  rest    = pp;
};

node::node(int ll)
{ 
   level=ll;
   current_offset=OFFSET;
   if (symtab == NULL)
     end_ptr = begin_ptr 
             = new entry(NO_IDENT,FATHER,NO_OFFSET,NORMAL,
                         new f_father(NULL,level)
                        );
   else
     end_ptr = begin_ptr
             = new entry(NO_IDENT,FATHER,NO_OFFSET,NORMAL,
                         new f_father(symtab->last,level)
                        );

   virtual_list       = NULL;
   begin_ptr->prev    = NULL;
   close=taken=hidden = NULL;

};

void node::close_unit()
{
  symtab->last=((f_father*)(begin_ptr->rest))->father_ptr;
};


node* node::open_unit()
{
  node* temp;
 
  temp = new node(level+1);

  ((c_class*)(symtab->last->end_ptr->rest))->my_symtab=temp;

  temp->current_offset=init_offset(end_ptr->kind);
  symtab->last=temp;

  return temp;
 
};

void node::add_spec(spec kind,String id)
{
  
  switch (kind) {

    case CLOSE :
      if (close==NULL) close=new spec_list;
      close->add(id);
      break;
  
    case HIDDEN :
      if (hidden==NULL) hidden=new spec_list;
      hidden->add(id);
      break;

    case TAKEN :
      if (taken==NULL) taken=new spec_list;
      taken->add(id);
      break;

  };

};

void node::add_virt(entry* v) {

  if (virtual_list == NULL) virtual_list=new list;
  virtual_list->add_elem(v);

};

void node::add_form_param(entry* fp) {

  if (formal_param_list == NULL) formal_param_list=new list;
  formal_param_list->add_elem(fp);

};


struct find_result* node::local_find(String id,visible vis) {

  entry*  temp;
  bool    stop=FALSE;

   
  temp=end_ptr;

  while ( !stop && temp->kind != FATHER) {
    if (temp->id != id)
      temp=temp->prev; 
    else
      switch (temp->visible)
      {
        case HIDDEN :
          if (vis == NOT_HIDDEN)
            temp=temp->prev;
          else
            stop=TRUE;
        break;

        case NORMAL :
        case CLOSE  :
          stop=TRUE;
          break;
      };
  };

         
  if ( !stop ) 
    return new find_result(level,NULL);
  else 
    return new find_result(level,temp);
};

struct find_result* symboltable::find_in_module(node* start,String id,bool dot=DOT,visible vis=ALL) {

  struct find_result*  temp1;
  node*                temp2;
  pref_list_elem*      temp3;
  bool                 stop=FALSE;

   temp1=start->local_find(id,vis);
  if (temp1->found != NULL && !dot) 
     return temp1;
  else if (start->prefix != NULL) {
        
      temp3=start->prefix->end_ptr;
      while ((temp3 != NULL) && !stop) {
        temp1=((c_class*)(temp3->where->rest))->my_symtab->local_find(id,NOT_HIDDEN);
        if (temp1->found != NULL) stop = TRUE;
        temp3=temp3->next;
       };
      if (temp3 != NULL && !dot) return temp1;
     };

return new find_result(temp1->level,NULL);

 
};  

struct find_result* symboltable::find(String id) {

  struct find_result*  temp1;
  entry*               temp2;
  node*                temp3;

  temp1=symtab->find_in_module(symtab->last,id,FALSE,ALL);
  if (temp1->found != NULL && temp1->found->id == id) return temp1; 
  temp3=((f_father*)(symtab->last->begin_ptr->rest))->father_ptr;
  while (temp3 != NULL && temp1->level != MAIN_LEVEL ) { 
    temp1=symtab->find_in_module(temp3,id,FALSE,NOT_HIDDEN);  
    temp3=((f_father*)(temp3->begin_ptr->rest))->father_ptr;
  };

  if (temp1->found != NULL && temp1->found->id == id)

    return temp1;

  else

    return new find_result(MAIN_LEVEL,NULL);
   
};

entry* symboltable::inherit(String id,sharing sh=NOT_SHARED) {

  entry*        temp;

  pref_list_elem*    temp1;


  temp=symtab->find(id)->found;

  if (temp != NULL  && ( temp->kind ==CLASS_S  ||  temp->kind == PROCESS_S  ||  temp->kind == COROUTINE_S) ) {

    if (last->prefix == NULL) last->prefix = new pref_list;
    
    if (((c_class*)(temp->rest))->prefix != NULL) {

      temp1=((c_class*)(temp->rest))->prefix->end_ptr;
      while (temp1 != NULL) {
        last->prefix->add_pref(temp1->where,temp1->shared);
        temp1=temp1->next;
      };
    }; 
    last->prefix->add_pref(temp,sh);
    return temp;
  }

  else
  
    return NULL;

};

entry* symboltable::insert(String id,type kind,packet* p) {

  entry*    temp;

  spec      temp1;

   if ( ((symtab->last->local_find(id,ALL))->found == NULL) )
   {

    temp1=NORMAL;
    if (symtab->last->close != NULL) 
      if (symtab->last->close->present(id) ) temp1=CLOSE;
    if (symtab->last->hidden != NULL) 
      if (symtab->last->hidden->present(id) ) temp1=HIDDEN;
    temp=new entry(id,kind,symtab->last->current_offset,temp1,p);
    symtab->last->current_offset += update_offset(kind);
    temp->prev=symtab->last->end_ptr;
    symtab->last->end_ptr=temp;
    if (kind == VARIABLE_S &&  (((v_variable*)p)->type == POINTER)) {
      if (symtab->last->ref_var_list == NULL) 
        symtab->last->ref_var_list=new list;
      symtab->last->ref_var_list->add_elem(temp);
    };
    return temp;

   }

   else return NULL;

};
