#include <vector>
#include <string>
#include <ostream>
#include <sstream>
#include <cassert>
#include <algorithm>
using namespace std;

class VarManager{
public:
    bool create_var(string name, ostream&out);
    bool add_arg(string name, unsigned pos);
   
    bool is_var_defined(string name)const;
    string get_var_address(string name)const;
    void push_scope();
    void pop_scope(ostream&out);

    void simulate_pop_all_scopes(ostream&out)const;
private:
    vector<string>var_name;
    vector<unsigned>scope_start;
    vector<string>args;
};

bool VarManager::create_var(string name, ostream&out){
    assert(!scope_start.empty());
    if(std::find(var_name.begin() + scope_start.back(), var_name.end(), name) == var_name.end()){
        var_name.push_back(name);
        out<<"subl $4, %esp"<<endl;
        return true;
    }else
	return false;
}

bool VarManager::add_arg(string name, unsigned pos){
    // Does argument with the same name exist?
    if(find(args.begin(), args.end(), name) != args.end())
        return false;
   
    args.resize(max(args.size(), pos+1));

    // Is the stack position still empty?
    if(args[pos].empty()){
        args[pos] = name;
        return true;
    }else
        return false;
}
   
bool VarManager::is_var_defined(string name)const{
    assert(!scope_start.empty());
    if(std::find(var_name.begin(), var_name.end(), name) != var_name.end())
        return true;
    if(std::find(args.begin(), args.end(), name) != args.end())
        return true;
    return false;
}

string VarManager::get_var_address(string name)const{
    assert(!scope_start.empty());
    for(unsigned i_plus_one = var_name.size(); i_plus_one > 0; --i_plus_one)
        if(var_name[i_plus_one-1] == name){
            ostringstream out;
            out<<"-"<< 4 *(i_plus_one)<<"(%ebp)";
            return out.str();
        }
   
    for(unsigned i=0; i<args.size(); ++i)
        if(args[i] == name){
            ostringstream out;
            out<< 4 *(i+2)<<"(%ebp)";
            return out.str();
        }
    assert(false);
}

void VarManager::push_scope(){
    scope_start.push_back(var_name.size());
}

void VarManager::pop_scope(ostream&out){
    assert(!scope_start.empty());
    unsigned to_pop = 4*(var_name.size() - scope_start.back());
    if(to_pop != 0){
        out<<"addl $"<<to_pop<<", %esp"<<endl;
        var_name.erase(var_name.begin() + scope_start.back(), var_name.end());
    }
    scope_start.pop_back();
}

void VarManager::simulate_pop_all_scopes(ostream&out)const{
    assert(!scope_start.empty());
    unsigned to_pop = 4*var_name.size();
    if(to_pop != 0)
        out<<"addl $"<<to_pop<<", %esp"<<endl;
}

