#include <map>
#include <list>
#include "TypedefTable.h"
#include <cassert>
using namespace std;

#if 0
#include <iostream>
#  define debugPrint(x) cerr << x
#else
#  define debugPrint(x)
#endif

TypedefTable::TypedefTable() : currentScope(0) {}

bool TypedefTable::isKind(string identifier, kind_t kind) const {
    tableType::const_iterator id_pos = table.find(identifier);
    if (id_pos == table.end()) {
	return true;
    } else {
	return (*((*id_pos).second.begin())).kind == kind;
    }
}

bool TypedefTable::isIdentifier(string identifier) const {
    return isKind(identifier, ID);
}

bool TypedefTable::isTypedef(string identifier) const {
    return isKind(identifier, TD);
}

bool TypedefTable::isTypegen(string identifier) const {
    return isKind(identifier, TG);
}

void TypedefTable::addToScope(const std::string &identifier, kind_t kind, int scope) {
    if ( currentContext != "" && scope == contextScope ) {
	DeferredEntry entry = { identifier, kind };
	contexts[currentContext].push_back( entry );
    } else {
	debugPrint( "Adding " << identifier << " as type " << kind << " scope " << scope << " from scope " << currentScope << endl );
	Entry newEntry = { scope, kind };
	tableType::iterator curPos = table.find(identifier);
	if (curPos == table.end()) {
	    list<Entry> newList;
	    newList.push_front(newEntry);
	    table[identifier] = newList;
	} else {
	    list<Entry>::iterator listPos = (*curPos).second.begin();
	    while( listPos != (*curPos).second.end() && listPos->scope > scope ) {
		listPos++;
	    }
	    (*curPos).second.insert(listPos, newEntry);
	}
    }
}

void TypedefTable::addToCurrentScope(const std::string &identifier, kind_t kind) {
    addToScope( identifier, kind, currentScope );
}

void TypedefTable::addToCurrentScope(kind_t kind) {
    addToCurrentScope( nextIdentifiers.top(), kind );
}

void TypedefTable::addToEnclosingScope(const std::string &identifier, kind_t kind) {
    assert( currentScope >= 1 );
    addToScope( identifier, kind, currentScope - 1 );
}

void TypedefTable::addToEnclosingScope(kind_t kind) {
    addToEnclosingScope( nextIdentifiers.top(), kind );
}

void TypedefTable::addToEnclosingScope2(const std::string &identifier, kind_t kind) {
    assert( currentScope >= 2 );
    addToScope( identifier, kind, currentScope - 2 );
}

void TypedefTable::addToEnclosingScope2(kind_t kind) {
    addToEnclosingScope2( nextIdentifiers.top(), kind );
}

void TypedefTable::setNextIdentifier( const std::string &identifier ) {
    nextIdentifiers.top() = identifier;
}

void TypedefTable::openContext( std::string contextName ) {
    map< string, deferListType >::iterator i = contexts.find( contextName );
    if ( i != contexts.end() ) {
	deferListType &entries = i->second;
	for (deferListType::iterator i = entries.begin(); i != entries.end(); i++) {
	    addToEnclosingScope( i->identifier, i->kind );
	}
    }
}

void TypedefTable::enterScope(void) {
    currentScope += 1;
    deferListStack.push( deferListType() );
    nextIdentifiers.push( "" );
    debugPrint( "Entering scope " << currentScope << ", nextIdentifiers size is " << nextIdentifiers.size() << endl );
}

void TypedefTable::leaveScope(void) {
    debugPrint( "Leaving scope " << currentScope << endl );
    for (tableType::iterator i = table.begin(); i != table.end(); i++) {
	list<Entry> &declList = (*i).second;
	while (!declList.empty() && declList.front().scope == currentScope) {
	    declList.pop_front();
	}
	if ( declList.empty() ) {
	    table.erase( i );
	}
    }
    currentScope -= 1;
    for (deferListType::iterator i = deferListStack.top().begin(); i != deferListStack.top().end(); i++) {
	addToCurrentScope( i->identifier, i->kind );
    }
    deferListStack.pop();
    debugPrint( "nextIdentifiers size is " << nextIdentifiers.size() << " top is " << nextIdentifiers.top() << endl );
    nextIdentifiers.pop();
}

void TypedefTable::enterContext( std::string contextName ) {
    currentContext = contextName;
    contextScope = currentScope;
}

void TypedefTable::leaveContext(void) {
    currentContext = "";
}

void TypedefTable::print(void) const {
    for (tableType::const_iterator i = table.begin(); i != table.end(); i++) {
	debugPrint( (*i).first << ": " );
	list<Entry> declList = (*i).second;
	for (list<Entry>::const_iterator j = declList.begin(); j != declList.end(); j++) {
	    debugPrint( "(" << (*j).scope << " " << (*j).kind << ") " );
	}
	debugPrint( endl );
    }
}
