#include "SymbolTable.h"

SymbolTable::SymbolTable(){
  CurrentDepth=0;
  Stack<String> *stk=new Stack<String>();
  stack.Push(stk);
}

SymbolTable::~SymbolTable(){
  while(!stack.Is_Empty()){
    Stack<String> *stk=stack.Pop();
    while(!stk->Is_Empty()){
      String varname=stk->Pop();
      table.Find(varname);
      Stack<Info> *valstk=table.Retrieve();
      valstk->Pop();
      if(valstk->Is_Empty()){
        delete valstk;
        table.Remove(varname);
      }
    }
    delete stk;
  }
}

void  SymbolTable::Declare(String VarName){
  Stack<Info> *stk;
  if(table.Find(VarName)){
    stk=table.Retrieve();
    if(stk->Top().Depth==CurrentDepth){
      cout<<"Error: Re-declaration of variable "<<VarName<<endl;
      return;
    }
  }else{
    stk=new Stack<Info>;
    table.Insert(VarName,stk);
  }
  Info inf;
  inf.Depth=CurrentDepth;
  inf.Val=0;
  stk->Push(inf);
  stack.Top()->Push(VarName);
}

void SymbolTable::CallProcedure(){
  Stack<String> *stk=new Stack<String>();
  stack.Push(stk);
  CurrentDepth++;
}

void SymbolTable::ExitProcedure(){
  if(CurrentDepth==0){
    cout<<"Can not exit the main procedure this way"<<endl;
    return;
  }
  Stack<String> *stk=stack.Pop();
  while(!stk->Is_Empty()){
    String varname=stk->Pop();
    table.Find(varname);
    Stack<Info> *valstk=table.Retrieve();
    valstk->Pop();
    if(valstk->Is_Empty()){
      delete valstk;
      table.Remove(varname);
    }
  }
  delete stk;
  CurrentDepth--;
}

void  SymbolTable::Write(String VarName, int Value){
    Stack<Info> *stk;
    if(!table.Find(VarName)){
      cout<<"Variable "<<VarName<<" was not declared"<<endl;
      return;
    }
    stk=table.Retrieve();
    Info tmp;
    if(stk->Top().Depth!=CurrentDepth){
      cout<<"Error: Write to out-of-scope variable "<<VarName<<endl;
      return;
    }
    tmp=stk->Pop();
    tmp.Val=Value;
    stk->Push(tmp);
  }

int   SymbolTable::Read(String VarName){
    Stack<Info> *stk;
    if(!table.Find(VarName)){
      cout<<"Variable "<<VarName<<" was not declared"<<endl;
      cout<<"Returning default value 0"<<endl;
      return 0;
    }
    stk=table.Retrieve();
    Info tmp;
    if(stk->Top().Depth!=CurrentDepth){
      cout<<"Error: Read from out-of-scope variable "<<VarName<<endl;
      cout<<"Returning default value 0"<<endl;
      return 0;
    }
    return stk->Top().Val;
  }
