/* 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/Operation.h"

#include "ilengineer/Blocks/Method.h"

#include "ilengineer/Ops/MSIL/Duplicate.h"
#include "ilengineer/Ops/MSIL/LoadConstant.h"
#include "ilengineer/Ops/MSIL/LoadNull.h"

#include "ilengineer/Ops/ILE/CompareNotEqual.h"
#include "ilengineer/Ops/ILE/LoadConstant.h"

bool isOpen(false);
int del(0);

namespace ILEngineer {

	Stack::~Stack() {
		while (!empty()) {
			delete top();
			pop();
		}
	}

	Operation::Operation(Blocks::Method *method, uint8_t *offset, bool skip, bool garbage) :
		m_Method(method),
		m_Offset(offset),
		m_Skip(skip),
		m_Garbage(garbage),
		m_Parent(NULL),
		m_Scope(false)
	{}

	Operation::~Operation() {
		if (isOpen && isBroken) del++;
		if (!m_Garbage) {
			if (isOpen && isBroken) std::cout << "deleting opr: " << del << std::endl;
			for (OperationVector::iterator opI(m_Operations.begin()); opI != m_Operations.end(); opI++) {
				if (isOpen && isBroken) std::cout << "delete(" << del << "):  " << typeid(**opI).name() << std::endl;
	  			delete *opI;
			}
		}
		if (isOpen && isBroken) del--;
	}

	void Operation::Find(const type_info &type, Operation::Reference::Vector &ops) {
		for (OperationVector::iterator opI(m_Operations.begin()); opI != m_Operations.end(); opI++) {
			(*opI)->Find(type, ops);
			if (!(*opI)->isSkip() && typeid(**opI) == type)
				ops.push_back(Operation::Reference(&m_Operations, opI));
		}
	}

	bool Operation::Find(const type_info &type, Operation::Reference &ref) {
		for (OperationVector::iterator opI(m_Operations.begin()); opI != m_Operations.end(); opI++) {
			if ((*opI)->Find(type, ref))
				return true;
			if (!(*opI)->isSkip() && typeid(**opI) == type) {
				ref = Operation::Reference(&m_Operations, opI);
				return true;
			}
		} return false;
	}

	bool Operation::Find(const uint8_t *offset, Operation::Reference &ref) {
		for (OperationVector::iterator opI(m_Operations.begin()); opI != m_Operations.end(); opI++) {
			if (!(*opI)->isSkip() && (*opI)->getOffset() == offset) {
				ref = Operation::Reference(&m_Operations, opI);
				return true;
			}
			if ((*opI)->Find(offset, ref))
				return true;
		} return false;
	}

	bool Operation::FindDup(Ops::MSIL::Duplicate *dup1, Operation::Reference &ref) {
		for (OperationVector::iterator opI(m_Operations.begin()); opI != m_Operations.end(); opI++) {
			if ((*opI)->FindDup(dup1, ref))
				return true;
			Ops::MSIL::Duplicate *dup2 = dynamic_cast<Ops::MSIL::Duplicate *>(*opI);
			if (dup2 != NULL && dup1->isOrig() != dup2->isOrig() && dup1->getValue() == dup2->getValue()) {
				ref = Operation::Reference(&m_Operations, opI);
				return true;
			}
		} return false;
	}

	void Operation::Tack(Operation *tack, Chordata::Element::Type type) {
		const type_info &info = typeid(*tack);
		Operation *rep = NULL;

		switch (type) {
			case Chordata::Element::Boolean:
				if (info == typeid(Ops::MSIL::LoadI4Constant))
					rep = new Ops::ILE::LoadBoolConstant(m_Method, tack->getOffset(), dynamic_cast<Ops::MSIL::LoadI4Constant *>(tack)->getConstant() != 0);
				else if (tack->getType()) {
					Chordata::Element::Type from(tack->getType()->getIntrinsic());
					switch (from) {
						case Chordata::Element::I4:
						case Chordata::Element::ValueType:
							rep = new Ops::ILE::CompareNotEqual(m_Method, tack, new Ops::MSIL::LoadI4Constant(m_Method, NULL, 0));
							tack = NULL;
						break;
						case Chordata::Element::Class:
						case Chordata::Element::Object:
						case Chordata::Element::String:
						case Chordata::Element::SZArray:
							rep = new Ops::ILE::CompareNotEqual(m_Method, tack, new Ops::MSIL::LoadNull(m_Method, NULL));
							tack = NULL;
						break;
					}
				}
			break;
			case Chordata::Element::Char:
				if (info == typeid(Ops::MSIL::LoadI4Constant))
					rep = new Ops::ILE::LoadCharConstant(m_Method, tack->getOffset(), (wchar_t) dynamic_cast<Ops::MSIL::LoadI4Constant *>(tack)->getConstant());
			break;
		};

		if (rep == NULL)
			Tack(tack);
		else {
			if (tack != NULL)
				delete tack;
			Tack(rep);
		}
	}

}