/* 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 "anakrino/TreeBrowser.h"

#include "dialect/IDialect.h"

namespace Anakrino {

void TreeBrowser::Insert(CTreeItem tParent, const Metallurgy::Field *mField) {
	uint32_t icon = binder->GetIcon(mField);
	CTreeItem tField = InsertItem(LPSTR_TEXTCALLBACK, icon, icon, tParent, NULL);
	tField.SetData(reinterpret_cast<uint32_t>(new Node(mField)));
}

void TreeBrowser::Insert(CTreeItem tParent, const Metallurgy::Method *mMethod) {
	uint32_t icon = binder->GetIcon(mMethod);
	CTreeItem tMethod = InsertItem(LPSTR_TEXTCALLBACK, icon, icon, tParent, NULL);
	tMethod.SetData(reinterpret_cast<uint32_t>(new Node(mMethod)));
}

void TreeBrowser::Insert(CTreeItem tParent, const Metallurgy::Module *mModule) {
	uint32_t icon = binder->GetIcon(mModule);
	CTreeItem tModule = InsertItem(mModule->getName().c_str(), icon, icon, tParent, NULL);
	tModule.SetData(reinterpret_cast<uint32_t>(new Node(mModule)));

	CTreeItem tModuleReferences = InsertItem(L"Type References", 2, 2, tModule, NULL);
	tModuleReferences.SetData(reinterpret_cast<uint32_t>(new Node(mModule, ReferencesMode, 5)));
	tModuleReferences.SetChildren(TRUE);

	typedef std::map<std::wstring, HTREEITEM> TreeMap;
	typedef std::pair<std::wstring, HTREEITEM> TreePair;

	TreeMap spaces;

	TV_INSERTSTRUCT tvis;
	tvis.hInsertAfter = NULL;
	tvis.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN | TVIF_PARAM;
	tvis.item.state = 0;
	tvis.item.stateMask = 0;
	tvis.item.cChildren = TRUE;

	Metallurgy::TokenList mTypeDefs = mModule->getTypeDefs();
	for (uint32_t i(0); i < mTypeDefs.size(); i++) {
		Metallurgy::TypeDef *mTypeDef = dynamic_cast<Metallurgy::TypeDef *>(mTypeDefs[i]);

		uint32_t visibility = mTypeDef->getVisibility();
		if (visibility != Chordata::Flags::tdPublic && visibility != Chordata::Flags::tdNotPublic)
			continue;

		std::wstring space = mTypeDef->getName().getSpace();
		if (space.size() == 0)
			tvis.hParent = tModule;
		else {
			TreeMap::iterator iNameSpace = spaces.find(space);
			if (iNameSpace != spaces.end())
				tvis.hParent = iNameSpace->second;
			else {
				tvis.hParent = InsertItem(space.c_str(), 8, 8, tModule, NULL);
				spaces.insert(TreePair(space, tvis.hParent));
			}
		}

		tvis.item.iImage = binder->GetIcon(mTypeDef);
		tvis.item.iSelectedImage = tvis.item.iImage;
		std::wstring name = mTypeDef->getName().getShort();
		tvis.item.pszText = const_cast<wstr_t>(name.c_str());
		tvis.item.lParam = reinterpret_cast<uint32_t>(new Node(mTypeDef));

		InsertItem(&tvis);
	}

	Metallurgy::TokenList mFields = mModule->getFields();
	for (i = 0; i < mFields.size(); i++) {
		Metallurgy::Field *mField = dynamic_cast<Metallurgy::Field *>(mFields[i]);
		Insert(tModule, mField);
	}

	Metallurgy::TokenList mMethods = mModule->getMethods();
	for (i = 0; i < mMethods.size(); i++) {
		Metallurgy::Method *mMethod = dynamic_cast<Metallurgy::Method *>(mMethods[i]);
		Insert(tModule, mMethod);
	}

	for (TreeMap::iterator space = spaces.begin(); space != spaces.end(); space++)
		TokenSort(space->second);

	TokenSort(tModule);
}

void TreeBrowser::OnContextMenu(HWND /*hWnd*/, CPoint ptMousePos) {
	CTreeItem tContext;
	if (ptMousePos.x == -1 && ptMousePos.y == -1)
		tContext = GetSelectedItem();
	else {
		UINT flags;
		ScreenToClient(&ptMousePos);
		tContext = HitTest(ptMousePos, &flags);
		if (tContext == NULL)
			return;
	}

	Node *node = (Node *) tContext.GetData();
	if (node == NULL || node->token == NULL)
		return;

	ClientToScreen(&ptMousePos);
	binder->ShowContext(node->token/*, ptMousePos*/);
}

LRESULT TreeBrowser::OnRClick(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) {
	SendMessage(WM_CONTEXTMENU, (WPARAM) m_hWnd, GetMessagePos());
	return TRUE;
}

LRESULT TreeBrowser::OnGetDispInfo(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) {
	LPNMTVDISPINFO lptvdi = (LPNMTVDISPINFO) pnmh;

	CTreeItem tDisplay(lptvdi->item.hItem, this);
	Node *node = reinterpret_cast<Node *>(tDisplay.GetData());

	if (node == NULL || node->token == NULL)
		return FALSE;

	static wchar_t buff[102400];
	_snwprintf(buff, 102400, L"%s", binder->GetDisplayText(node->token).c_str());
	lptvdi->item.pszText = buff;

	return TRUE;
}

#define RipMethod(tParent, mMethod) \
	if (mMethod != NULL) { \
		methods.erase(mMethod); \
		Insert(tParent, mMethod); \
	}

LRESULT TreeBrowser::OnItemExpanding(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) {
	LPNMTREEVIEW pmtv = (LPNMTREEVIEW) pnmh;

	if (pmtv->action == TVE_COLLAPSE)
		return FALSE;

	CTreeItem tExpanding(pmtv->itemNew.hItem, this);
	Node *node = reinterpret_cast<Node *>(tExpanding.GetData());

	if (node == NULL || node->state != Irrelevant)
		return FALSE;

	bool gotChild(false);
	if (node->token == NULL) {
		return FALSE;
	} else {
		Diapexis::Handle<Dialect::IDialect> dialect(binder->GetDialect(node->token));

		switch (node->token->getTokenType()) {

			case Chordata::Tokens::mdtMethodDef: {
            } break;

            case Chordata::Tokens::mdtModule: {
				switch (node->mode) {
					case ReferencesMode: {
						const Metallurgy::Module *mModule = dynamic_cast<const Metallurgy::Module *>(node->token);
						Metallurgy::TokenList mTypeRefs = mModule->getTypeRefs();

						for (uint32_t i(0); i < mTypeRefs.size(); i++) {
							Metallurgy::TypeRef *mTypeRef = dynamic_cast<Metallurgy::TypeRef *>(mTypeRefs[i]);
							int icon = binder->GetIcon(mTypeRef);
							CTreeItem tTypeRef = InsertItem(mTypeRef->getName().c_str(), icon, icon, tExpanding, NULL);
							tTypeRef.SetData(reinterpret_cast<uint32_t>(new Node(mTypeRef)));
							tTypeRef.SetChildren(TRUE);
							gotChild = true;
						}
					} break;

					default: return FALSE;
				}
			} break;

			case Chordata::Tokens::mdtTypeDef: {
				const Metallurgy::TypeDef *mTypeDef = dynamic_cast<const Metallurgy::TypeDef *>(node->token);
				Metallurgy::TokenList mMethods = mTypeDef->getMethods();

				Metallurgy::TokenSet methods;
				for (uint32_t i(0); i < mMethods.size(); i++)
					methods.insert(mMethods[i]);

				Metallurgy::TokenList mProperties = mTypeDef->getProperties();
				for (i = 0; i < mProperties.size(); i++) {
					Metallurgy::Property *mProperty = dynamic_cast<Metallurgy::Property *>(mProperties[i]);

					CTreeItem tProperty = InsertItem(LPSTR_TEXTCALLBACK, PropertyIconOffset, PropertyIconOffset, tExpanding, NULL);
					tProperty.SetData(reinterpret_cast<uint32_t>(new Node(mProperty)));

					RipMethod(tProperty, mProperty->getGetter());
					RipMethod(tProperty, mProperty->getSetter());

					gotChild = true;
				}

				Metallurgy::TokenList mEvents = mTypeDef->getEvents();
				for (i = 0; i < mEvents.size(); i++) {
					Metallurgy::Event *mEvent = dynamic_cast<Metallurgy::Event *>(mEvents[i]);
					std::wstring delegate;
					dialect->Render(mEvent->getDelegate()->getElement(), delegate);
					CTreeItem tEvent = InsertItem((mEvent->getName().getFull() + L" : " + delegate).c_str(), EventIconOffset, EventIconOffset, tExpanding, NULL);
					tEvent.SetData(reinterpret_cast<uint32_t>(new Node(mEvent)));

					RipMethod(tEvent, mEvent->getAdd())
					RipMethod(tEvent, mEvent->getRemove())
					RipMethod(tEvent, mEvent->getFire())

					gotChild = true;
				}

				Metallurgy::TokenList mFields = mTypeDef->getFields();
				for (i = 0; i < mFields.size(); i++) {
					Metallurgy::Field *mField = dynamic_cast<Metallurgy::Field *>(mFields[i]);
					Insert(tExpanding, mField);
					gotChild = true;
				}

				for (Metallurgy::TokenSet::const_iterator token = methods.begin(); token != methods.end(); ++token) {
					const Metallurgy::Token *tk = *token;
					Insert(tExpanding, dynamic_cast<const Metallurgy::Method *>(*token));
					gotChild = true;
				}

				TV_INSERTSTRUCT tvis;
				tvis.hInsertAfter = NULL;
				tvis.hParent = tExpanding;
				tvis.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN | TVIF_PARAM;
				tvis.item.state = 0;
				tvis.item.stateMask = 0;
				tvis.item.cChildren = TRUE;

				Metallurgy::TokenList mNestedClasses = mTypeDef->getNestedClasses();
				for (i = 0; i < mNestedClasses.size(); i++) {
					Metallurgy::TypeDef *mNestedClass = dynamic_cast<Metallurgy::TypeDef *>(mNestedClasses[i]);

					tvis.item.iImage = binder->GetIcon(mNestedClass);
					tvis.item.iSelectedImage = tvis.item.iImage;
					std::wstring name = mNestedClass->getName().getShort();
					tvis.item.pszText = const_cast<wstr_t>(name.c_str());
					tvis.item.lParam = reinterpret_cast<uint32_t>(new Node(mNestedClass));

					InsertItem(&tvis);
				}
			} break;

			case Chordata::Tokens::mdtTypeRef: {
				const Metallurgy::TypeRef *mTypeRef = dynamic_cast<const Metallurgy::TypeRef *>(node->token);

				Metallurgy::TokenList tMemberRefs = mTypeRef->getMemberRefs();
				for (uint32_t i(0); i < tMemberRefs.size(); i++) {
					Metallurgy::MemberRef *mMemberRef = dynamic_cast<Metallurgy::MemberRef *>(tMemberRefs[i]);

					int icon = binder->GetIcon(mMemberRef);
					CTreeItem tMemberRef = InsertItem(LPSTR_TEXTCALLBACK, icon, icon, tExpanding, NULL);
					tMemberRef.SetData(reinterpret_cast<uint32_t>(new Node(mMemberRef)));
					gotChild = true;
				}
			} break;

			default: return FALSE;
		}

	}

	if (gotChild) {
		TokenSort(tExpanding);
		node->state = Stretched;
		return FALSE;
	} else {
		tExpanding.SetChildren(FALSE);
		node->state = Shrunk;
		return TRUE;
	}
}

LRESULT TreeBrowser::OnSelChanged(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) {
	LPNMTREEVIEW pnmtv = (LPNMTREEVIEW) pnmh;

	CTreeItem tExpanding(pnmtv->itemNew.hItem, this);
	Node *node = (Node *) tExpanding.GetData();

	if (node == NULL)
		return FALSE;

	const Metallurgy::Token *token = node->token;
	binder->Select(token == NULL || node->mode != 0 ? NULL : token);

	return FALSE;
}


LRESULT TreeBrowser::OnItemExpanded(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) {
	LPNMTREEVIEW pnmtv = (LPNMTREEVIEW) pnmh;

	CTreeItem tExpanded(pnmtv->itemNew.hItem, this);
	Node *node = (Node *) tExpanded.GetData();

	if (node == NULL)
		return FALSE;

	if (node->trade != -1) {
		tExpanded.SetImage(node->trade, node->trade);
		node->trade = pnmtv->itemNew.iImage;
	}

	const Metallurgy::Token *mToken = node->token;

	return FALSE;
}

}