/* ILEngineer - Crummy .NET Decompiler
 * Copyright (C) 2001-2002  Jay Freeman (saurik)
*/

/*
 *        Redistribution and use in source and binary
 * forms, with or without modification, are permitted
 * provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the
 *    above copyright notice, this list of conditions
 *    and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the
 *    above copyright notice, this list of conditions
 *    and the following disclaimer in the documentation
 *    and/or other materials provided with the
 *    distribution.
 * 3. The name of the author may not be used to endorse
 *    or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "stdafx.h"
#include "ilengineer/Blocks/Method.h"

#include "ilengineer/Ops/MSIL/Add.h"
#include "ilengineer/Ops/MSIL/Branch.h"
#include "ilengineer/Ops/MSIL/BranchFalse.h"
#include "ilengineer/Ops/MSIL/BranchTrue.h"
#include "ilengineer/Ops/MSIL/CompareGreaterThan.h"
#include "ilengineer/Ops/MSIL/CompareLessThan.h"
#include "ilengineer/Ops/MSIL/LoadStaticField.h"
#include "ilengineer/Ops/MSIL/Return.h"

#include "ilengineer/Ops/ILE/CompareNotEqual.h"
#include "ilengineer/Ops/ILE/Goto.h"
#include "ilengineer/Ops/ILE/LoadProperty.h"
#include "ilengineer/Ops/ILE/LogicAnd.h"
#include "ilengineer/Ops/ILE/LogicOr.h"
#include "ilengineer/Ops/ILE/OpCall.h"
#include "ilengineer/Ops/ILE/StoreProperty.h"
#include "ilengineer/Ops/ILE/StringEquals.h"
#include "ilengineer/Ops/ILE/StringNotEquals.h"

void ILEngineer::Blocks::Method::OptimizeGeneral() {
	if (m_Statements.size() == 0)
		return; // Ummm... :)

	//Operation *op;
	//OperationVector::iterator opI;
	Operation::Reference::Vector refs;

	BlockVector blocks;
	getBlocks(blocks);

	//Metallurgy::Signature *locals = method->getLocals()->getSignature();
	//std::cerr << "=< " << typeid(*m_Statements.back()->getOperations()[0]).name() << " >=" << std::endl;

	OptimizeSwitch();
	OptimizeBranch();

	refs.clear();
	Find(typeid(Ops::MSIL::BranchFalse), refs);
	for (Operation::Reference::Vector::iterator orI(refs.begin()); orI != refs.end(); orI++) {
		Ops::MSIL::BranchFalse *branch = dynamic_cast<Ops::MSIL::BranchFalse *>(*orI->opI);
		*orI->opI = new Ops::MSIL::BranchTrue(m_Method, branch);
		delete branch;
	}

	OptimizeInline();
	OptimizeLock();
	OptimizeForEach();
	OptimizeDoWhile();
	OptimizeIf();
	OptimizeIfWhile();
	//OptimizeWhileFor();
	OptimizeStrings();
	OptimizeTypeOf();
	OptimizeTry();

	OptimizeIfBranch();

	OptimizeBreak();
	OptimizeContinue();
	OptimizeElse();

	/*refs.clear();
	Find(typeid(Ops::MSIL::StoreField), refs);
	for (orI = refs.begin(); orI != refs.end(); orI++) {
		Ops::MSIL::StoreField *store = dynamic_cast<Ops::MSIL::StoreField *>(*orI->opI);
		if (typeid(*store->getObj()) == typeid(Ops::MSIL::Duplicate)) {
			if (typeid(*store->getValue()) == typeid(Ops::MSIL::Add))
				OptimizeStarEqualField<Ops::MSIL::Add, Ops::ILE::PlusEqualsField>(store, dynamic_cast<Ops::MSIL::Add *>(store->getValue()), *orI->opI);
			else if (typeid(*store->getValue()) == typeid(Ops::MSIL::Or))
				OptimizeStarEqualField<Ops::MSIL::Or, Ops::ILE::OrEqualsField>(store, dynamic_cast<Ops::MSIL::Or *>(store->getValue()), *orI->opI);
		}
	}*/

	refs.clear();
	Find(typeid(Ops::MSIL::StoreLocal), refs);
	for (orI = refs.begin(); orI != refs.end(); orI++) {
		Ops::MSIL::StoreLocal *store = dynamic_cast<Ops::MSIL::StoreLocal *>(*orI->opI);
		if (typeid(*store->getValue()) == typeid(Ops::MSIL::Add)) {
			Ops::MSIL::Add *add = dynamic_cast<Ops::MSIL::Add *>(store->getValue());
			if (typeid(*add->getValue1()) == typeid(Ops::MSIL::LoadLocal)) {
				Ops::MSIL::LoadLocal *field = dynamic_cast<Ops::MSIL::LoadLocal *>(add->getValue1());
				if (field->getIndx() == store->getIndx()) {
					*orI->opI = new Ops::ILE::PlusEqualsLocal(m_Method, add, store);
					delete store;
				}
			}
		} else if (typeid(*store->getValue()) == typeid(Ops::MSIL::Or)) {
			Ops::MSIL::Or *or = dynamic_cast<Ops::MSIL::Or *>(store->getValue());
			if (typeid(*or->getValue1()) == typeid(Ops::MSIL::LoadLocal)) {
				Ops::MSIL::LoadLocal *field = dynamic_cast<Ops::MSIL::LoadLocal *>(or->getValue1());
				if (field->getIndx() == store->getIndx()) {
					*orI->opI = new Ops::ILE::OrEqualsLocal(m_Method, or, store);
					delete store;
				}
			}
		}
	}

	refs.clear();
	Find(typeid(Ops::MSIL::StoreStaticField), refs);
	for (orI = refs.begin(); orI != refs.end(); orI++) {
		Ops::MSIL::StoreStaticField *store = dynamic_cast<Ops::MSIL::StoreStaticField *>(*orI->opI);
		if (typeid(*store->getValue()) == typeid(Ops::MSIL::Add)) {
			Ops::MSIL::Add *add = dynamic_cast<Ops::MSIL::Add *>(store->getValue());
			if (typeid(*add->getValue1()) == typeid(Ops::MSIL::LoadStaticField *)) {
				Ops::MSIL::LoadStaticField *field = dynamic_cast<Ops::MSIL::LoadStaticField *>(add->getValue1());
				if (field->getField() == store->getMember()) {
					*orI->opI = new Ops::ILE::PlusEqualsStaticField(m_Method, add, store);
					delete store;
				}
			}
		} else if (typeid(*store->getValue()) == typeid(Ops::MSIL::Add)) {
			Ops::MSIL::Or *or = dynamic_cast<Ops::MSIL::Or *>(store->getValue());
			if (typeid(*or->getValue1()) == typeid(Ops::MSIL::LoadStaticField *)) {
				Ops::MSIL::LoadStaticField *field = dynamic_cast<Ops::MSIL::LoadStaticField *>(or->getValue1());
				if (field->getField() == store->getMember()) {
					*orI->opI = new Ops::ILE::OrEqualsStaticField(m_Method, or, store);
					delete store;
				}
			}
		}
	}

	bool havenots(true);
	while (havenots) {
		havenots = false;
		refs.clear();
		Find(typeid(Ops::ILE::LogicNot), refs);
		for (orI = refs.begin(); orI != refs.end(); orI++) {
			Ops::ILE::LogicNot *not = dynamic_cast<Ops::ILE::LogicNot *>(*orI->opI);
			Operation *value = not->getValue();
			if (typeid(*value) == typeid(Ops::ILE::LogicNot)) {
				Ops::ILE::LogicNot *contra = dynamic_cast<Ops::ILE::LogicNot *>(value);
				*orI->opI = contra->getValue();
				contra->toTrash();
				delete not;
			} else if (typeid(*value) == typeid(Ops::ILE::LogicAnd)) {
				Ops::ILE::LogicAnd *contra = dynamic_cast<Ops::ILE::LogicAnd *>(value);
				*orI->opI = new Ops::ILE::LogicOr(m_Method, new Ops::ILE::LogicNot(m_Method, contra->getValue1()), new Ops::ILE::LogicNot(m_Method, contra->getValue2()));
				contra->toTrash();
				delete not;
				havenots = true;
			} else if (typeid(*value) == typeid(Ops::ILE::LogicOr)) {
				Ops::ILE::LogicOr *contra = dynamic_cast<Ops::ILE::LogicOr *>(value);
				*orI->opI = new Ops::ILE::LogicAnd(m_Method, new Ops::ILE::LogicNot(m_Method, contra->getValue1()), new Ops::ILE::LogicNot(m_Method, contra->getValue2()));
				contra->toTrash();
				delete not;
				havenots = true;
			} else if (typeid(*value) == typeid(Ops::ILE::StringEquals)) {
				Ops::ILE::StringEquals *contra = dynamic_cast<Ops::ILE::StringEquals *>(value);
				*orI->opI = new Ops::ILE::StringNotEquals(m_Method, contra);
				delete not;
			} else if (typeid(*value) == typeid(Ops::ILE::StringNotEquals)) {
				Ops::ILE::StringNotEquals *contra = dynamic_cast<Ops::ILE::StringNotEquals *>(value);
				*orI->opI = new Ops::ILE::StringEquals(m_Method, contra);
				delete not;
			} else if (typeid(*value) == typeid(Ops::MSIL::CompareGreaterThan)) {
				Ops::MSIL::CompareGreaterThan *contra = dynamic_cast<Ops::MSIL::CompareGreaterThan *>(value);
				*orI->opI = new Ops::ILE::CompareLessThanEqual(m_Method, contra);
				delete not;
			} else if (typeid(*value) == typeid(Ops::MSIL::CompareLessThan)) {
				Ops::MSIL::CompareLessThan *contra = dynamic_cast<Ops::MSIL::CompareLessThan *>(value);
				*orI->opI = new Ops::ILE::CompareGreaterThanEqual(m_Method, contra);
				delete not;
			} else if (typeid(*value) == typeid(Ops::MSIL::CompareEqual)) {
				Ops::MSIL::CompareEqual *contra = dynamic_cast<Ops::MSIL::CompareEqual *>(value);
				*orI->opI = new Ops::ILE::CompareNotEqual(m_Method, contra);
				delete not;
			} else if (typeid(*value) == typeid(Ops::ILE::CompareNotEqual)) {
				Ops::ILE::CompareNotEqual *contra = dynamic_cast<Ops::ILE::CompareNotEqual *>(value);
				*orI->opI = new Ops::MSIL::CompareEqual(m_Method, contra);
				delete not;
			} else if (typeid(*value) == typeid(Ops::ILE::CompareGreaterThanEqual)) {
				Ops::ILE::CompareGreaterThanEqual *contra = dynamic_cast<Ops::ILE::CompareGreaterThanEqual *>(value);
				*orI->opI = new Ops::MSIL::CompareLessThan(m_Method, contra);
				delete not;
			} else if (typeid(*value) == typeid(Ops::ILE::CompareLessThanEqual)) {
				Ops::ILE::CompareLessThanEqual *contra = dynamic_cast<Ops::ILE::CompareLessThanEqual *>(value);
				*orI->opI = new Ops::MSIL::CompareGreaterThan(m_Method, contra);
				delete not;
			}
		}
	}

	refs.clear();
	Find(typeid(Ops::MSIL::Call), refs);
	for (orI = refs.begin(); orI != refs.end(); orI++) {
		static dummy(0);
		Ops::MSIL::Call *call = dynamic_cast<Ops::MSIL::Call *>(*orI->opI);
		Metallurgy::Member *member = call->getMember();
		if (dummy++ == 14)
			std::wcout << member->getName().getFull() << std::endl;
		if (member->getName().getFull().substr(0, 4) == L"get_") {
			if (member->getSignature()->getElements().size() == 0) {
				*orI->opI = new Ops::ILE::LoadProperty(this, call);
				delete call;
			// XXX: } else if (member->getName().substr(4) == isDefaultMember) {
			}
		} else if (member->getName().getFull().substr(0, 4) == L"set_") {
			if (member->getSignature()->getElements().size() == 1) {
				*orI->opI = new Ops::ILE::StoreProperty(this, call);
				delete call;
			// XXX: } else if (member->getName().substr(4) == isDefaultMember) {
			}
		}
	}

	// OK... now just 'goto' :(.

	refs.clear();
	Find(typeid(Ops::MSIL::Branch), refs);
	for (orI = refs.begin(); orI != refs.end(); orI++) {
		Ops::MSIL::Branch *branch = dynamic_cast<Ops::MSIL::Branch *>(*orI->opI);
		*orI->opI = new Ops::ILE::Goto(this, branch);
		delete branch;
	}

	if (typeid(*m_Statements.back()->getOperations()[0]) == typeid(Ops::MSIL::Return)) {
		OptimizeReturn(dynamic_cast<Ops::MSIL::Return *>(m_Statements.back()->getOperations()[0])->getOffset());
		delete m_Statements.back();
		m_Statements.pop_back();
	} else
		OptimizeReturn(m_End);
}