/**
 * @file
 * $Id$
 * $Revision$
 * $Author$
 * $Date$
 *
 * This file is part of The iWear Framework.
 *
 * The iWear Framework is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by the
 * Free Software Foundation as in version 2 of the License.

 * 
 * The iWear Framework is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * The iWear Framework; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <iwremote/idl.h>
#include <iwear/debugstream.h>
#include <iwear/i18n.h>
#include <iwear/scopetracer.h>
#include <iwremote/translationunit.h>
#include <iwremote/namespace.h>
#include <iwremote/class.h>
#include <iwremote/function.h>
#include <iwremote/declaration.h>
#include <iwremote/enum.h>
//#include <iwremote/

#include <list>
#include <string>
#include <queue>
#include <stack>
using namespace std;
extern "C" int yylineno;
extern "C" char* yytext;
#define PARSE_ERROR(x) THROW(parse_error,((x),yylineno,yytext));

extern "C"
{
#include <stdint.h>
}
/*
 * TODO:
 * 
 * - Rewrite this part and the internal structure part to easily support scoped
 *   classes and typedefs. The was to do this would probably be to add the
 *   containers used for parsing here to the structures, so each namespace or
 *   class would have such a container and only duplicate entries there would
 *   be refused. Currently we refuse every typedef to a same name, regardless
 *   in what namespace or class scope it is.
 *   We then have to make sure too that when a typedefed type is used the
 *   correct scope will be chosen.
 */
namespace iwear
{
    namespace net
    {
	namespace idl
	{

#define set_free(x) free(x);
	    
TranslationUnit tu;

const char* expected_token = 0;
#define REALIFY(x) "MISSING CODE EXPRESSION IN LINE "#x
#define STRINGIFY(x) REALIFY(x)

Namespace* actual_namespace = 0;
Class* actual_class = 0;
ContainerType* actual_container = 0;

uint32_t arg_count = 0;

std::string function_return;
std::string call_declaration;
list<FunctionArgument*> function_arguments;
map<string,Class*> classes;
list<SubClass> actual_subclasses;
protection actual_protection;
list<EnumDef> enumdef_list;
map<string,TypeDef*> typedefs;
map<string,Type*> alltypes;
stack<ContainerType* > started_containers;
map<string,Enum*> enums;

uint32_t actual_version_specifier = 0;

void start_namespace( char* const& ns )
{
    TRACE_SCOPE(ns);
    expected_token = "class, enum or typedef";
    d_dbg << ANSI_GREEN << "namespace " << ANSI_NORMAL << ns << " {" << endl;

    Namespace* nn = new Namespace(ns);
    nn->parent = actual_namespace;
    if( actual_namespace )
    {
	actual_namespace->add_item(*nn);
    }
    else
    {
	tu.add_namespace(*nn);
    }
    actual_namespace = nn;

    set_free(ns);
}

void end_namespace( void )
{
    TRACE_SCOPE();
    expected_token = STRINGIFY(__LINE__);
    d_dbg << "}" << endl;
    if( ! actual_namespace )
    {
	PARSE_ERROR(i18n::trans("end_namespace without active namespace"));
    }
    actual_namespace = actual_namespace->parent;
}
extern "C"  char* yytext;

void start_class( char* const& c )
{
    TRACE_SCOPE(c);
    expected_token = STRINGIFY(__LINE__);
    d_dbg << ANSI_GREEN << "class " << ANSI_NORMAL << c << " {" << endl;
    if( ! actual_namespace )
    {
	PARSE_ERROR(i18n::trans("class only allowed in namespace context"));
    }

    Class* cl = new Class(c,actual_version_specifier);
    actual_version_specifier = 0;

    if( actual_class )
    {
	PARSE_ERROR(i18n::trans("nested classes not supported"));
    }
    actual_class = cl;
    actual_namespace->add_item(*cl);
    actual_protection = private_t;

    set_free(c);
}

void end_class( void )
{
    TRACE_SCOPE();
    expected_token = "class, enum, typedef or namespace end";
    classes.insert(make_pair(actual_class->id, actual_class));

    actual_class->subclasses.insert(actual_class->subclasses.end(), actual_subclasses.begin(), actual_subclasses.end());
    actual_class = 0;
    actual_subclasses.clear();
    d_dbg << "};" << endl;
}

FunctionArgument* actual_return = 0;

void start_function( char* const& cd, char* const& rt )
{
    TRACE_SCOPE(cd,rt);
    expected_token = "Function Argument List: e.g. (int,int)";
    d_dbg << "    " << ANSI_BLUE << cd << " " << ANSI_GREEN << rt << ANSI_NORMAL;

    if( actual_return )
    {
	PARSE_ERROR(i18n::trans("Starting a function with active return type"));
    }

    actual_return = new FunctionArgument( "", "void", "");

    actual_return->type = get_type(rt);
 
    call_declaration = cd;

    function_arguments.clear();

    set_free(cd);
    set_free(rt);
}

void start_function( char* const& rt )/*{{{*/
{
    TRACE_SCOPE(rt);
    start_function(strdup(""),rt);
}/*}}}*/

SubClass* actual_subclass = 0;

void start_derivation( char* const& prot, char* const& cid )
{
    TRACE_SCOPE(prot,cid);
    expected_token = STRINGIFY(__LINE__);
    d_dbg << ANSI_DARKYELLOW << prot << " " << ANSI_NORMAL << cid << "," << endl;
    if( actual_subclass )
    {
	PARSE_ERROR(i18n::trans("Actual Subclass still active"));
    }
    actual_subclass = new SubClass(prot, cid );
}

void finish_parsing( void )
{
    TRACE_SCOPE();
    expected_token = STRINGIFY(__LINE__);
    // Loop through the known classes and lookup their derivations. If we
    // cannot find a matching one, abort
    if( classes.size() == 0 )
    {
	cerr << i18n::trans("Warning: No classes found at EOF") << endl;
    }

    map<string,Class*>::iterator ci(classes.begin());
    for(; ci != classes.end(); ++ci )
    {
	Class* ac = ci->second;

	// Iterate through the subclasses
	list<SubClass>& scl = ac->subclasses;

	list<SubClass>::iterator scli(scl.begin());
	for(; scli != scl.end(); ++scli )
	{
	    map<string,Class*>::const_iterator fc(classes.find(scli->class_id));
	    if( fc == classes.end() )
	    {
		PARSE_ERROR(i18n::trans("Cannot find declaration of class:") + scli->class_id);
	    }
	    scli->d_class = fc->second;
	}
    }
}


void start_container_list( void )
{
    TRACE_SCOPE();
    expected_token = STRINGIFY(__LINE__);
    start_container(strdup("list"),1);
}

void start_container_set( void )
{
    TRACE_SCOPE();
    expected_token = STRINGIFY(__LINE__);
    start_container(strdup("set"),1);
}

void start_container_vector( void )
{
    TRACE_SCOPE();
    expected_token = STRINGIFY(__LINE__);
    start_container(strdup("vector"),1);
}

void start_container_map( void )
{
    TRACE_SCOPE();
    expected_token = STRINGIFY(__LINE__);
    start_container(strdup("map"),2);
}

void start_container_pair( void )
{
    TRACE_SCOPE();
    expected_token = STRINGIFY(__LINE__);
    start_container(strdup("pair"),2);
}

void add_container_type_x( char* const& ct )
{
    TRACE_SCOPE(ct);
    expected_token = STRINGIFY(__LINE__);
    d_inf << ANSI_BLUE << "add_container_type_x(" << ct << ")" << ANSI_NORMAL << endl;
    add_container_type(ct); /// @todo check if this is really correct
//    PARSE_ERROR(i18n::trans("You have reached a dark end of the compiler, please file a bug report"));
}

void add_container_type( char* const& ct )
{
    TRACE_SCOPE(ct);
    expected_token = STRINGIFY(__LINE__);
    ContainerType* ac = started_containers.top();
    if( ! ac )
    {
	PARSE_ERROR(i18n::trans("adding container type without container"));
    }

    if( string(ct) == "<container type>" )
    {
	// looks like the last one added was a container type
    }
    else
    {
	ac->ids.push_back( get_type(ct) );
    }

    set_free(ct);
}

void end_derivation( void )
{
    TRACE_SCOPE();
    expected_token = STRINGIFY(__LINE__);
    if( ! actual_subclass )
    {
	PARSE_ERROR(i18n::trans("Cannot end derivation without an active one"));
    }
    actual_subclasses.push_back(*actual_subclass);
    actual_subclass = 0;
}

void end_function( char* const& fn )/*{{{*/
{
    TRACE_SCOPE(fn);
    end_function( fn, strdup(""));
}/*}}}*/

void end_function( char* const& fn, char* const& alias )/*{{{*/
{
    TRACE_SCOPE(fn,alias);
    expected_token = I18NTRANS("Function declaration, member variable declaration or class end via };");
    d_dbg << " " << fn << "( ";

   Function* func = new Function(*actual_return,fn,false, 
	    call_declaration == "oneway", call_declaration == "cached", actual_version_specifier,alias);
   actual_version_specifier = 0;
   

d_dbg << "CALL DECLARATION WHEN ENDING " << fn << " : \"" << call_declaration << "\"" << endl;
    if( call_declaration == "deferrable" )
    {
	d_dbg << ANSI_RED << "Setting " << fn << " to deferred" << ANSI_NORMAL << endl;
	func->is_deferred = true;
    }

    actual_return = 0;
    func->prot = actual_protection;

    if( ! actual_class )
    {
	PARSE_ERROR(i18n::trans("Cannot add Function without class"));
    }

    if( actual_class->has_variables )
    {
	//XXX
	PARSE_ERROR(i18n::trans("Sorry, mixed data/function objects not yet supported"));
    }

    actual_class->add_item(*func);

    list<FunctionArgument*>::iterator li(function_arguments.begin());

    for(; li != function_arguments.end();)
    {
	func->add_argument(**li);
	
	if( (*li)->type )
	{
	    d_dbg << ANSI_GREEN << (*li)->type->id << ANSI_NORMAL;
	}
	else
	{
	    d_dbg << ANSI_GREEN << "void" << ANSI_NORMAL;
	}

	if( (*li)->id.length() )
	{
	    d_dbg << (*li)->id.length() << " ";
	}
	else
	{
	    d_dbg << " ";
	}
	++li;
	if( li != function_arguments.end() )
	{
	    d_dbg << ", ";
	}
    }
    d_dbg << ");" << endl;

    set_free(fn);
}/*}}}*/

void create_parameter( void )/*{{{*/
{
    TRACE_SCOPE();
    expected_token = STRINGIFY(__LINE__);
    char* a = strdup("");
    char* b = strdup("void");
    char* c = strdup("");
    create_parameter(a,b,c);
}/*}}}*/

void create_parameter( char* const& pd, char* const& type )/*{{{*/
{
    TRACE_SCOPE(pd,type);
    expected_token = STRINGIFY(__LINE__);
    char* str = (char*)malloc(200);
    snprintf(str,200,"arg%u",arg_count);
    arg_count++;
    create_parameter( pd, type, str );
}/*}}}*/

Type* get_type( const string& tn )
{
    /**
     * We want the type object named by the string.
     */
    TRACE_SCOPE(tn);
    d_dbg << "Searching for type \"" << tn << "\"" << endl;
    // This is a little hack to set the container type for when we finish it.
    if( actual_container )
    {
	d_dbg << tn << " is the actual_container" << endl;
	Type* r = actual_container;
	actual_container = 0;
	d_dbg << "return " << *r << endl;
	return r;
    }
    // Search first if its a typedef (we can afford the few nullptrs that this will insert)
    TypeDef* td = typedefs[tn];
    if( td )
    {
	d_dbg << tn << " is a typedef for " << td->dtype->id << endl;
	d_dbg << "return " << *(td->dtype) << endl;
	return td->dtype;
    }

    // Look if we have a type with this name we know.
    Type*& ret = alltypes[tn];
    if( ! ret ) // Its a new type we shall create.
    {
	d_dbg << tn << " seems to be a new yet unknown type" << endl;
	ret = new Type(tn,false); // XXX look here again when adding reference support
    }

    // If its an enum, mark it as such
    if( enums.find(tn) != enums.end() )
    {
	ret->is_enum = true;
    }
    d_dbg << "return " << *ret << endl;
    return ret;
}

void create_parameter( char* const& pd, char* const& type, char* const& id )/*{{{*/
{
    TRACE_SCOPE(pd,type,id);
    expected_token = STRINGIFY(__LINE__);
    try
    {
	if( actual_container )
	{
	    actual_container->Type::id = id;
	}

	FunctionArgument* nfa = new FunctionArgument( pd,type,id);
	nfa->type = get_type(type);
	function_arguments.push_back(nfa);
    }
    catch(const exception& e )
    {
	PARSE_ERROR(e.what());
    }

    free(pd);
    free(type);
    free(id);
}/*}}}*/

void change_protection( char* const& pr )/*{{{*/
{
    TRACE_SCOPE(pr);
    expected_token = STRINGIFY(__LINE__);
    d_dbg << ANSI_DARKYELLOW << pr << ANSI_NORMAL << ":" << endl;
    if( std::string("protected") == pr )
    {
	actual_protection = protected_t;
    }
    else if( std::string("private") == pr )
    {
	actual_protection = private_t;
    }
    else if( std::string("public") == pr )
    {
	actual_protection = public_t;
    }
    else
    {
	PARSE_ERROR(i18n::trans("Invalid protection"));
    }
}/*}}}*/

void finish_container( )
{
    TRACE_SCOPE();
    expected_token = STRINGIFY(__LINE__);
    d_inf << ANSI_BLUE << "finish_container" << ANSI_NORMAL << endl;
    ContainerType* ac = started_containers.top();

    if( ac->ids.size() != ac->expected )
    {
	stringstream sz;
	sz << "(" << ac->ids.size() << " vs " << ac->expected << ") for container " << ac->container;
	PARSE_ERROR(i18n::trans("Unexpected number of template parameters") + sz.str());
    }

    if( started_containers.size() > 1 )
    {
	started_containers.pop();
    }
    else if (started_containers.size() == 1 )
    {
	actual_container = started_containers.top();
	started_containers.pop();
    }
    else
    {
	PARSE_ERROR(i18n::trans("Cannot finish container without active container"));
    }
}

void start_container( char* const& ct, uint32_t exp )
{
    TRACE_SCOPE(ct,exp);
    expected_token = STRINGIFY(__LINE__);
    d_inf << __FFL__ << endl;
    d_inf << "Container: " << ct << " exp= " << exp<< endl;
    d_dbg << "Starting container " << ct << endl;

    ContainerType* ac = new ContainerType( ct,0, "", false, exp );

    // if an actual container was started, add this to the type list of the container.
    if( started_containers.size() )
    {
	started_containers.top()->ids.push_back(ac);
    }
    else if( actual_container )
    {
	PARSE_ERROR(i18n::trans("cannot start container with actual active container"));
    }

    // add it to the started containers queue.
    started_containers.push(ac);

    set_free(ct);
}

void enum_identifier( char* const& eid )
{
    TRACE_SCOPE(eid);
    expected_token = STRINGIFY(__LINE__);
    enum_identifier( eid, strdup("") );
}

void enum_identifier( char* const& rid, uint32_t ev )
{
    TRACE_SCOPE(rid,ev);
    expected_token = STRINGIFY(__LINE__);
    stringstream eva;
    eva << ev;
    char* evc = strdup(eva.str().c_str());
    enum_identifier(rid,evc);
}

void enum_identifier( char* const& eid, char* const& vid)
{
    TRACE_SCOPE(eid,vid);
    expected_token = STRINGIFY(__LINE__);
//    d_dbg << "enum identifier " << eid << " with value set to " << vid << endl;
    enumdef_list.push_back(EnumDef(eid,vid));

    set_free(eid);
    set_free(vid);
}


void add_enum( char* const& en )
{
    TRACE_SCOPE(en);
    expected_token = STRINGIFY(__LINE__);

    d_dbg << ANSI_GREEN << "enum " << ANSI_NORMAL << en << endl;
    d_dbg << "{" << endl;

    list<EnumDef>::iterator ei(enumdef_list.begin()); 

    for(; ei != enumdef_list.end(); ++ei )
    {
	d_dbg << ei->id << "=" << ei->value << "," << endl;
    }

    d_dbg << "}" << endl;


    Enum* ne = new Enum( en );
    enums.insert(make_pair(en,ne));
    ne->enumdefs = enumdef_list;
    enumdef_list.clear();

    if( actual_class )
    {
	actual_class->add_item(*ne);
    }
    else if ( actual_namespace )
    {
	actual_namespace->add_item(*ne);
    }
    else
    {
	PARSE_ERROR(i18n::trans("Need class or namespace for enum definition"));
    }

    set_free(en);
}

void include( char* const& ad )
{
    TRACE_SCOPE(ad);
    expected_token = STRINGIFY(__LINE__);
    if( actual_namespace )
    {
	PARSE_ERROR(i18n::trans("include directives must be outside namespaces"));
    }

    tu.includes.push_back(ad);

    set_free(ad);
}

void adapt_private( char* const& ad )
{
    TRACE_SCOPE(ad);
    expected_token = STRINGIFY(__LINE__);
    adapt(ad,true);
}

void adapt( char* const& ad, bool priv )
{
    TRACE_SCOPE(ad,priv);
    expected_token = STRINGIFY(__LINE__);
    if( ! actual_class )
    {
	PARSE_ERROR(i18n::trans("cannot adapt non-class"));
    }

    actual_class->adapters.push_back(ad);
    actual_class->adapt_private = priv;
    set_free(ad);
}

void declare_variable( char* const& vt, char* const& vn )
{
    TRACE_SCOPE(vt,vn);
    expected_token = STRINGIFY(__LINE__);
    d_dbg << ANSI_GREEN << vt << " " << ANSI_NORMAL << vn << ";" << endl;

    if( ! actual_class )
    {
	PARSE_ERROR(i18n::trans("Cannot add Variable Declaration without class"));
    }

    if( actual_class->has_functions )
    {
	//XXX
	PARSE_ERROR(i18n::trans("Sorry, mixed data/function objects not yet supported"));
    }

    Type* tp;
    if( actual_container ) 
    {
	tp = actual_container;
	actual_container = 0;
    }
    else
    {
	tp = get_type( vt );
    }

    Declaration* dec = new Declaration( tp, vn, false );
    actual_class->add_item(*dec);

    set_free(vt);
    set_free(vn);
}


void add_typedef( char* const& td, char* const& dt )
{
    TRACE_SCOPE(td,dt);
    expected_token = STRINGIFY(__LINE__);
    d_dbg << "Adding a typedef" << endl;
    d_dbg << "Added typedef " << td << " vs " << dt << endl;
    TypeDef*& ntd = typedefs[td];
    if( ntd )
    {
	PARSE_ERROR("Already have this typedef");
    }

    if( actual_container )
    {
	d_dbg << "Typedef is a container" << endl;
	d_dbg << "TO : " << actual_container->id << endl;
	ntd = new TypeDef(td, actual_container);
	actual_container = 0;
    }
    else
    {
	d_dbg << "Typedef is ordinary type " << dt << endl;
	ntd = new TypeDef(td, get_type(dt));
    }

    set_free(td);
    set_free(dt);
}


void version_specifier( int vma )
{
    TRACE_SCOPE(vma);
    expected_token = STRINGIFY(__LINE__);
    d_dbg << "Parsed Version String : " << vma << endl;
    actual_version_specifier = (vma<<16);
    d_dbg << "Version Specifier Code: " << actual_version_specifier << endl;
}

void version_specifier( int vma, int vmi )
{
    TRACE_SCOPE(vma,vmi);
    expected_token = STRINGIFY(__LINE__);
    d_dbg << "Parsed Version String : " << vma << "." << vmi << endl;
    actual_version_specifier = (vma<<16) | (vmi<<8);
    d_dbg << "Version Specifier Code: " << actual_version_specifier << endl;
}

void version_specifier( int vma, int vmi, int vre )
{
    TRACE_SCOPE(vma,vmi,vre);
    expected_token = STRINGIFY(__LINE__);
    d_dbg << "Parsed Version String : " << vma << "." << vmi << "." << vre << endl;
    actual_version_specifier = (vma<<16) | (vmi<<8) | (vre);
    d_dbg << "Version Specifier Code: " << hex << actual_version_specifier << endl;
}

}    
}
}
