/* Anakrino - .NET Reflection Browser
 * 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 "MasterBinder.h"

#include "config/config.h"
#include "fission/fission.h"

namespace Anakrino {

uint32_t MasterBinder::GetIcon(const Metallurgy::Token *token) {
	uint32_t icon(6);

	switch (token->getTokenType()) {

		case Chordata::Tokens::mdtFieldDef: {
			const Metallurgy::Field *field = dynamic_cast<const Metallurgy::Field *>(token);
			icon = FieldIconOffset;

			if (field->IsLiteral())
				icon += NextIconOffset;

			switch (field->getAccess()) {
				case Chordata::Flags::fdPublic:
					icon += PublicAccessOffset;
				break;

				case Chordata::Flags::fdPrivateScope:
				case Chordata::Flags::fdPrivate:
					icon += PrivateAccessOffset;
				break;

				case Chordata::Flags::fdFamily:
				case Chordata::Flags::fdFamORAssem:
					icon += ProtectedAccessOffset;
				break;

				case Chordata::Flags::fdAssembly:
				case Chordata::Flags::fdFamANDAssem:
					icon += InternalAccessOffset;
				break;
			}
		} break;

		case Chordata::Tokens::mdtMemberRef: {
			const Metallurgy::MemberRef *member = dynamic_cast<const Metallurgy::MemberRef *>(token);
			icon = member->getSignature()->isField() ? FieldIconOffset : MethodIconOffset;
		} break;

		case Chordata::Tokens::mdtMethodDef: {
			const Metallurgy::Method *method = dynamic_cast<const Metallurgy::Method *>(token);
	        icon = MethodIconOffset;

	        switch (method->getAccess()) {
		        case Chordata::Flags::mdPublic:
			        icon += PublicAccessOffset;
		        break;

		        case Chordata::Flags::mdPrivateScope:
		        case Chordata::Flags::mdPrivate:
			        icon += PrivateAccessOffset;
		        break;

		        case Chordata::Flags::mdFamily:
		        case Chordata::Flags::mdFamORAssem:
			        icon += ProtectedAccessOffset;
		        break;

		        case Chordata::Flags::mdFamANDAssem:
		        case Chordata::Flags::mdAssem:
			        icon += InternalAccessOffset;
		        break;
	        }
		} break;

		case Chordata::Tokens::mdtModule: {
			const Metallurgy::Module *module = dynamic_cast<const Metallurgy::Module *>(token);

			SHFILEINFO info;
			::SHGetFileInfo(module->getPath().c_str(), 0, &info, sizeof(SHFILEINFO), SHGFI_ICON | SHGFI_SMALLICON);
			icon = m_TokenIcons.AddIcon(info.hIcon);
		} break;

		case Chordata::Tokens::mdtTypeDef: {
			const Metallurgy::TypeDef *type = dynamic_cast<const Metallurgy::TypeDef *>(token);
			icon = ClassIconOffset;

			if (type->getSemantics() == Chordata::Flags::tdInterface)
				icon += NextIconOffset;
			else {
				std::wstring name = type->getName().getFull();

				if (
					name == L"System.Boolean" ||
					name == L"System.Int16" ||
					name == L"System.Int32" ||
					name == L"System.String" ||
					name == L"System.UInt16" ||
					name == L"System.UInt32"
				)
					icon = IntrinsicIconOffset;
				else {
					Metallurgy::Type *extends = type->getExtends();
					if (extends != NULL) {
						std::wstring name = extends->getName();
						if (name == L"System.Enum")				icon += NextIconOffset * 2;
						else if (name == L"System.ValueType")	icon += NextIconOffset * 3;
					}
				}

				if (icon == ClassIconOffset && type->IsSealed())
					icon = SealedIconOffset;
			}

			if (icon > LastStandingIconOffset)
				switch (type->getVisibility()) {
					case Chordata::Flags::tdNestedPublic:
						icon += PublicAccessOffset;
					break;

					case Chordata::Flags::tdNestedPrivate:
						icon += PrivateAccessOffset;
					break;

					case Chordata::Flags::tdNestedFamily:
					case Chordata::Flags::tdNestedFamORAssem:
						icon += ProtectedAccessOffset;
					break;

					case Chordata::Flags::tdNestedFamANDAssem:
					case Chordata::Flags::tdNestedAssembly:
						icon += InternalAccessOffset;
					break;
				}
		} break;

		case Chordata::Tokens::mdtTypeRef: {
			//const Metallurgy::TypeRef *type = dynamic_cast<const Metallurgy::TypeRef *>(token);
			icon = ClassIconOffset;
		} break;

	}

	return icon;
}

void MasterBinder::ShowContext(const Metallurgy::Token *token) {
}

std::wstring MasterBinder::GetDisplayText(const Metallurgy::Token *token) {
	Diapexis::Handle<Dialect::IDialect> dialect(GetDialect(token));
	std::wstring proto, scratch;

	try {
		switch (token->getTokenType()) {

			case Chordata::Tokens::mdtFieldDef: {
				const Metallurgy::Field *mField = dynamic_cast<const Metallurgy::Field *>(token);

				dialect->Render(mField->getElement(), scratch = L"");
				proto = mField->getName().getFull() + L" : " + scratch;

				if (mField->getFlags() & Chordata::Flags::fdHasDefault) {
					proto += L" = ";
					dialect->Render(mField->getValue(), scratch = L"");
					proto += scratch;
				}
			} break;

			case Chordata::Tokens::mdtMemberRef: {
				const Metallurgy::MemberRef *mMemberRef = dynamic_cast<const Metallurgy::MemberRef *>(token);

				proto = mMemberRef->getName().getFull();

				const Metallurgy::Signature *mSignature = mMemberRef->getSignature();
				if (mSignature->isField()) {
					proto += L" : ";
					dialect->Render(mSignature->getElement(0), scratch = L"");
					proto += scratch;
				} else {
					proto += L"(";
					for (uint32_t i(0); i < mSignature->getCount(); i++) {
						if (i != 0) proto += L", ";
						dialect->Render(mSignature->getElement(i), scratch = L"");
						proto += scratch;
					}
					proto += L") : ";
					dialect->Render(mSignature->getReturn(), scratch = L"");
					proto += scratch;
				}
			} break;

			case Chordata::Tokens::mdtMethodDef: {
				const Metallurgy::Method *mMethod = dynamic_cast<const Metallurgy::Method *>(token);

				proto = mMethod->getName().getFull() + L'(';
				const Metallurgy::Signature *mSignature = mMethod->getSignature();
				for (uint32_t i(0); i < mSignature->getCount(); i++) {
					if (i != 0) proto += L", ";
					dialect->Render(mSignature->getElement(i), scratch = L"");
					proto += scratch;
				}

				proto += L") : ";
				dialect->Render(mSignature->getReturn(), scratch = L"");
				proto += scratch;
			} break;

			case Chordata::Tokens::mdtProperty: {
				const Metallurgy::Property *mProperty = dynamic_cast<const Metallurgy::Property *>(token);

				proto = mProperty->getName().getFull();
				if (mProperty->getGetter() != NULL) {
					const Metallurgy::Signature *mSignature = mProperty->getSignature();
					if (mSignature->getCount() != 0) {
						proto += L"[";
						for (uint32_t i(0); i < mSignature->getCount(); i++) {
							if (i != 0) proto += L", ";
							dialect->Render(mSignature->getElement(i), scratch = L"");
							proto += scratch;
						}
						proto += L"]";
					}

					proto += L" : ";
					dialect->Render(mSignature->getReturn(), scratch = L"");
					proto += scratch;
				}
			} break;

		}
	} catch (const Dialect::LanguageSpecificException &) {
		return L"% Language Specific";
	} catch (const Dialect::IDialectException &) {
		return L"% Unknown Result";
	}

	return proto;
}

int MasterBinder::CompareToken(const Metallurgy::Token *ltoken, const Metallurgy::Token *rtoken) {
	CorTokenType ltype = ltoken->getTokenType();
	CorTokenType rtype = rtoken->getTokenType();

	if (ltype == rtype) {
		const Metallurgy::IName *lname = dynamic_cast<const Metallurgy::IName *>(ltoken);
		const Metallurgy::IName *rname = dynamic_cast<const Metallurgy::IName *>(rtoken);
		return _wcsicmp(lname->getName().getFull().c_str(), rname->getName().getFull().c_str());
	} else {
		return (ltoken->getTokenType() < rtoken->getTokenType()) ? -1 : 1;
	}
}

void MasterBinder::XmlDocument(const Metallurgy::Token *token, DOM_Node &node) {
	node = NULL;
	if (token == NULL)
		return;

	const Metallurgy::Assembly *assembly = token->getAssembly();
	if (assembly == NULL)
		return;

	DOM_Document doc;

	DocumentMap::const_iterator docI = m_Documents.find(assembly);
	if (docI == m_Documents.end()) {
		DOMParser parser;
		parser.setValidationScheme(DOMParser::Val_Auto);
		parser.setDoNamespaces(false);
		parser.setDoSchema(false);

		std::wstring path = assembly->getPath();
		parser.parse((path.substr(0, path.size() - 4) + L".xml").c_str());

		if (parser.getErrorCount() == 0)
			m_Documents[assembly] = (doc = parser.getDocument());
		else
			m_Documents[assembly] = NULL;
	} else {
		doc = docI->second;
	}

	if (doc.isNull())
		return;

	if (token->getTokenType() == Chordata::Tokens::mdtAssembly) {
		// TODO: return doc/assembly
		return;
	}

	const Metallurgy::IXmlMemberKey *member = dynamic_cast<const Metallurgy::IXmlMemberKey *>(token);
	if (member == NULL)
		return;

	std::wstring key(member->getXmlMemberKey());

	DOM_NodeList nodes = doc.getElementsByTagName(L"member");
	for (uint32_t i(0); i < nodes.getLength(); i++)
		if (key == nodes.item(i).getAttributes().getNamedItem("name").getNodeValue().rawBuffer()) {
			node = nodes.item(i);
			return;
		}
}

const Metallurgy::TypeDef *MasterBinder::Resolve(const Metallurgy::Type *type) {
	const Metallurgy::TypeDef *def = dynamic_cast<const Metallurgy::TypeDef *>(type);
	if (def != NULL)
		return def;

	return def;
}

LRESULT MasterBinder::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) {
	/*HWND hWndCmdBar = m_CmdBar.Create(m_hWnd, rcDefault, NULL, ATL_SIMPLE_CMDBAR_PANE_STYLE);
	m_CmdBar.AttachMenu(GetMenu());
	m_CmdBar.LoadImages(IDR_MAINFRAME);
	SetMenu(NULL);*/

	//HWND hWndToolBar = CreateSimpleToolBarCtrl(m_hWnd, IDR_MAINFRAME, FALSE, ATL_SIMPLE_TOOLBAR_PANE_STYLE);

	HWND hWndToolBar = m_ToolBar.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NORESIZE | CCS_NOPARENTALIGN | /*TBSTYLE_TOOLTIPS |*/ TBSTYLE_LIST | TBSTYLE_FLAT);
	m_ToolBar.SetButtonStructSize();
	m_ToolBar.SetExtendedStyle(TBSTYLE_EX_MIXEDBUTTONS);
	m_ToolBar.LoadStdImages(IDB_HIST_SMALL_COLOR);
	m_ToolBar.AddStrings(L"Back\0Forward\0\0");

	TBBUTTON button[2];

	button[0].iBitmap = HIST_BACK;
	button[0].idCommand = 3370;
	button[0].fsState = 0;
	button[0].fsStyle = BTNS_AUTOSIZE | BTNS_BUTTON | BTNS_SHOWTEXT;
	button[0].dwData = 0;
	button[0].iString = 0;

	button[1].iBitmap = HIST_FORWARD;
	button[1].idCommand = 3371;
	button[1].fsState = 0;
	button[1].fsStyle = BTNS_AUTOSIZE | BTNS_BUTTON;
	button[1].dwData = 0;
	button[1].iString = 1;

	m_ToolBar.AddButtons(2, button);

	CreateSimpleReBar(ATL_SIMPLE_REBAR_NOBORDER_STYLE);
	//AddSimpleReBarBand(hWndCmdBar);
	AddSimpleReBarBand(hWndToolBar, NULL, TRUE);
	//CreateSimpleStatusBar();

	////////////////////////////////
	// Tree Icons
	m_TokenIcons.Create(16, 16, ILC_COLOR32 | ILC_MASK, 50, 10);
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TBOOKCLOSED));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TBOOKOPEN));

	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TREFSCLOSED));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TREFSOPEN));

	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TFOLDCLOSED));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TFOLDOPEN));

	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TDOCUMENT));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TREFERENCE));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TNAMESPACE));

	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TSEALED));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TINTRINSIC));

	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPUBLICCLASS));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPRIVATECLASS));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPROTECTEDCLASS));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TINTERNALCLASS));

	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPUBLICINTERFACE));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPRIVATEINTERFACE));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPROTECTEDINTERFACE));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TINTERNALINTERFACE));

	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPUBLICENUM));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPRIVATEENUM));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPROTECTEDENUM));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TINTERNALENUM));

	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPUBLICSTRUCT));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPRIVATESTRUCT));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPROTECTEDSTRUCT));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TINTERNALSTRUCT));

	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPUBLICMETHOD));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPRIVATEMETHOD));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPROTECTEDMETHOD));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TINTERNALMETHOD));

	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPUBLICEVENT));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPRIVATEEVENT));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPROTECTEDEVENT));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TINTERNALEVENT));

	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPUBLICPROPERTY));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPRIVATEPROPERTY));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPROTECTEDPROPERTY));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TINTERNALPROPERTY));

	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPUBLICFIELD));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPRIVATEFIELD));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPROTECTEDFIELD));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TINTERNALFIELD));

	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPUBLICCONST));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPRIVATECONST));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TPROTECTEDCONST));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_TINTERNALCONST));

	////////////////////////////////
	// Browser Icons

	/* // I don't use this anymore.
	m_BrowserIcons.Create(16, 16, ILC_COLOR | ILC_MASK, 50, 10);
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_BBACKDISABLE));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_BBACKABLE));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_BFORWARDDISABLE));
	m_TokenIcons.AddIcon(AtlLoadIcon(IDI_BFORWARDABLE));*/

	////////////////////////////////
	// Interface Construction

	RECT rcSplitter;
	GetClientRect(&rcSplitter);

	m_hWndStatusBar = m_StatusBar.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP);

	HWND hWndHSplitter = m_HSplitter.Create(m_hWnd, rcSplitter, NULL, WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS, 0 /*WS_EX_DLGMODALFRAME*/);
	HWND hWndVSplitter = m_VSplitter.Create(hWndHSplitter, rcSplitter, NULL, WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS, 0);
	HWND hWndInfoView = m_InfoView.Initialize(this, hWndHSplitter);

	////////////////////////////////
	// General Setup

	UIAddToolBar(hWndToolBar);
	//UISetCheck(ID_LANGUAGE_CSHARP, 1);

	// register object for message filtering and idle updates
	CMessageLoop* pLoop = _Module.GetMessageLoop();
	ATLASSERT(pLoop != NULL);
	//pLoop->AddMessageFilter(this);
	pLoop->AddIdleHandler(this);

	DOMParser parser;
	parser.setValidationScheme(DOMParser::Val_Auto);
	parser.setDoNamespaces(false);
	parser.setDoSchema(false);

	parser.parse((Config::GetBaseDirectory() + "\\Anakrino.xml").c_str());

	if (parser.getErrorCount() == 0) {
		DOM_Document doc = parser.getDocument();

		DOM_NodeList nodes;
		uint32_t i;

		MENUITEMINFO i_menu;
		i_menu.cbSize = sizeof(MENUITEMINFO);
		i_menu.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
		i_menu.fType = MFT_STRING | MFT_RADIOCHECK;
		i_menu.fState = MFS_ENABLED;
		i_menu.wID = ID_DIALECT_AUTOMATIC;
		i_menu.hbmpChecked = NULL;
		i_menu.hbmpUnchecked = NULL;
		i_menu.dwItemData = NULL;
		i_menu.cch = 0;
		i_menu.hbmpItem = NULL;

		//CMenuHandle menu = m_CmdBar.GetMenu();
		CMenuHandle menu = GetMenu();
		CMenuHandle dialects = menu.GetSubMenu(1);

		nodes = doc.getElementsByTagName(L"dialect");
		for (i = 0; i < nodes.getLength(); i++) {
			DOM_NamedNodeMap dialect = nodes.item(i).getAttributes();
			m_Dialects.push_back(ManualOverride.Resolve(dialect.getNamedItem("type").getNodeValue().rawBuffer()));

			i_menu.wID++;
			i_menu.dwTypeData = const_cast<wstr_t>(dialect.getNamedItem("name").getNodeValue().rawBuffer());
			dialects.InsertMenuItem(2 + i, TRUE, &i_menu);
		}

		m_Viewers.Initialize(this, hWndVSplitter, doc.getElementsByTagName(L"viewer"));
		m_Browsers.Initialize(this, hWndVSplitter, doc.getElementsByTagName(L"browser"));

		nodes = doc.getElementsByTagName(L"tool");
		for (i = 0; i < nodes.getLength(); i++) {
			DOM_NamedNodeMap n_tool = nodes.item(i).getAttributes();

			Diapexis::Handle<ITool> tool(n_tool.getNamedItem("type").getNodeValue().rawBuffer());
			tool->Initialize(this);
			m_Tools.push_back(tool);
		}

		nodes = doc.getElementsByTagName(L"assembly");
		for (i = 0; i < nodes.getLength(); i++) {
			DOM_NamedNodeMap assembly = nodes.item(i).getAttributes();

			DOM_Node name = assembly.getNamedItem("name");
			if (name != NULL) {
				Load(Fission::Locate(name.getNodeValue().rawBuffer()).c_str());
				continue;
			}

			DOM_Node path = assembly.getNamedItem("path");
			if (path != NULL) {
				Load(path.getNodeValue().rawBuffer());
			}
		}
	}

	////////////////////////////////
	// Splitter Setup

	POINT size = { rcSplitter.right - rcSplitter.left , rcSplitter.bottom - rcSplitter.top };
	m_VSplitter.SetSplitterPos(std::max(size.x - 520, static_cast<LONG>(size.x * 0.35)));
	m_HSplitter.SetSplitterPos(std::max(size.y - 200, static_cast<LONG>(size.y * 0.8)));

	m_VSplitter.SetSplitterPanes(m_Browsers, m_Viewers);
	m_HSplitter.SetSplitterPanes(hWndVSplitter, hWndInfoView);

	m_hWndClient = hWndHSplitter;

	//SizeSimpleReBarBands();
	return 0;
}

}