#include <ctime>
#include <cstdlib>

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

#include "AssignExpand.h"

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

namespace Tuples {
  AssignExpander::AssignExpander() : temporaryNamer("__tpl") {}

  Statement *AssignExpander::mutate( ExprStmt *exprStmt ) {
    replace.clear();
    extra.clear();
    extra2.clear();
    exprStmt->set_expr( maybeMutate( exprStmt->get_expr(), *this ) );

    CompoundStmt *newSt = 0;
    if (! extra.empty() ) {
      if ( !newSt )
	newSt= new CompoundStmt(std::list<Label>());

      newSt->get_kids().splice(newSt->get_kids().end(), extra);
    }

    if (! extra2.empty() ) {
      if ( !newSt )
	newSt= new CompoundStmt(std::list<Label>());

      newSt->get_kids().splice(newSt->get_kids().end(), extra2);
    }

    if (! replace.empty() ) {
      if ( !newSt )
	newSt= new CompoundStmt(std::list<Label>());

      for ( std::list<Expression *>::iterator r = replace.begin(); r != replace.end(); r++ )
	newSt->get_kids().push_back( new ExprStmt( std::list<Label>(), *r ));
    }

    if( newSt ) return newSt; else return exprStmt;
  }

  Expression *AssignExpander::mutate( SolvedTupleExpr *tupleExpr ) {
    /* 
    std::list<Expression *> &exprs = tupleExpr->get_exprs();

    if ( tupleExpr->get_type() == SolvedTupleExpr::MASS ) {
      // extract lhs of assignments, assert that rhs is the same, create temporaries
      assert (! exprs.empty());
      ApplicationExpr *ap1 = dynamic_cast< ApplicationExpr * >( exprs.front() );
      std::list<Expression *> &args = ap1->get_args();
      assert(args.size() == 2);
      std::list<Type *> &temp_types = args.back()->get_results();
      assert(temp_types.size() == 1);
      extra.push_back(new DeclStmt( std::list<Label>(), new ObjectDecl(temporaryNamer.newName(), Declaration::Auto, LinkageSpec::C, 0, temp_types.front(), 0 ) ));

      for ( std::list<Expression *>::iterator e = exprs.begin(); e != exprs.end(); e++ ) {
	ApplicationExpr *ap = dynamic_cast< ApplicationExpr * >( *e );
	assert( ap != 0 );
	replace.push_back(ap);
      }

      return tupleExpr;
    } else if ( tupleExpr->get_type() == SolvedTupleExpr::MULTIPLE ||
		   tupleExpr->get_type() == SolvedTupleExpr::MASS ) */ {
      std::list<Expression *> &comps = tupleExpr->get_exprs();
      for( std::list<Expression *>::iterator i = comps.begin(); i != comps.end(); ++i ) {
	std::list<Statement *> decls;
	std::list<Statement *> temps;
	std::list<Statement *> assigns;
	if( ApplicationExpr *app = dynamic_cast< ApplicationExpr * >(*i) ) {
	  assert( app->get_args().size() == 2 );

	  Expression *lhsT = app->get_args().front();
	  Expression *rhsT = app->get_args().back();
	  // after the round of type analysis this should be true
	  assert( lhsT->get_results().size() == 1 );
	  assert( rhsT->get_results().size() == 1 );
	  // declare temporaries
	  ObjectDecl *lhs = new ObjectDecl( temporaryNamer.newName("_lhs_"), Declaration::NoStorageClass, LinkageSpec::Intrinsic, 0,
					    lhsT->get_results().front(), 0 );
	  decls.push_back( new DeclStmt( std::list< Label >(), lhs ) );
	  ObjectDecl *rhs = new ObjectDecl( temporaryNamer.newName("_rhs_"), Declaration::NoStorageClass, LinkageSpec::Intrinsic, 0,
					    rhsT->get_results().front(), 0);
	  decls.push_back( new DeclStmt( std::list< Label >(), rhs ));


	  // create temporary for lhs, assign address
	  UntypedExpr *assgnL = new UntypedExpr( new NameExpr( "?=?" ) );
	  assgnL->get_args().push_back( new VariableExpr( lhs ) );
	  assgnL->get_args().push_back( lhsT );
	  temps.push_back( new ExprStmt(std::list<Label>(), assgnL) );

	  // create temporary for rhs, assign value
	  UntypedExpr *assgnR = new UntypedExpr( new NameExpr( "?=?" ) );
	  assgnR->get_args().push_back( new VariableExpr( rhs ) );
	  assgnR->get_args().push_back( rhsT );
	  temps.push_back( new ExprStmt(std::list<Label>(), assgnR) );

	  // assign rhs to lhs
	  UntypedExpr *assgn = new UntypedExpr( new NameExpr( "?=?" ) );
	  UntypedExpr *deref = new UntypedExpr( new NameExpr( "*?" ) );
	  deref->get_args().push_back( new VariableExpr( lhs ) );
	  assgn->get_args().push_back( deref );
	  assgn->get_args().push_back( new VariableExpr( rhs ) );
	  assigns.push_back( new ExprStmt(std::list<Label>(), assgn) );

	} else
	  throw CompilerError("Solved Tuple should contain only assignment statements");
	  
	extra.splice( extra.begin(), decls );
	extra.splice( extra.end(), temps );
	extra2.splice( extra2.end(), assigns );
      }

      return tupleExpr;
    }

    throw 0; // shouldn't be here
  }


} // namespace Tuples

