/* 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"

void Dialect::MCppDialect::Perform(const Metallurgy::Element *element, std::wstring &builder, Metallurgy::ModDeque &mods) {
	while (!mods.empty()) {
		std::wstring mod = mods.front().first;
		if (mod == L"Microsoft.VisualC.IsConstModifier") {
			builder.insert(0, L" const");
		} else break;
		mods.pop_front();
	}

	switch (element->getIntrinsic()) {
		case Chordata::Element::Void:		builder.insert(0, L"void");				break;
		case Chordata::Element::Boolean:	builder.insert(0, L"bool");				break;
		case Chordata::Element::Char:		builder.insert(0, L"wchar_t");			break;

		case Chordata::Element::I1:
			if (!mods.empty() && mods.back().first == L"Microsoft.VisualC.NoSignSpecifiedModifier") {
				builder.insert(0, L"char");
				mods.pop_back();
			} else
				builder.insert(0, L"signed char");
		break;

		case Chordata::Element::U1:			builder.insert(0, L"unsigned char");	break;
		case Chordata::Element::I2:			builder.insert(0, L"short");			break;
		case Chordata::Element::U2:			builder.insert(0, L"unsigned short");	break;
		case Chordata::Element::I4:			builder.insert(0, L"int");				break;
		case Chordata::Element::U4:			builder.insert(0, L"unsigned int");		break;
		case Chordata::Element::I8:			builder.insert(0, L"__int64");			break;
		case Chordata::Element::U8:			builder.insert(0, L"unsigned __int64");	break;
		case Chordata::Element::R4:			builder.insert(0, L"float");			break;
		case Chordata::Element::R8:			builder.insert(0, L"double");			break;
		case Chordata::Element::String:		builder.insert(0, L"String *");			break;
		case Chordata::Element::Object:		builder.insert(0, L"Object *");			break;

		case Chordata::Element::I:			builder.insert(0, L"IntPtr");			break;
		case Chordata::Element::U:			builder.insert(0, L"UIntPtr");			break;
		case Chordata::Element::TypedByRef:	builder.insert(0, L"TypedReference");	break;

		case Chordata::Element::ByRef:
			if (!mods.empty()) {
				std::wstring name = mods.back().first;
				if (name == L"Microsoft.VisualC.IsCXXReferenceModifier") {
					builder.insert(0, L" __gc &");
					mods.pop_back();
				} else if (name == L"System.Runtime.CompilerServices.IsVolatile") {
					builder.insert(0, L" __pin *");
					mods.pop_back();
				}
			} else
				builder.insert(0, L" __gc *");
			Render(element->getExtra(), builder);
		break;

		case Chordata::Element::Ptr:
			if (!mods.empty()) {
				if (mods.back().first == L"Microsoft.VisualC.IsCXXReferenceModifier") {
					builder.insert(0, L" __nogc &");
					mods.pop_back();
				}
			} else
				builder.insert(0, L" __nogc *");
			Render(element->getExtra(), builder);
		break;

		case Chordata::Element::Class:
		case Chordata::Element::ValueType:
		{
			const Metallurgy::Type *defref = dynamic_cast<const Metallurgy::Type *>(element->getType());

			if (defref == NULL)
				Render(element->getType()->getElement(), builder);
			else {
				std::wstring name = defref->getName().getShort();
				if (element->getIntrinsic() == Chordata::Element::Class)
					builder.insert(0, L" __gc *");
				builder.insert(0, name.c_str());
			}
		} break;

		/*case Chordata::Element::CMod_Reqd:
		case Chordata::Element::CMod_Opt: {
			const Metallurgy::Type *type = dynamic_cast<const Metallurgy::Type *>(element->getType());
			std::wstring name = type->getName().getFull();
			//mods.push(name);
			if (name == L"System.Runtime.CompilerServices.CallConvCdecl") {
				builder.insert(0, L" __cdecl");
				Render(element->getExtra(), builder);
			} else if (name == L"System.Runtime.CompilerServices.CallConvFastcall") {
				builder.insert(0, L" __fastcall");
				Render(element->getExtra(), builder);
			} else if (name == L"System.Runtime.CompilerServices.CallConvStdcall") {
				builder.insert(0, L" __stdcall");
				Render(element->getExtra(), builder);
			} else if (name == L"System.Runtime.CompilerServices.CallConvThiscall") {
				Render(element->getExtra(), builder);
			} else if (name == L"System.Runtime.InteropServices.CallConvCdecl") {
				builder.insert(0, L" __cdecl");
				Render(element->getExtra(), builder);
			} else if (name == L"System.Runtime.InteropServices.CallConvStdcall") {
				builder.insert(0, L" __stdcall");
				Render(element->getExtra(), builder);
			} else
				throw std::wstring(L"unsupported, language specific modification: " + name);
		} break;*/

		case Chordata::Element::FnPtr: {
			std::wstring scratch;
			const Metallurgy::Signature *signature = element->getNested();
			builder.insert(0, L"*");

			Metallurgy::ModDeque decls;
			Render(signature->getReturn(), scratch = L"", decls);
			if (!decls.empty()) {
				std::wstring name = decls.back().first;
				bool remove(true);
				if (name == L"System.Runtime.CompilerServices.CallConvCdecl")
					builder.insert(0, L" __cdecl ");
				else if (name == L"System.Runtime.CompilerServices.CallConvFastcall")
					builder.insert(0, L" __fastcall ");
				else if (name == L"System.Runtime.CompilerServices.CallConvStdcall")
					builder.insert(0, L" __stdcall ");
				else if (name == L"System.Runtime.CompilerServices.CallConvThiscall")
					/* XXX: Ummm... dude? */;
				else if (name == L"System.Runtime.InteropServices.CallConvCdecl")
					builder.insert(0, L" __cdecl ");
				else if (name == L"System.Runtime.InteropServices.CallConvStdcall")
					builder.insert(0, L" __stdcall ");
				else remove = false;

				if (remove) decls.pop_back();
			}

			mods.insert(mods.end(), decls.begin(), decls.end());

			builder.insert(0, L" (");
			builder.insert(0, scratch);
			builder.append(L")(");
			for (uint32_t i(0); i < signature->getCount(); i++) {
				if (i != 0) builder.append(L", ");
				Render(signature->getElement(i), scratch = L"");
				builder.append(scratch);
			}
			builder.append(L")");
		} break;

		case Chordata::Element::SZArray:
			builder.insert(0, L"[]");
			Render(element->getExtra(), builder);
		break;

		case Chordata::Element::Array: {
			Render(element->getExtra(), builder);
			wchar_t buff[16];
			const Metallurgy::Element::DimensionVector *array = element->getArray();
			for (uint32_t i(0); i < array->size(); i++) {
				builder.append(L"[");
				builder.append(_itow((*array)[i].size, buff, 10));
				builder.append(L"]");
			}
		} break;

		default:
			std::wostringstream serr;
			serr << L"unable to render element: [" << std::setbase(16) << element->getIntrinsic() << std::setbase(10) << L"]";
			throw SimpleException(serr.str());
		break;
	}

	while (!mods.empty()) {
		std::wstring mod = mods.back().first;
		if (mod == L"Microsoft.VisualC.IsLongModifier") {
			builder.insert(0, L"long ");
		} else break;
		mods.pop_back();
	}
}
