#include <cassert>

#include "Expression.h"
#include "Declaration.h"
#include "Type.h"
#include "TypeSubstitution.h"
#include "utility.h"


ParamEntry::ParamEntry( const ParamEntry &other )
    : decl( other.decl ), actualType( maybeClone( other.actualType ) ), formalType( maybeClone( other.formalType ) ), expr( maybeClone( other.expr ) )
{
}

ParamEntry &
ParamEntry::operator=( const ParamEntry &other )
{
    if( &other == this ) return *this;
    decl = other.decl;
    actualType = maybeClone( other.actualType );
    formalType = maybeClone( other.formalType );
    expr = maybeClone( other.expr );
    return *this;
}

ParamEntry::~ParamEntry()
{
    delete actualType;
    delete formalType;
    delete expr;
}

ApplicationExpr::ApplicationExpr( Expression *funcExpr )
    : function( funcExpr )
{
    PointerType *pointer = dynamic_cast< PointerType* >( funcExpr->get_results().front() );
    assert( pointer );
    FunctionType *function = dynamic_cast< FunctionType* >( pointer->get_base() );
    assert( function );
    
    for( std::list< DeclarationWithType* >::const_iterator i = function->get_returnVals().begin(); i != function->get_returnVals().end(); ++i ) {
	get_results().push_back( (*i)->get_type()->clone() );
    }
}

ApplicationExpr::ApplicationExpr( const ApplicationExpr &other )
    : Expression( other ), function( maybeClone( other.function ) ), inferParams( other.inferParams )
{
    cloneAll( other.args, args );
}

ApplicationExpr::~ApplicationExpr()
{
    delete function;
    deleteAll( args );
}

void 
ApplicationExpr::print( std::ostream &os, int indent ) const
{
    os << std::string( indent, ' ' ) << "Application of" << std::endl;
    function->print( os, indent+2 );
    if( !args.empty() ) {
	os << std::string( indent, ' ' ) << "to arguments" << std::endl;
	printAll( args, os, indent+2 );
    }
    if( !inferParams.empty() ) {
	os << std::string(indent, ' ') << "with inferred parameters:" << std::endl;
	for( InferredParams::const_iterator i = inferParams.begin(); i != inferParams.end(); ++i ) {
	    os << std::string(indent+2, ' ');
	    Declaration::declFromId( i->second.decl )->printShort( os, indent+2 );
	    os << std::endl;
	}
    }
    Expression::print( os, indent );
}

