//
// 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.
//
// FunctionFixer.cc -- 
//
// Author           : Rodolfo G. Esteves
// Created On       : Mon May 18 07:44:20 2015
// Last Modified By : Peter A. Buhr
// Last Modified On : Mon May 18 12:02:22 2015
// Update Count     : 1
//

#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 );
		} // for

		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

			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);
				} // for
				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 ) );
			} // if
		} // if
		/*
		  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
// Local Variables: //
// tab-width: 4 //
// mode: c++ //
// compile-command: "make install" //
// End: //
