#include "FunctionChecker.h"
#include "FunctionFixer.h"
#include "SemanticError.h"

#include <algorithm>
#include <iostream>
#include <cassert>

namespace Tuples {
  using namespace std;

  void checkFunctions( std::list< Declaration * > translationUnit )
  {
    FunctionChecker fchk(true);
    TupleDistrib td;
    FunctionFixer ff;

    mutateAll( translationUnit , fchk );
    mutateAll( translationUnit , ff );
    mutateAll( translationUnit , td );
    return;
  }

  FunctionChecker::FunctionChecker( bool _topLevel, UniqueName *_nameGen ) : topLevel( _topLevel ), nameGen( _nameGen ) {
    if( topLevel) {
      assert( !nameGen );
      nameGen = new UniqueName("_MVR_");
    } else
      assert( nameGen );
  }

  FunctionChecker::~FunctionChecker() {
    if( topLevel) {
      delete nameGen;
      nameGen = 0;
    }
  }

  Statement* FunctionChecker::mutate(ExprStmt *exprStmt) {
    exprStmt->set_expr( maybeMutate( exprStmt->get_expr(), *this ) );
    if ( !tempExpr.empty() ) {
      assert ( !temporaries.empty() );
      CompoundStmt *newBlock = new CompoundStmt( std::list< Label >() );
      // declarations
      for( std::list< ObjectDecl *>::iterator d = temporaries.begin(); d != temporaries.end(); ++d )
	newBlock->get_kids().push_back( new DeclStmt( std::list<Label>(), *d ) );
      // new expression statements
      for( std::list< Expression *>::iterator e = tempExpr.begin(); e != tempExpr.end(); ++e )
	newBlock->get_kids().push_back( new ExprStmt( std::list<Label>(), *e ) );

      newBlock->get_kids().push_back( exprStmt );
      return newBlock;
    } else
      return exprStmt;
  }

  Expression* FunctionChecker::mutate(ApplicationExpr *applicationExpr) {
    if ( topLevel )
      ; // In top level of Functionchecker

    if ( applicationExpr->get_results().size() > 1 ) {
      for ( std::list< Type *>::iterator res = applicationExpr->get_results().begin(); res != applicationExpr->get_results().end(); res++ )
	temporaries.push_back( new ObjectDecl(nameGen->newName(),Declaration::Auto,LinkageSpec::AutoGen, 0, (*res)->clone(), 0 ) );

      assert( ! temporaries.empty() );
    }

    applicationExpr->set_function(  maybeMutate( applicationExpr->get_function(), *this ) );

    std::list< Expression * > newArgs;
    for( std::list< Expression *>::iterator e = applicationExpr->get_args().begin(); e != applicationExpr->get_args().end(); ++e ) {
      FunctionChecker rec( false, nameGen );
      (*e)->acceptMutator( rec );

      if ( !rec.temporaries.empty() ) {
	TupleExpr *lhs = new TupleExpr;
	std::list< Expression * > &tmem = lhs->get_exprs();
	for( std::list<ObjectDecl *>::iterator d = rec.temporaries.begin();  d != rec.temporaries.end(); ++d ) {
	  tmem.push_back( new VariableExpr( *d ) );
	  newArgs.push_back( new VariableExpr( *d ) );
	}

	// construct tuple assignment
	std::list<Expression *> args;
	args.push_back( new AddressExpr(lhs) );
	args.push_back( *e );
	tempExpr.push_back( new UntypedExpr( new NameExpr("?=?"), args ) );

	temporaries.splice( temporaries.end(), rec.temporaries );
      } else
	newArgs.push_back( *e );
      // percolate to recursive calls
    }

    applicationExpr->get_args().clear();
    std::copy( newArgs.begin(), newArgs.end(), back_inserter(applicationExpr->get_args()) );

    return applicationExpr;
  }

  Expression* TupleDistrib::mutate(UntypedExpr *expr) {
    if(  NameExpr *assgnop = dynamic_cast< NameExpr * >(expr->get_function()) ) {
      if( assgnop->get_name() == std::string("?=?") ) {
	std::list<Expression *> &args = expr->get_args();
	assert(args.size() == 2);
	//if args.front() points to a tuple and if args.back() is already resolved
	if( AddressExpr *addr = dynamic_cast<AddressExpr *>(args.front()) )
	  if( TupleExpr *lhs = dynamic_cast<TupleExpr *>(addr->get_arg()) )
	    if( ApplicationExpr *rhs = dynamic_cast<ApplicationExpr *>( args.back() ) ) {
	      for ( std::list<Expression *>::iterator tc = lhs->get_exprs().begin(); tc != lhs->get_exprs().end(); ++tc )
		rhs->get_args().push_back( new AddressExpr( *tc ) );
	      return rhs; // XXX
	    }
      } else
	assert(false); // It's not an assignment, shouldn't be here
    }
    return expr;
  }

} // namespace Tuples
