#include <list>
#include <vector>
#include <cassert>
#include <algorithm>

#include "FunctionFixer.h"

namespace Tuples {
  DeclarationWithType *FunctionFixer::mutate( FunctionDecl *functionDecl ) {
    functionDecl->set_functionType( maybeMutate( functionDecl->get_functionType(), *this ) );
    mutateAll( functionDecl->get_oldDecls(), *this );
    functionDecl->set_statements( maybeMutate( functionDecl->get_statements(), *this ) );
    index.visit( functionDecl );
    rets.clear();
    return functionDecl;
  }

  Type *FunctionFixer::mutate( FunctionType *functionType )
  {
    typedef std::list< DeclarationWithType * >  Decls;

    if ( functionType->get_returnVals().size() <= 1 )  return functionType;
    std::copy( functionType->get_returnVals().begin(), functionType->get_returnVals().end(), back_inserter(rets) );

    Type::Qualifiers qual;
    for ( Decls::iterator i = rets.begin(); i != rets.end(); i++ ) {
      (*i)->set_type( new PointerType( qual, (*i)->get_type() ) );
      functionType->get_parameters().push_back( *i );
    }

    functionType->get_returnVals() = *(new std::list< DeclarationWithType * >());

    functionType->set_isVarArgs( false );
    return functionType;
  }

  Statement *FunctionFixer::mutate( ReturnStmt *retStmt )
  {
    bool tupleReturn = false;
    Expression *rhs = 0;
    // also check if returning multiple values
    if( CastExpr *cst = dynamic_cast<CastExpr *>( retStmt->get_expr() ) ) {
      if( ApplicationExpr *app = dynamic_cast<ApplicationExpr *>( cst->get_arg() ) ) {
	if( app->get_results().size() > 1 ) { // doesn't need to be ApplicationExpr
	  tupleReturn = true;
	  rhs = app;
	}
      } else if( TupleExpr *t = dynamic_cast<TupleExpr *>( cst->get_arg() ) ) {
	tupleReturn = true;
	assert( rets.size() == t->get_exprs().size() ); // stupid check, resolve expression
	rhs = t;
      }

      if( tupleReturn ) {
	assert ( rhs != 0 );
	std::list< Expression * > lhs;
	for( std::list< DeclarationWithType * >::iterator d = rets.begin(); d != rets.end(); ++d ) {
	  std::list<Expression *> largs;
	  largs.push_back(new VariableExpr( *d ));
	  Expression *exp = ResolvExpr::resolveInVoidContext( new CastExpr( new UntypedExpr(new NameExpr("*?"), largs), (*d)->get_type()),
								       index );
	  lhs.push_back(exp);
	}
	std::list< Expression * > args;
	TupleExpr *tlhs = new TupleExpr; tlhs->set_exprs( lhs );
	args.push_back( new AddressExpr(tlhs) );
	args.push_back(rhs);

	return new ExprStmt( std::list< Label>(), new UntypedExpr( new NameExpr("?=?"), args ) );
      }
    }
    /*
    else
      std::cerr << "Empty return statement" << std::endl;
    */

    return retStmt;
  }

  Expression* FunctionFixer::mutate( VariableExpr *variableExpr ) {
    if ( rets.empty() ) return variableExpr;
    mutateAll( variableExpr->get_results(), *this );
    if( std::find( rets.begin(), rets.end(), variableExpr->get_var() ) != rets.end() )
//      if ( PointerType *ptr = dynamic_cast<PointerType *>(variableExpr->get_var()->get_type()) ) {
      if ( dynamic_cast<PointerType *>(variableExpr->get_var()->get_type()) != 0 ) {
	std::list<Expression *> largs;
	largs.push_back( new AddressExpr(variableExpr) );
	  Expression *expr = ResolvExpr::resolveInVoidContext( /*new CastExpr(*/new UntypedExpr( new NameExpr( "*?" ), largs )/*,
																 ptr->get_base()),*/, index);
	  if( ApplicationExpr *app = dynamic_cast< ApplicationExpr * >( expr ) ) {
	    assert( app->get_args().size() == 1 );
	    app->get_args().pop_front();
	    app->get_args().push_back( variableExpr );
	    return app;
	  }
      }
    return variableExpr;
  }

  /*
  Expression* FunctionFixer::mutate(ApplicationExpr *applicationExpr) {
    std::cerr << "In Application Expression" << std::endl;
    mutateAll( applicationExpr->get_results(), *this );
    applicationExpr->set_function(  maybeMutate( applicationExpr->get_function(), *this ) );
    mutateAll( applicationExpr->get_args(), *this );
    return applicationExpr;
  }
  */
} // namespace Tuples
