#include <strstream>
#include <cassert>

#include "GenType.h"
#include "CodeGenerator2.h"
#include "SynTree/Visitor.h"
#include "SynTree/Type.h"
#include "SynTree/Expression.h"

namespace CodeGen {
    class GenType : public Visitor {
      public:
	GenType( const std::string &typeString );
	std::string get_typeString() const { return typeString; }
	void set_typeString( const std::string &newValue ) { typeString = newValue; }
  
	virtual void visit( FunctionType *funcType );
	virtual void visit( VoidType *voidType );
	virtual void visit( BasicType *basicType );
	virtual void visit( PointerType *pointerType );
	virtual void visit( ArrayType *arrayType );
	virtual void visit( StructInstType *structInst );
	virtual void visit( UnionInstType *unionInst );
	virtual void visit( EnumInstType *enumInst );
	virtual void visit( TypeInstType *typeInst );
  
      private:
	void handleQualifiers( Type *type );
	void genArray( const Type::Qualifiers &qualifiers, Type *base, Expression *dimension, bool isVarLen, bool isStatic );
  
	std::string typeString;
    };

    std::string genType( Type *type, const std::string &baseString ) {
	GenType gt( baseString );
	type->accept( gt );
	return gt.get_typeString();
    }

    GenType::GenType( const std::string &typeString ) : typeString( typeString ) {}

    void GenType::visit( VoidType *voidType ) {
	typeString = "void " + typeString;
	handleQualifiers( voidType );
    }

    void GenType::visit( BasicType *basicType ) {
	BasicType::Kind kind = basicType->get_kind();
	assert( 0 <= kind && kind < BasicType::NUMBER_OF_BASIC_TYPES );
	typeString = std::string( BasicType::typeNames[kind] ) + " " + typeString;
	handleQualifiers( basicType );
    }

    void GenType::genArray( const Type::Qualifiers &qualifiers, Type *base, Expression *dimension, bool isVarLen, bool isStatic ) {
	std::ostrstream os;
	if ( typeString != "" ) {
	    if ( typeString[ 0 ] == '*' ) {
		os << "(" << typeString << ")";
	    } else {
		os << typeString;
	    } // if
	} // if
	os << "[";

	if ( isStatic ) {
	    os << "static ";
	} // if
	if ( qualifiers.isConst ) {
	    os << "const ";
	} // if
	if ( qualifiers.isVolatile ) {
	    os << "volatile ";
	} // if
	if ( qualifiers.isRestrict ) {
	    os << "__restrict ";
	} // if
	if ( isVarLen ) {
	    os << "*";
	} // if
	if ( dimension != 0 ) {
	    CodeGenerator2 cg( os );
	    dimension->accept( cg );
	} // if
	os << "]";

	typeString = std::string( os.str(), os.pcount() );
  
	base->accept( *this );
    }

    void GenType::visit( PointerType *pointerType ) {
	assert( pointerType->get_base() != 0);
	if ( pointerType->get_isStatic() || pointerType->get_isVarLen() || pointerType->get_dimension() ) {
	    genArray( pointerType->get_qualifiers(), pointerType->get_base(), pointerType->get_dimension(), pointerType->get_isVarLen(), pointerType->get_isStatic() );
	} else {
	    handleQualifiers( pointerType );
	    if ( typeString[ 0 ] == '?' ) {
		typeString = "* " + typeString;
	    } else {
		typeString = "*" + typeString;
	    } // if
	    pointerType->get_base()->accept( *this );
	} // if
    }

    void GenType::visit( ArrayType *arrayType ){
	genArray( arrayType->get_qualifiers(), arrayType->get_base(), arrayType->get_dimension(), arrayType->get_isVarLen(), arrayType->get_isStatic() );
    }

    void GenType::visit( FunctionType *funcType ) {
	std::ostrstream os;

	if ( typeString != "" ) {
	    if ( typeString[ 0 ] == '*' ) {
		os << "(" << typeString << ")";
	    } else {
		os << typeString;
	    } // if
	} // if
  
	/************* parameters ***************/

	const std::list<DeclarationWithType *> &pars = funcType->get_parameters();

	if ( pars.empty() ) {
	    if ( funcType->get_isVarArgs() ) {
		os << "()";
	    } else {
		os << "(void)";
	    } // if
	} else {
	    CodeGenerator2 cg( os );
	    os << "(" ;

	    cg.genCommaList( pars.begin(), pars.end() );

	    if ( funcType->get_isVarArgs() ){
		os << ", ...";
	    } // if
	    os << ")";
	} // if
  
	typeString = std::string( os.str(), os.pcount() );

	if ( funcType->get_returnVals().size() == 0 ) {
	    typeString = "void " + typeString;
	} else {
	    funcType->get_returnVals().front()->get_type()->accept( *this );
	} // if
    }

    void GenType::visit( StructInstType *structInst )  {
	typeString = "struct " + structInst->get_name() + " " + typeString;
	handleQualifiers( structInst );
    }

    void GenType::visit( UnionInstType *unionInst ) {
	typeString = "union " + unionInst->get_name() + " " + typeString;
	handleQualifiers( unionInst );
    }

    void GenType::visit( EnumInstType *enumInst ) {
	typeString = "enum " + enumInst->get_name() + " " + typeString;
	handleQualifiers( enumInst );
    }

    void GenType::visit( TypeInstType *typeInst ) {
	typeString = typeInst->get_name() + " " + typeString;
	handleQualifiers( typeInst );
    }

    void GenType::handleQualifiers( Type *type ) {
	if ( type->get_isConst() ) {
	    typeString = "const " + typeString;
	} // if
	if ( type->get_isVolatile() ) {
	    typeString = "volatile " + typeString;
	} // if
	if ( type->get_isRestrict() ) {
	    typeString = "__restrict " + typeString;
	} // if
    }
} // namespace CodeGen
