/* Dialect - Code Output Formatter
 * 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 "MCppDialect.h"

#include "ilengineer/Blocks.h"
#include "ilengineer/Ops/MSIL.h"

void Dialect::MCppDialect::Formulate(ILEngineer::Statement *st) {
	const type_info &type = typeid(*st);
	const Metallurgy::Module &import = *st->getMethod()->getImport();
	const Metallurgy::Method &method = st->getMethod()->getMethodDef();
	const Metallurgy::ILMethodImpl *impl = dynamic_cast<const Metallurgy::ILMethodImpl *>(method.getImpl());
	ILEngineer::Block *block = (st->isBlock() ? dynamic_cast<ILEngineer::Block *>(st) : NULL);

	if (type == typeid(ILEngineer::Blocks::Method)) {
		ILEngineer::StatementVector &sts = dynamic_cast<ILEngineer::Block *>(st)->getStatements();
		ILEngineer::StatementVector::iterator skip(NULL);

		for (ILEngineer::StatementVector::iterator stI(block->getStatements().begin()); skip == NULL && stI != block->getStatements().end(); stI++)
			if (!(*stI)->isBlock()) {
				ILEngineer::Operation *op = (*stI)->getOperations()[0];
				if (typeid(*op) == typeid(ILEngineer::Ops::MSIL::Call)) {
					ILEngineer::Ops::MSIL::Call *call = dynamic_cast<ILEngineer::Ops::MSIL::Call *>(op);
					Metallurgy::Member *member = call->getMember();
					if (member->getName().getFull() == L".ctor" && member->getType() == dynamic_cast<const Metallurgy::TypeDef *>(method.getType())->getExtends()) {
						skip = stI;
						*wout << L" : base(";
						ILEngineer::OperationVector::iterator opI(call->getOperations().begin() + 1);
						for (bool notFirst(false); opI != op->getOperations().end(); opI++, notFirst = true) {
							if (notFirst)
								*wout << L", ";
							Formulate(*opI);
						} *wout << L")";

					}
				}
			}

		*wout << L" {" << /*XXX*/ L"\r\n";
		UpIndent();

		Metallurgy::StandAlone *locals = impl->getLocals();
		if (locals != NULL) {
			Metallurgy::Signature *localsig = locals->getSignature();
			int i(0);
			for (Metallurgy::ElementVector::const_iterator elI(localsig->getElements().begin()); elI != localsig->getElements().end(); elI++, i++) {
				*wout << GetIndent();
				Formulate(*elI);
				*wout << L" local" << i << L";" << /*XXX*/ L"\r\n";
			}
			*wout << /*XXX*/ L"\r\n";
		}

		for (stI = block->getStatements().begin(); stI != block->getStatements().end(); stI++)
			if (stI != skip)
				Formulate(*stI);

		DownIndent();
		*wout << GetIndent() << L"}" << /*XXX*/ L"\r\n";

	} else {
		/* // Not on first...
		if (st->isBlock())*/
		//	*wout << /*XXX*/ L"\r\n";
		*wout << GetIndent();
		if (offsets) {
			*wout << st->getOffset();
			if (st->isBlock())
				*wout << L"/" << dynamic_cast<ILEngineer::Block *>(st)->getEnd();
			ILEngineer::Blocks::CheckedLoop *checked = dynamic_cast<ILEngineer::Blocks::CheckedLoop *>(st);
			if (checked != NULL)
				*wout << L"!" << checked->getCheck();
			*wout << L": ";
		}
		if (st->getGoto() != -1)
			*wout << L"i" << st->getGoto() << L": ";

		if (type == typeid(ILEngineer::Blocks::Try)) {
			ILEngineer::Blocks::Try *triumph = dynamic_cast<ILEngineer::Blocks::Try *>(st);
			*wout << L"try {" << /*XXX*/ L"\r\n";
			UpIndent();

			for (ILEngineer::StatementVector::iterator stI(block->getStatements().begin()); stI != block->getStatements().end(); stI++)
				Formulate(*stI);
			DownIndent();
			*wout << GetIndent() << L"}";
			
			/*for (ILEngineer::StatementVector::const_iterator hnI(triumph->getCatches().begin()); hnI != triumph->getCatches().end(); hnI++) {
				ILEngineer::Blocks::Catch *kludge(dynamic_cast<ILEngineer::Blocks::Catch *>(*hnI));

				*wout << L" catch (";
				Metallurgy::IMetaType *type = import.getType(kludge->getException()->ClassToken);
				Formulate(*type->getElement());
				if (!kludge->getPop())
					*wout << L" local" << kludge->getIndx();*/
			//	*wout << L") {" << /*XXX*/ L"\r\n";
			/*	UpIndent();
				for (ILEngineer::StatementVector::iterator stI(kludge->getStatements().begin()); stI != kludge->getStatements().end(); stI++)
					Formulate(*stI);

				DownIndent();
				*wout << GetIndent() << L"}";
				//Formulate(*stI);
			}

			for (ILEngineer::StatementVector::const_iterator hnI(triumph->getFinallies().begin()); hnI != triumph->getFinallies().end(); hnI++) {*/
			//	*wout << L" finally {" << /*XXX*/ L"\r\n";
			/*	UpIndent();

				ILEngineer::Blocks::Finally *final(dynamic_cast<ILEngineer::Blocks::Finally *>(*hnI));
				for (ILEngineer::StatementVector::iterator stI(final->getStatements().begin()); stI != final->getStatements().end(); stI++)
					Formulate(*stI);

				DownIndent();
				*wout << GetIndent() << L"}";
				//Formulate(*stI);
			}*/
			*wout << /*XXX*/ L"\r\n";

		} else if (st->isBlock()) {
			bool evil(false), blank(false), strange(false), second(false);

			if (type == typeid(ILEngineer::Blocks::Catch)) {
				strange = true;
				ILEngineer::Blocks::Catch *kludge = dynamic_cast<ILEngineer::Blocks::Catch *>(st);

				*wout << L"catch (";
				Metallurgy::Type *type = dynamic_cast<Metallurgy::Type *>(import.ResolveToken(kludge->getException()->ClassToken));
				Formulate(type->getElement());
				if (!kludge->getPop())
					*wout << L" local" << kludge->getIndx();
				*wout << L")";

			} else if (type == typeid(ILEngineer::Blocks::Case)) {
				blank = true;

				ILEngineer::OperationVector &opV = st->getOperations();
				for (ILEngineer::OperationVector::iterator opI(opV.begin()); opI != opV.end(); ) {
					*wout << L"case ";
					Formulate(*opI);
					*wout << L":";
					if (++opI != opV.end()) {
						*wout << /*XXX*/ L"\r\n";
						UpIndent();
						*wout << GetIndent() << L"goto case ";
						Formulate(opV.back());
						*wout << L";" << /*XXX*/ L"\r\n";
						DownIndent();
						*wout << GetIndent();
					}
				}

			} else if (type == typeid(ILEngineer::Blocks::Default)) {
				blank = true;
				*wout << L"default:";

			} else if (type == typeid(ILEngineer::Blocks::Do)) {
				strange = true;
				second = true;
				*wout << L"do";

			} else if (type == typeid(ILEngineer::Blocks::If)) {
				evil = true;
				*wout << L"if (";
				Formulate(dynamic_cast<ILEngineer::Blocks::If *>(st)->getValue());
				*wout << L")";

			} else if (type == typeid(ILEngineer::Blocks::Else)) {
				evil = true;
				*wout << L"else";

			} else if (type == typeid(ILEngineer::Blocks::Finally)) {
				strange = true;
				*wout << L"finally";

			} else if (type == typeid(ILEngineer::Blocks::Lock)) {
				*wout << L"lock (";
				Formulate(dynamic_cast<ILEngineer::Blocks::Lock *>(st)->getOperations()[0]);
				*wout << L")";

			} else if (type == typeid(ILEngineer::Blocks::ForEach)) {
				ILEngineer::Blocks::ForEach *foreach = dynamic_cast<ILEngineer::Blocks::ForEach *>(st);
				*wout << L"foreach (";
				Formulate(impl->getLocals()->getSignature()->getElement(foreach->getIndx()));
				*wout << L" local" << foreach->getIndx() << L" in ";
				Formulate(foreach->getArray());
				*wout << L")";

			} else if (type == typeid(ILEngineer::Blocks::Switch)) {
				evil = true;
				*wout << L"switch (";
				Formulate(dynamic_cast<ILEngineer::Blocks::Switch *>(st)->getValue());
				*wout << L")";

			} else if (type == typeid(ILEngineer::Blocks::While)) {
				ILEngineer::Blocks::While *bwhile = dynamic_cast<ILEngineer::Blocks::While *>(st);
				*wout << L"while (";
				Formulate(bwhile->getOperations()[0]);
				*wout << L")";

			} else {
				wchar_t buff[1024];
				buff[1023] = '\0';
				_snwprintf(buff, 1023, L"<{ %S }>", type.name());
				*wout << buff << /*XXX*/ L"\r\n";
				return;
			}

			ILEngineer::StatementVector &sts = dynamic_cast<ILEngineer::Block *>(st)->getStatements();
			if (sts.size() == 0 && !second) {
				if (evil || strange)
					*wout << L" {}" << /*XXX*/ L"\r\n";
				else if (blank)
					*wout << /*XXX*/ L"\r\n";
				else
					*wout << L";" << /*XXX*/ L"\r\n";

			} else {
				if (strange || !blank && sts.size() != 1)
					*wout << L" {";
				*wout << /*XXX*/ L"\r\n";

				UpIndent();
				for (ILEngineer::StatementVector::iterator stI(sts.begin()); stI != sts.end(); stI++)
					Formulate(*stI);
				DownIndent();
			}

			if (type == typeid(ILEngineer::Blocks::Case)) {
				// XXX: This is redundant, and maybe wrong.
				*wout << GetIndent() << L"break;" << /*XXX*/ L"\r\n";

			} else if (type == typeid(ILEngineer::Blocks::Do)) {
				ILEngineer::Blocks::Do *bdo = dynamic_cast<ILEngineer::Blocks::Do *>(st);
				*wout << GetIndent() << L"} while (";
				Formulate(bdo->getOperations()[0]);
				*wout << L");" << /*XXX*/ L"\r\n";

			} else if (strange || !blank && sts.size() > 1)
				*wout << GetIndent() << L"}" << /*XXX*/ L"\r\n";

			//*wout << /*XXX*/ L"\r\n";

		} else {
			Formulate(st->getOperations()[0]);
			*wout << L";" << /*XXX*/ L"\r\n";
		}
	}
}