//
// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
//
// The contents of this file are covered under the licence agreement in the
// file "LICENCE" distributed with Cforall.
//
// MultRet.cc -- 
//
// Author           : Rodolfo G. Esteves
// Created On       : Mon May 18 07:44:20 2015
// Last Modified By : Peter A. Buhr
// Last Modified On : Mon Jun  8 14:54:44 2015
// Update Count     : 2
//

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

#include "MultRet.h"
#include "SynTree/Statement.h"
#include "SynTree/Expression.h"
#include "SynTree/Declaration.h"
#include "SynTree/Type.h"

namespace Tuples {
	MVRMutator::MVRMutator() : newVars( 0 ), newCode( 0 ) {
	}

	MVRMutator::~MVRMutator() {
	}

	int MVRMutator::curVal = 0;

	Statement *MVRMutator::mutate( ExprStmt *exprStmt ) {
		MVRMutator toplevel;
		exprStmt->set_expr( maybeMutate( exprStmt->get_expr(), toplevel ) );

		if ( toplevel.hasCode() ) {
			assert ( toplevel.getVars() != 0 );

			typedef std::list<Statement *> Statements;

			CompoundStmt *code = new CompoundStmt( std::list< Label >() );

			// copy variables
			Statements &vars = toplevel.getVars()->get_kids();
			for ( Statements::iterator i = vars.begin(); i != vars.end(); i++ )
				code->get_kids().push_back( *i );

			// copy statements
			Statements &block = toplevel.getCode()->get_kids();
			for ( Statements::iterator i = block.begin(); i != block.end(); i++ )
				code->get_kids().push_back( *i );

			return code;
		} else
			return exprStmt;
	}

	Expression *MVRMutator::mutate( ApplicationExpr *appExpr ) {
		// appExpr->set_function(  maybeMutate( appExpr->get_function(), *this ) );
		bool mulretp = false;
		VariableExpr *funname;
		if ( (funname = dynamic_cast<VariableExpr *>(appExpr->get_function())) == 0 ) goto DoArgs;

		FunctionDecl *fundecl;
		if ((fundecl = dynamic_cast<FunctionDecl *>(funname->get_var())) == 0) goto DoArgs;
		{
			typedef std::list<DeclarationWithType*> RetType;

			RetType &rets = fundecl->get_functionType()->get_returnVals();
			if ( rets.size() <= 1 ) goto DoArgs;
			mulretp = true;

			if ( newVars == 0 )
				newVars = new CompoundStmt( std::list<Label>(0) );

			for (RetType::iterator i = rets.begin() ; i != rets.end(); i++) {
				DeclStmt *arg = newVar( *i );
				newVars->get_kids().push_back( arg );
				add_pending( arg->get_decl() );
			} // for
		}

	  DoArgs:
		// mutate the argument list
		typedef std::list< Expression *> Exprs;
		Exprs &args = appExpr->get_args();
		std::list< Expression * > newArgs;
		for ( Exprs::iterator i = args.begin(); i != args.end(); i++ ) {
			MVRMutator next;
			Expression *mutated = (*i)->acceptMutator( next );

			if ( next.hasCode() ) {
				// merge new vars and bodies
				typedef std::list< Statement * > Stmts;
				Stmts &vars = next.getVars()->get_kids();
				Stmts &block = next.getCode()->get_kids();

				if (newVars == 0)
					newVars = new CompoundStmt( std::list< Label >() );
				for ( Stmts::iterator i = vars.begin(); i != vars.end(); i++ )  // std::splice? -- need to append lists
					newVars->get_kids().push_back( *i );

				if (newCode == 0)
					newCode = new CompoundStmt( std::list< Label >() );
				for ( Stmts::iterator i = block.begin(); i != block.end(); i++ )
					newCode->get_kids().push_back( *i );
			} // if

			if ( next.hasResults() ) {
				Exprs &res = next.get_results();
				for ( Exprs::iterator i = res.begin(); i != res.end(); i++ )
					newArgs.push_back( *i );
			} else
				newArgs.push_back( mutated );
		}

		appExpr->get_args() = newArgs;  // new argument list

		if ( mulretp ) {
			// add 'out' parameters
			if ( ! argsToAdd.empty() )
				for (std::list< Expression *>::iterator i = argsToAdd.begin(); i != argsToAdd.end(); i++)
					(appExpr->get_args()).push_back( *i );
			// clear 'out' parameters ( so that the list can be reused -- substitute by auto_ptr later? )

			if (newCode == 0)
				newCode = new CompoundStmt( std::list<Label>(0) );
		} // if

		// add to block of code
		if ( newCode != 0 )
			newCode->get_kids().push_back( new ExprStmt( std::list<Label>(), appExpr ) );

		return appExpr;
	}

	// Auxiliary function to generate new names for the `output' parameters
	DeclStmt *MVRMutator::newVar( DeclarationWithType *reqDecl ) {
		// std::ostringstream os;
		// os << "__" << curVal++ << "__";// << std::ends;

		ObjectDecl *decl;
		if ((decl = dynamic_cast<ObjectDecl *>( reqDecl )) != 0)
			// return new DeclStmt( new ObjectDecl( std::string (os.str(), os.pcount()), );
			return new DeclStmt( std::list<Label>(), decl );
		else
			return 0;
	}

	void MVRMutator::add_pending( Declaration *decl ) {
		ObjectDecl *obj;
		if ( (obj = dynamic_cast< ObjectDecl * >( decl )) == 0 ) return;

		VariableExpr *var = new VariableExpr(obj, 0 );
		results.push_back( var ); // probably change this name to newResults or something
		argsToAdd.push_back( new AddressExpr( var ) );
		return;
	}
}

// Local Variables: //
// tab-width: 4 //
// mode: c++ //
// compile-command: "make install" //
// End: //
