//________________________________________________________________________________________
CCUStmt es_global; // containts global declarations
//________________________________________________________________________________________
CCUStmt simplify_stmt(stmt)
{
	switch (stmt)	
	case "label: s" // labeled statement, label may be IDENTIFIER
			CCUStmt es = simplify_stmt(label);			
			return CCUStmt(es->d, "label: es->s", es->start_labels, es->end_labels);			
	case "typeName var = e" // declaration + initialization
	case "typeName var"		// simple declaration 
			CCUStmt ret = simplify_decl("var");
			if ("var" is a static variable in a function "func")
			{
				//rename "var" to "func_var" and make it global
				es_global = (es_global + ret);
				return CCUStmt(); //empty
			}			
			else return ret;		
	case "{ decls stats }"
		CCUStmt es = new CCUStmt;
		for all (stmt in block) {
			es1 = simplify_stmt(stmt);
			es = es +  es1;
		}
		return CCUStmt(CoreDeclarations(), "{ es->d, es->s }", es->start_labels, es->end_labels);
	case "switch (e) s"								return simplify_switch(stmt);
	case "if (e) s1"
		CCUCond es = simplify_bool_exp("e")->exp2cond();	
		CCUStmt es1 = simplify_stmt("s1");
		return new(core_manager) CCUStmt_((es->d + es1->d), 
				(es->s + "es->trues:" + es1->s + "es->falses: ;"),
				es1->start_labels, es1->end_labels);
	case "if (e) s1 else s2"						
		CCUCond es = simplify_bool_exp("e")->exp2cond();	
		CCUStmt es1 = simplify_stmt("s1");
		CCUStmt es2 = simplify_stmt("e2");
		l1 = new label();		
		return CCUStmt_((es->d + es1->d + es2->d), 
				(es->s + "es->trues:" + es1->s + "goto l1" + "es->falses:" + es2->s + "l1: ;"),
				(es1->start_labels + es2->start_labels),
				(es1->end_labels + es2->end_labels));
	case "while (e) s"
		CCUCond es = simplify_bool_exp("e")->exp2cond();
		CCUStmt es1 = simplify_stmt("s");	
		return new CCUStmt_((es->d + es1->d), ("es1->start_labels:"+"l1:" + es->s + es->trues + es1->s + "goto l1" + es->falses + "es1->end_labels:"));
	case "do s while(e)"							
		CCUCond es = simplify_bool_exp("e")->exp2cond();
		CCUStmt es1 = simplify_stmt("s");
			return CCUStmt_((es->d + es1->d), ("es->trues:" + es1->s + "es1->start_labels:" + es->s + "es->falses:" + "es1->end_labels:"));
	case "for (e1; e2; e3) s"						
		CCUStmt es1 = simplify_stmt("e1");	
		CCUCond es2 = simplify_bool_exp("e2")->exp2cond();
		CCUExp es3 = simplify_exp("e3")->cond2exp();
		CCUStmt es4 = simplify_stmt("s");
		l1 = new label();
		return new CCUStmt_((es1->d + es2->d + es3->d + es4->d), ("es1->start_labels:" + es1->s + "es1->end_labels:" + "l1:"+ es2->s + es2->s + "es2->trues:" + es4->s + "es4->start_labels:" + es3->s + "goto l1" + "es2->falses:" + "es4->end_labels:")

	case "return e"
			CCUExp es = simplify_exp(e)->cond2exp();
			CCUExp ccu_sym1 = create_temp(es->exp, ccPrimary);
			return CCUStmt( (es->d + ccu_sym1->d), 
							(es->s + ccu_sym1->s + "return ccu_sym1->exp"));			
	case "return" // no value		
			return CCUStmt(CoreDeclarations(), "return");
	case "goto e"	
			return CCUStmt(CoreDeclarations(), "goto l");
	case "continue"
			l1 = new label;
			return CCUStmt(CoreDeclarations(), "goto l1", LabelList(l1), LabelList());
	case "break"					
			l1 = new label;			
			return CCUStmt(CoreDeclarations(), "goto l1", LabelList(), LabelList(l1));
	case "exp" // expression
			CCUExp es = simplify_exp(exp)->cond2exp();			
			// if function call with no return, convert it for expression to statement
			if (es->exp is function call) 
				return CCUStmt(es->d, (es->s + "es->exp()");
			else 
				return CCUStmt(es->d, es->s);
	case ";" // empty stmt
			return	CCUStmt();
}
//________________________________________________________________________________________
// always returns EXP (lvalue is an object, so it can't be conditional)
CCUExp Simp::simplify_lvalue(exp)
{
	switch (exp)
	case "var" // IDENTIFIER
			return CCUExp(var);		
	case "e[i]"	return simplify_lvalue("*(e + i)");		
	case "e.f"			
			CCUExp es = simplify_lvalue("e"); //e is lvalue, result can be name or lhs only						
			return CCUExp(es->d, es->s, "es->exp"."f");
	case "e->f"	return simplify_lvalue("(*e).f");
	case "*e"
			if ("e" == "&e1") return simplify_lvalue(e1);
			else {
				CCUExp es = simplify_exp(e)->cond2exp();
				if ( es->exp == "&e2" )	return CCUExp(es->d, es->s, e2);
				else return create_deref(es);
			}
}
//________________________________________________________________________________________
CCU Simp::simplify_exp(exp)
{
	switch (exp)  	
	case "c" /* CONSTANT */	return new(core_manager) CCUExp_("c");
	case "var" /* IDENTIFIER */	return CCUExp_("var");
	case "e[i]"	return simplify_exp("*(e + i)");
	case "func(arglist)"
			CCUExp es2 = simplify_exp("func")->cond2exp(*this, scope);
			CCUExp es3 = new CCUExp();
			CCUExp es_tmp = new CCUExp();
			for all (exp is "arglist") {
				es3 = simplify_exp(exps)->cond2exp();				
				es_tmp = create_temp(es3->exp, ccPrimary);
				es3->d = es3->d + es_tmp->d;
				es3->s = es3->s + es_tmp->s;
				es3->exp = es_tmp->exp;
				es1->d = es1->d + es3->d;
				es1->s = es1->s + es3->s;
				es1->arglist = es1->arglist + "," + es->exp;
			}
			CCUExp ccu_sym1 = create_temp(es2->exp, ccName);			
			return CCUExp_((es2->d + es1->d + ccu_sym1->d), (es2->s + es1->s + ccu_sym1->s), 
				"ccu_sym1->exp(es1->arglist)");	
	case "e.f"									
			CCUExp es = simplify_exp("e")->cond2exp();
			if (IsCore(es->exp, ccDeref)) return CCUExp(es->d, es->s, "es->exp"."f");			
			else {
				CCUExp ccu_sym1 = create_temp(es->exp, ccName);
				return CCUExp_((es->d + ccu_sym1->d), (es->s + ccu_sym1->s), "ccu_sym1->exp"."f");
			}
	case "e->f"	return simplify_exp("(*e).f");
	case "e++"	return simplify_post(exp, "+");
	case "e--"	return simplify_post(exp, "-");
	case "++e"	return simplify_pre(exp, "-");
	case "--e"	return simplify_pre(exp, "-");	
	case "(typeName)e" /* cast */ 
		CCUExp es1 = simplify_exp("e")->cond2exp();
		CCUExp ccu_sym = create_temp(es1->exp, ccPrimary);		
		return new CCUExp_((es1->d + ccu_sym->d), (es1->s + ccu_sym->s), "(typeName)ccu_sym->exp");
	case "&e" 			
			if ("e" == "*e1") return simplify_exp("e1");
			else
			{
				CCUExp es1 = simplify_lvalue("e");
				if (es1->exp == "*e2") 									
					return CCUExp(es1->d, es1->s, "e2");
				else if (es1->exp == "(*e2).f"					
					return CCUExp_(es1->d, es1->s, "&((*e2).f)");
				else if (IsCore(es1->exp, ccName)) 							
					return new(core_manager) CCUExp_(es1->d, es1->s, &"es1->exp");			
			}
	case "!e", "||", "&&" return simplify_bool_exp(exp);
	case "~e", "+e", "-e" return simplify_unary("e", operator(exp));
	case "*e"			
			CCUExp es1 = simplify_exp("e")->cond2exp();
			return create_deref(es1);
	case "e1 + e2", "e1 - e2", "e1 * e2", "e1 / e2", "e1 % e2", 
		 "e1 << e", "e1 >> e", "e1 ^ e2", "e1 & e2", "e1 | e2"
	   	 return simplify_binary(e1, e2, operator(exp));
	case "e1 = e2"	
		CCUExp es1 = simplify_lvalue("e1");
		CCUExp es2 = simplify_exp("e2")->cond2exp();
		return create_core_assign(es1, es2);
	case e1 += e2, e1 -= e2, e1 *= e2, e1 /= e2, e1 %= e2, e1 ^= e2, 
		 e1 &= e2, e1 |= e2, e1 <<= e2, e1 >>= e2
		return simplify_op_assign("e1", "e2", operator(exp));
	case "e1?e2:e3" 
		CCUCond es = simplify_bool_exp("e1")->exp2cond();	
		CCUExp es1 = simplify_exp("e2")->cond2exp();
		CCUExp es2 = simplify_exp("e3")->cond2exp();	
		typeName = get_type_of("e1");
		l1 = new label();
		return new CCUExp_((es->d + es1->d + es2->d + "typeName t;"),
			(es->s +  "es->trues:" + es1->s + 
			"t = es1->exp;" + "goto l1;" + "es->falses:" + es2->s + "t = es2->exp;" + l1: ;")	,"t");
	case e1 == e2, e1 != e2, e1 < e2, e1 > e2, e1 <= e2, e1 >= e2
		return simplify_bool_exp(exp);						
	case "(e1, e2)" /* comma expression */	
		CCUExp es1 = simplify_exp(e1)->cond2exp(); 
		CCUExp es2 = simplify_exp(e2)->cond2exp(); //e2
		return CCUExp((es1->d + es2->d), (es1->s + es2->s), es2->exp);	
}
//________________________________________________________________________________________
// returns relational/equality operator on primary_expression
CCU Simp::simplify_bool_exp(AstExpression *exp, SymbolTable *scope)
{
	switch(exp->GetOpCode())
	case "!e" // switch between the exits			
			CCUCond es1 = simplify_bool_exp("e")->exp2cond();			
			return CCUCond(es1->d, es1->s, es1->falses, es1->trues);
	case "e1 || e2"	
			CCUCond es1 = simplify_bool_exp("e1")->exp2cond();
			CCUCond es2 = simplify_bool_exp("e2")->exp2cond();			
			return CCUCond((es1->d + es2->d), (es1->s + "es1->falses:" + es2->s),
							(es1->trues + es2->trues), LabelList(es2->falses));
	case "e1 && e2"
			CCUCond es1 = simplify_bool_exp("e1")->exp2cond();
			CCUCond es2 = simplify_bool_exp("e2")->exp2cond();													
			return CCUCond((es1->d + es2->d), (es1->s + "es1->trues" + es2->s),
				LabelList(es2->trues), (es1->falses +es2->falses) );
	case e1 == e2, e1 != e2, e1 < e2, e1 > e2, e1 <= e2, e1 >= e2
		 return simplify_binary(e1, e2, operator(exp))->exp2cond();
	default:
			return simplify_exp();
}
//________________________________________________________________________________________
CCUStmt Simp::simplify_decl(decl)
{
	switch(decl)
	case "typeName IDENTIFIER" // variable
		 "typeName IDENTIFIER[IDENTIFIER]" // array
		 "typeName IDENTIFIER(paramList)" // function (pointer) declaration			
			return CCUStmt_( remove_const_qualifiers(decl) );
	case "typeName var = e"
		if ("e" is { e1..en } ) // initialization list
		{
			CCUStmt es = simplify_init_list("var", e);
			return new CCUStmt_( ("typeName var;" + es->d), es->s);
		}
		else if ("typeName" is array of char and "e" is constant string) {
			CCUExp es = simplify_exp("e")->cond2exp();
			CCUStmt ret = simplify_init_string("var", es);
			ret->d = ret->d + new_d; return ret;
		}
		else  {
			CCUExp es = simplify_exp("e")->cond2exp();
			return new CCUStmt_(("typeName var;" + es->d), (es->s + "a = es->exp"));				
		}					
}
//________________________________________________________________________________________
CCUStmt Simp::simplify_init_string("var", CCUExp es)
{
	CCUExp es1;
	CCUStmt ret = new CCUStmt_(es->d, es->s);	
	buffer = get_string_buffer(es->exp);
	for all (char "c" in position "i" in buffer) {			
		es1 = simplify_exp("var[i] = c")->cond2exp();		
		ret->s = ret->s + es1->s;
		ret->d = ret->d + es1->d;
	}
	return ret;
}
//________________________________________________________________________________________
CCUExp Simp::simplify_post(exp, bin_op)
{			
	CCUExp es1 = simplify_lvalue(exp);
	CCUExp es2 = simplify_exp(es1->exp)->cond2exp();
	typeName = get_type_of(es2->exp);
	if (! IsCore(es1->exp, ccName))
		return new CCUExp_(  (es1->d + es2->d + "typeName t1;" + "typeName t2;"), 
				(es1->s + es2->s + "t1 = es2->exp;" + "t2 = t1 bin_op 1;" + "es1.exp = t2;"), "t1");
	else
		return new CCUExp_((es1->d + es2->d + "typeName t1;"), 
				(es1->s + es2->s + "t1 = es2->exp" + "es1->exp = t1 bin_op 1"), "t1");
}
//________________________________________________________________________________________
CCUExp Simp::simplify_pre(exp, bin_op)
{
	CCUExp es1 = simplify_lvalue(exp);
	CCUExp es2 = simplify_exp(es1->exp)->cond2exp();
	typeName = get_type_of(es2->exp);	
	if (!IsCore(es1->exp, ccName))	
		return new CCUExp_( (es1->d + es2->d + "typeName t1;" + "typeName t2"), 
			(es1->s + es2->s + "t1 = es2->exp;" + "t2 = t1 bin_op 1;" + "es1->exp = t2;"), "t2");
	else
		return new CCUExp_((es1->d + es2->d + "typeName t1"), 
			(es1->s + es2->s + "es1->exp = es2->exp + 1;" + "t1 = es1->exp;"), "t1");
}
//________________________________________________________________________________________
CCUExp Simp::simplify_unary(exp, op)
{  
	CCUExp es1 = simplify_exp(exp)->cond2exp();
	CCUExp ccu_sym = create_temp(es1->exp, ccPrimary);	
	return new CCUExp_((es1->d + ccu_sym->d), (es1->s + ccu_sym->s), "op ccu_sym->exp");
}
//________________________________________________________________________________________
CCUExp Simp::simplify_binary(exp1, exp2, bin_op)
{
	CCUExp es1 = simplify_exp(exp1)->cond2exp();
	CCUExp es2 = simplify_exp(exp2)->cond2exp();
	
	CCUExp ccu_sym1 = create_temp(es1->exp, ccPrimary);
	CCUExp ccu_sym2 = create_temp(es2->exp, ccPrimary);

	return new(core_manager) CCUExp_((es1->d + es2->d + ccu_sym1->d + ccu_sym2->d), 
		(es1->s + es2->s + ccu_sym1->s + ccu_sym2->s), 
		"ccu_sym1->exp bin_op ccu_sym2->exp");
}
//________________________________________________________________________________________
CCUExp Simp::simplify_op_assign(exp1, exp2, bin_op)
{
	ACCUExp es1 = simplify_lvalue(exp1);		
	AstBinary *ast_binary = new(manager) AstBinary(manager, CoreBinary::bcc2opcode(bin_op), es1->exp->asAstExpression(),
							ast_assign->GetNewValue(), exp->GetReturnType(), *(exp->GetSFA()));
	CCUExp es2 = simplify_exp("es1->exp bin_op exp2")->cond2exp();
	return create_core_assign(es1, es2);
}
//________________________________________________________________________________________
// "Constructors"
//________________________________________________________________________________________
CCUExp Simp::create_core_assign(CCUExp es1, CCUExp es2)
{
	if (IsCore(es1->exp, ccName))		
		return CCUExp_((es1->d + es2->d), (es1->s + es2->s + "es1->exp = es2->exp;"), es1->exp);
	else {
		CCUExp ccu_sym = create_temp(es2->exp, ccPrimary);		
		return new CCUExp_((es1->d + es2->d + ccu_sym->d), 
					(es1->s + es2->s + ccu_sym->s + "es1->exp = ccu_sym->exp;"), es1->exp);
	}
}
//________________________________________________________________________________________
CCUExp Simp::create_deref(CCUExp es1)
{
	CCUExp ccu_sym = create_temp(es1->exp, ccName);	
	return new CCUExp_((es1->d + ccu_sym->d), (es1->s + ccu_sym->s), "*(ccu_sym->exp)" );
}
//________________________________________________________________________________________
CCUExp Simp::create_temp(exp, op)
{
	if (!IsCore(exp, op)) {		
		typeName = get_type_of(exp);
		return new  CCUExp_("typeName t", "t = exp;", "t");
	}
	else return new  CCUExp_(exp);
}
//________________________________________________________________________________________
// Conversions between representations
//________________________________________________________________________________________
CCUCond CCUExp_::exp2cond()
{		
	label_true = new label();
	label_false = new_label();
	CCUExp ccu_sym1 = create_temp(exp, ccPrimary);
	return new CCUCond( (d + ccu_sym1->d), (s + ccu_sym1->s + "if (exp == 1) goto label_true; else goto label_false;"), 
					label_true, label_false);
}
//________________________________________________________________________________________
// returns COND CCP
CCUCond CCUBool_::exp2cond(Simp &sim, SymbolTable *scope)
{	
	label_true = new label();
	label_false = new_label();
	return new CCUCond(d, (s + "if (exp) goto label_true; else goto label_false;"), 
				label_true, label_false );		
}
//________________________________________________________________________________________
CCUExp CCUCond_::cond2exp(Simp &sim, SymbolTable *scope, SFA sfa)
{
	t = new variable;
	l1 = new label;
	return new CCUExp( (d + "int t;"), (s + "trues: t=1;" + "goto l1;" + "falses: t=0;" + "l1: ;"), "t");
}

//________________________________________________________________________________________
// TODO: write it in pseudo-code
//________________________________________________________________________________________
CCUStmt Simp::simplify_init_list(AstExpression *id, AstExpression **vec, int count, int &start, SymbolTable *scope)  //typeName IDENTIFIER = { initList }:	
{	
	CCUStmt es1 = new(core_manager) CCUStmt_(), es2;
	AstExpression *id_i;
	AstExpression *exp;	
	int max_fields = get_num_of_fields(id);
	int i = 0;
	while ((start < count) && ( i < max_fields))
	{
		// define i : exp is expression number i of init_list 
		// define lhs : lhs is the identifier of field i of exp, where exp is a struct
		//				or the i-th element of exp, where exp is an array 
		exp = vec[start];
		id_i = get_id_by_index(id, i, *(exp->GetSFA()));		
		if (exp->GetOpCode() == ocInitList)
		{
			AstInitList *inner_init_list = cast<AstInitList *>(exp);
			int inner_start = 0;
			es2 = simplify_init_list(id_i, inner_init_list->GetInitVector(), inner_init_list->GetInitCount(), inner_start, scope);
			start++;
		}
		else
		{
			if (!MaybeIncompatibleTypes(id_i->GetReturnType(), exp->GetReturnType()))
			{
				if ((exp->GetOpCode() == ocConstant) &&
					((cast<AstConstant *>(exp))->GetConstantValue()->GetKind() == ckString) &&
					(id_i->GetReturnType()->SkipModifiersAndTypedefs()->GetTypeKind() == tkArray))
				{
						Type *elem_type = id_i->GetReturnType()->SkipModifiersAndTypedefs();
						if (elem_type->GetTypeKind() == tkArray)
							elem_type = elem_type->asArray()->GetElemType();
						else
							ERROR_EXIT("Unknown type in declaration with initialization list.\n");

						AstConstant *ast_const = cast<AstConstant *>(exp);
						char *str_exp = ast_const->GetConstantValue()->AsString();		
						es2 = new(core_manager) CCUStmt_();
						for (int j = 0; j < strlen(str_exp)-2; j++)		//because the string is always returned with ""
						{						
							IntegralValue *const_j = IntegralValue::Make(manager->GetAllocator(), j, false); 
							AstConstant *ast_j = new(manager) AstConstant(manager, const_j, Type::SInt, *(exp->GetSFA()));
							AstBinary *id_i_j = new(manager) AstBinary(manager, ocIndex, id_i, ast_j, 
								elem_type, *(exp->GetSFA()));
							/*
							char *letter = new char[2];
							letter[0] = str_exp[j+1];
							letter[1] = '\0';
							StringValue *str_letter = new StringValue(2, letter);	 
							*/
							IntegralValue *str_letter = IntegralValue::Make(manager->GetAllocator(), str_exp[j+1], false); 
							AstConstant *ast_letter = new(manager) AstConstant(manager, str_letter, Type::Char, *(exp->GetSFA())); 
							AstAssignment *ast_asgn = new(manager) AstAssignment(manager, ocAssign, id_i_j, ast_letter, 
								id_i_j->GetReturnType(), *(id_i_j->GetSFA()));
							AstExpr *ast_asgn_stmt = new(manager) AstExpr(manager, ast_asgn, *(id_i->GetSFA()));
							es2 = concat(es2, simplify_stmt(ast_asgn_stmt, scope));							
						}
						start++;
				}
				else
				{
					// ast_asgn = [id_i = exp]
					AstAssignment *ast_asgn = new(manager) AstAssignment(manager, ocAssign, id_i, exp, 
						id_i->GetReturnType(), *(id_i->GetSFA()));
					AstExpr *ast_asgn_stmt = new(manager) AstExpr(manager, ast_asgn, *(id_i->GetSFA()));
					es2 = simplify_stmt(ast_asgn_stmt, scope);
					start++;
				}
			}
			else
			{					
				es2 = simplify_init_list(id_i, vec, count, start, scope);				
			}	
		}
		es1 = concat(es1, es2);
		i++;
	}
	if ((start >= count) && (i < max_fields))
		es1 = concat(es1, create_padding(id, i, scope));	
	return es1;
}
//________________________________________________________________________________________


