#include "Commands_Menu.h"
#include "ParamInfos.h"
#include "Script.h"


#if OBLIVION

#include "GameObjects.h"
#include "GameTiles.h"
#include "GameMenus.h"
#include "GameAPI.h"
#include "GameForms.h"
#include "StringVar.h"
#include "ScriptUtils.h"

typedef void (* _CloseAllMenus)(void);
#if OBLIVION_VERSION == OBLIVION_VERSION_1_1

static _CloseAllMenus CloseAllMenus = (_CloseAllMenus)0x0056C910;

#elif OBLIVION_VERSION == OBLIVION_VERSION_1_2

static _CloseAllMenus CloseAllMenus = (_CloseAllMenus)0x00579670;

#elif OBLIVION_VERSION == OBLIVION_VERSION_1_2_416

static _CloseAllMenus CloseAllMenus = (_CloseAllMenus)0x00579770;

const _ShowMessageBox_Callback ContainerMenuCallback =	(_ShowMessageBox_Callback)0x00597B10;
const _ShowMessageBox_Callback SpellPurchaseCallback =	(_ShowMessageBox_Callback)0x005D9730;
const _ShowMessageBox_Callback PoisonConfirmCallback =	(_ShowMessageBox_Callback)0x00666890;
const _ShowMessageBox_Callback OverwriteSaveGameCallback = (_ShowMessageBox_Callback)0x005D3390;
const _ShowMessageBox_Callback LoadGameCallback =		(_ShowMessageBox_Callback)0x005AE190;
const _ShowMessageBox_Callback MissingContentCallback = (_ShowMessageBox_Callback)0x00578DC0;

#else

#error unsupported oblivion version

#endif

static bool Cmd_GetActiveMenuMode_Execute(COMMAND_ARGS)
{
	*result = 0;
	InterfaceManager* intfc = InterfaceManager::GetSingleton();
	if (intfc && intfc->activeMenu)
		*result = intfc->activeMenu->id;

	return true;
}

static enum eMenuValue {
	//no params
	kMenu_Soulgem,
	kMenu_EnchantItem,
	kMenu_Barter,
	kMenu_ContainerView,

	//one optional int param
	kMenu_Selection,
	kMenu_Object,
	kMenu_Ref,
	kMenu_Filter,

	//one int param
	kMenu_Ingredient,
	kMenu_IngredientCount,
	kMenu_Apparatus
};

union MenuInfo {
	const TESForm* form;
	UInt32	 integer;
};

static bool GetActiveMenuElement(COMMAND_ARGS, eMenuValue whichValue, MenuInfo* out, UInt32 whichMenu = 0)
{
	InterfaceManager* intfc = InterfaceManager::GetSingleton();
	Menu* activeMenu = intfc->activeMenu;
	Tile* activeTile = intfc->activeTile;
	bool gotValue = false;
	UInt32 intArg = -1;

	//Extract arguments
	if (whichValue < kMenu_Ingredient && whichValue >= kMenu_Selection)	//optional int param specifies menu type
	{
		ExtractArgs(EXTRACT_ARGS, &intArg);
		if (intArg != -1)
			activeMenu = GetMenuByType(intArg);
	}
	else if (whichValue >= kMenu_Ingredient)
	{
		ExtractArgs(EXTRACT_ARGS, &intArg);
		if (intArg == -1)
			return false;
	}

	if (whichMenu)	//specific menu, so look it up directly
		activeMenu = GetMenuByType(whichMenu);

	if (!activeMenu)
		return false;

	//get element based on menu type
	switch (activeMenu->id)
	{
	case kMenuType_Message:
		{
			MessageMenu* msgMenu = (MessageMenu*)activeMenu;
			switch (whichValue)
			{
			case kMenu_Object:
				if (ShowMessageBox_pScriptRefID && msgMenu->IsScriptMessageBox())
				{
					out->form = LookupFormByID(*ShowMessageBox_pScriptRefID);
					gotValue = true;
				}
				else
					out->form = 0;
			default:
				break;
			}
		}
		break;
	case kMenuType_Alchemy:
		{
			AlchemyMenu* menu = (AlchemyMenu*)activeMenu;
			switch (whichValue)
			{
			case kMenu_Object:
				out->form = menu->potion;
				gotValue = true;
				break;
			case kMenu_Ingredient:
				out->form = menu->GetIngredientItem(intArg);
				gotValue = true;
				break;
			case kMenu_IngredientCount:
				out->integer = menu->GetIngredientCount(intArg);
				gotValue = true;
				break;
			case kMenu_Apparatus:
				out->form = menu->GetApparatus(intArg);
				gotValue = true;
				break;
			default:
				break;
			}
		}
		break;
	case kMenuType_Container:
		{
			ContainerMenu* menu = (ContainerMenu*)activeMenu;
			switch (whichValue)
			{
			case kMenu_ContainerView:
				if (menu->isContainerContents)
					out->integer = 0;
				else
					out->integer = 1;
				gotValue = true;
				break;
			case kMenu_Ref:
				{
					out->form = menu->refr;
					gotValue = true;
					break;
				}
			case kMenu_Filter:
				out->integer = menu->filterType;
				gotValue = true;
				break;
			case kMenu_Selection:
				{
					if (activeTile)
					{
						float fIndex;
						if (activeTile->GetFloatValue(kTileValue_user11, &fIndex))
						{
							UInt32 index = fIndex;
							if (menu->isContainerContents)
								out->form = menu->refr->GetInventoryItem(index, menu->isBarter);
							else
								out->form = (*g_thePlayer)->GetInventoryItem(index, 0);
							gotValue = true;
						}
					}
				}
				break;
			case kMenu_Barter:
				out->integer = menu->isBarter;
				gotValue = true;
				break;
			default:
				break;
			}
			break;
		}
	case kMenuType_Magic:
		{
			MagicMenu* menu = (MagicMenu*)activeMenu;
			switch (whichValue)
			{
			case kMenu_Selection:
				{
					if (activeTile && menu->filterType != MagicMenu::kFilter_ActiveEffects)
					{
						float fIndex;
						if (activeTile->GetFloatValue(kTileValue_user11, &fIndex))
						{
							UInt32 index = fIndex;
							out->form = MenuSpellListVisitor(&menu->spells).GetNthInfo(index);
							gotValue = true;
						}
					}
				}
				break;
			case kMenu_Filter:
				out->integer = menu->filterType;
				gotValue = true;
				break;
			default:
				break;
			}
			break;
		}
	case kMenuType_SpellPurchase:
		{
			SpellPurchaseMenu* menu = (SpellPurchaseMenu*)activeMenu;
			switch (whichValue)
			{
			case kMenu_Selection:
				{
					if (activeTile)
					{
						float fIndex;
						if (activeTile->GetFloatValue(kTileValue_user11, &fIndex))
						{
							UInt32 index = fIndex;
							out->form = MenuSpellListVisitor(&menu->spells).GetNthInfo(index);
							gotValue = true;
						}
					}
					break;
				}
			case kMenu_Ref:
				{
					out->form = menu->spellMerchant;
					gotValue = true;
					break;
				}
			default:
				break;
			}
		}
		break;
	case kMenuType_Enchantment:
		{
			EnchantmentMenu* menu = (EnchantmentMenu*)activeMenu;
			switch (whichValue)
			{
			case kMenu_Soulgem:
				if (menu->soulGemInfo)
				{
					out->form = menu->soulGemInfo->form;
					gotValue = true;
				}
				break;
			case kMenu_EnchantItem:
				out->form = menu->enchantItem;
				gotValue = true;
				break;
			default:
				break;
			}
		}
		break;
	case kMenuType_Book:
		{
			BookMenu* menu = (BookMenu*)activeMenu;
			switch (whichValue)
			{
			case kMenu_Ref:
				out->form = menu->bookRef;
				gotValue = true;
				break;
			case kMenu_Object:
				out->form = menu->book;
				gotValue = true;
				break;
			default:
				break;
			}
		}
		break;
	case kMenuType_Inventory:
		{
			InventoryMenu* menu = (InventoryMenu*)activeMenu;
			switch (whichValue)
			{
			case kMenu_Selection:
				{
					if (activeTile)
					{
						float fIndex;
						if (activeTile->GetFloatValue(kTileValue_user11, &fIndex))
						{
							UInt32 index = fIndex;
							out->form = (*g_thePlayer)->GetInventoryItem(index, 0);
							gotValue = true;
						}
					}
				}
				break;
			case kMenu_Filter:
				out->integer = menu->filterType;
				gotValue = true;
				break;
			default:
				break;
			}
		}
		break;
	case kMenuType_Dialog:
		{
			DialogMenu* menu = (DialogMenu*)activeMenu;
			switch (whichValue)
			{
			case kMenu_Ref:
				out->form = menu->speaker;
				gotValue = true;
				break;
			default:
				break;
			}
		}
		break;
	default:
		break;
	}

	return gotValue;
}

static bool Cmd_GetEnchMenuEnchItem_Execute(COMMAND_ARGS)
{
	UInt32* refResult = (UInt32*)result;
	*refResult = 0;

	MenuInfo info;
	if (GetActiveMenuElement(PASS_COMMAND_ARGS, kMenu_EnchantItem, &info, kMenuType_Enchantment) && info.form)
		*refResult = info.form->refID;

	return true;
}

static bool Cmd_GetEnchMenuSoulgem_Execute(COMMAND_ARGS)
{
	UInt32* refResult = (UInt32*)result;
	*refResult = 0;

	MenuInfo info;
	if (GetActiveMenuElement(PASS_COMMAND_ARGS, kMenu_Soulgem, &info, kMenuType_Enchantment) && info.form)
		*refResult = info.form->refID;

	return true;
}

static bool Cmd_GetActiveMenuSelection_Execute(COMMAND_ARGS)
{
	UInt32* refResult = (UInt32*)result;
	*refResult = 0;

	MenuInfo info;
	if (GetActiveMenuElement(PASS_COMMAND_ARGS, kMenu_Selection, &info) && info.form)
	{
		*refResult = info.form->refID;
		if (IsConsoleMode())
			Console_Print("GetActiveMenuSelection >> %s (%08x)", GetFullName(const_cast<TESForm*>(info.form)), info.form->refID);
	}

	return true;
}

static bool Cmd_GetActiveMenuObject_Execute(COMMAND_ARGS)
{
	UInt32* refResult = (UInt32*)result;
	*refResult = 0;

	MenuInfo info;
	if (GetActiveMenuElement(PASS_COMMAND_ARGS, kMenu_Object, &info) && info.form)
		*refResult = info.form->refID;

	return true;
}

static bool Cmd_GetActiveMenuRef_Execute(COMMAND_ARGS)
{
	UInt32* refResult = (UInt32*)result;
	*refResult = 0;

	MenuInfo info;
	if (GetActiveMenuElement(PASS_COMMAND_ARGS, kMenu_Ref, &info) && info.form)
		*refResult = info.form->refID;

	return true;
}

static bool Cmd_GetActiveMenuFilter_Execute(COMMAND_ARGS)
{
	*result = 0;

	MenuInfo info;
	if (GetActiveMenuElement(PASS_COMMAND_ARGS, kMenu_Filter, &info))
		*result = info.integer;

	return true;
}

static bool Cmd_IsBarterMenuActive_Execute(COMMAND_ARGS)
{
	*result = 0;

	MenuInfo info;
	if (GetActiveMenuElement(PASS_COMMAND_ARGS, kMenu_Barter, &info))
		*result = (info.integer) ? 1 : 0;

	return true;
}

static bool Cmd_GetAlchMenuIngredient_Execute(COMMAND_ARGS)
{
	UInt32* refResult = (UInt32*)result;
	*refResult = 0;

	MenuInfo info;
	if (GetActiveMenuElement(PASS_COMMAND_ARGS, kMenu_Ingredient, &info, kMenuType_Alchemy) && info.form)
		*refResult = info.form->refID;
	
	return true;
}

static bool Cmd_GetAlchMenuIngredientCount_Execute(COMMAND_ARGS)
{
	*result = -1;

	MenuInfo info;
	if (GetActiveMenuElement(PASS_COMMAND_ARGS, kMenu_IngredientCount, &info, kMenuType_Alchemy))
		*result = info.integer;

	return true;
}

static bool Cmd_GetAlchMenuApparatus_Execute(COMMAND_ARGS)
{
	UInt32* refResult = (UInt32*)result;
	*refResult = 0;

	MenuInfo info;
	if (GetActiveMenuElement(PASS_COMMAND_ARGS, kMenu_Apparatus, &info, kMenuType_Alchemy) && info.form)
		*refResult = info.form->refID;

	return true;
}

/*	Need to figure out what BaseExtraList* and unk4 do in ApparatusInfo struct first
static bool Cmd_SetAlchMenuApparatus_Execute(COMMAND_ARGS)
{
	UInt32 whichAppa = 0;
	TESForm* newAppa = NULL;
	*result = 0;

	if (ExtractArgs(PASS_EXTRACT_ARGS, &whichAppa, &newAppa))
	{
		Menu* menu = GetMenuByType(kMenuType_Alchemy);
		if (alchMenu)
		{
			AlchemyMenu* alchMenu = (AlchemyMenu*)menu;
			alchMenu->
*/

static bool Cmd_CloseAllMenus_Execute(COMMAND_ARGS)
{
	CloseAllMenus();
	return true;
}


static bool Cmd_GetContainerMenuView_Execute(COMMAND_ARGS)
{
	*result = -1;

	MenuInfo info;
	if (GetActiveMenuElement(PASS_COMMAND_ARGS, kMenu_ContainerView, &info, kMenuType_Container))
		*result = info.integer;

	return true;
}

static enum {
	kGetFloat,
	kSetFloat,
	kGetString,
	kSetString,
	kExists
};

static bool GetSetMenuValue_Execute(COMMAND_ARGS, UInt32 mode)
{
#if _DEBUG
	_MESSAGE("GetSetMenuValue: %08x", &GetSetMenuValue_Execute);
	Console_Print("GetSetMenuValue: %08x", &GetSetMenuValue_Execute);
#endif

	const char* separatorChar = GetSeparatorChars();
	char stringArg[kMaxMessageLength] = { 0 };
	UInt32 menuType = 0;
	float newFloatVal = 0;
	char* newStringVal = NULL;
	
	char* componentPath = stringArg;

	bool bExtracted = false;
	switch (mode)
	{
	case kGetFloat:
	case kGetString:
	case kExists:
		bExtracted = ExtractFormatStringArgs(0, stringArg, paramInfo, arg1, opcodeOffsetPtr, scriptObj, eventList, kCommandInfo_GetMenuFloatValue.numParams, &menuType);
		break;
	case kSetFloat:
		bExtracted = ExtractFormatStringArgs(0, stringArg, paramInfo, arg1, opcodeOffsetPtr, scriptObj, eventList, kCommandInfo_SetMenuFloatValue.numParams, &menuType, &newFloatVal);
		break;
	case kSetString:
		{
			bExtracted = ExtractFormatStringArgs(0, stringArg, paramInfo, arg1, opcodeOffsetPtr, scriptObj, eventList, kCommandInfo_GetMenuFloatValue.numParams, &menuType);
			// extract new value from format string
			char* context = NULL;
			componentPath = strtok_s(stringArg, separatorChar, &context);
			newStringVal = strtok_s(NULL, separatorChar, &context);
			bExtracted = (bExtracted && componentPath && newStringVal);
		}
		break;
	}

	char* strToAssign = "";

	if (bExtracted)
	{
		Menu* menu = GetMenuByType(menuType);
		if (menu && menu->tile)
		{
			Tile::Value* val = menu->tile->GetValueByName(componentPath);
			if (val)
			{
				DEBUG_PRINT("Value Address: %08x", val);
#if _DEBUG
				val->DumpExpressionList();
#endif
				switch (mode)
				{
				case kExists:
					*result = 1;
					break;
				case kGetFloat:
					if (val->IsNum())
						*result = val->num;
					break;
				case kSetFloat:
					val->bIsNum = 1;
					val->parentTile->UpdateFloat(val->id, newFloatVal);
					break;
				case kGetString:
					if (val->IsString())
						strToAssign = val->str.m_data;
					break;
				case kSetString:
					val->bIsNum = 0;
					val->parentTile->UpdateString(val->id, newStringVal);
					val->parentTile->SetStringValue(val->id, newStringVal);
					val->parentTile->UpdateField(val->id, 0, newStringVal);

					break;
				}
			}
		}
	}

	if (mode == kGetString)		// need to assign even if errors occur during arg extraction/value retrieval
		AssignToStringVar(PASS_COMMAND_ARGS, strToAssign);

	return true;
}

static bool Cmd_GetMenuFloatValue_Execute(COMMAND_ARGS)
{
	return GetSetMenuValue_Execute(PASS_COMMAND_ARGS, kGetFloat);
}

static bool Cmd_GetMenuStringValue_Execute(COMMAND_ARGS)
{
	return GetSetMenuValue_Execute(PASS_COMMAND_ARGS, kGetString);
}

static bool Cmd_SetMenuFloatValue_Execute(COMMAND_ARGS)
{
	return GetSetMenuValue_Execute(PASS_COMMAND_ARGS, kSetFloat);
}

static bool Cmd_SetMenuStringValue_Execute(COMMAND_ARGS)
{
	return GetSetMenuValue_Execute(PASS_COMMAND_ARGS, kSetString);
}

static bool Cmd_GetMenuHasTrait_Execute(COMMAND_ARGS)
{
	*result = 0;
	return GetSetMenuValue_Execute(PASS_COMMAND_ARGS, kExists);
}

static bool Cmd_SetButtonPressed_Execute(COMMAND_ARGS)
{
	UInt32 button = 0;
	if (ExtractArgs(PASS_EXTRACT_ARGS, &button))
	{
		MessageMenu* menu = (MessageMenu*)GetMenuByType(kMenuType_Message);
		if (menu)
			menu->HandleClick(menu->kButtonID_Button1 + button, 0);
	}

	return true;
}

static bool Cmd_GetActiveUIComponentName_Execute(COMMAND_ARGS)
{
	const char* name = "";

	InterfaceManager* intfc = InterfaceManager::GetSingleton();
	if (intfc->activeTile)
		name = intfc->activeTile->name.m_data;

	AssignToStringVar(PASS_COMMAND_ARGS, name);
	return true;
}

static bool Cmd_GetActiveUIComponentFullName_Execute(COMMAND_ARGS)
{
	const char* name = "";
	std::string nameStr;

	InterfaceManager* intfc = InterfaceManager::GetSingleton();
	if (intfc->activeTile)
	{
		nameStr = intfc->activeTile->GetQualifiedName();
		name = nameStr.c_str();
	}

	AssignToStringVar(PASS_COMMAND_ARGS, name);
	return true;
}

static bool Cmd_GetActiveUIComponentID_Execute(COMMAND_ARGS)
{
	*result = -1;

	InterfaceManager* intfc = InterfaceManager::GetSingleton();
	if (intfc->activeTile)
	{
		float id;
		if (intfc->activeTile->GetFloatValue(kTileValue_id, &id))
			*result = id;
	}

	return true;
}

static bool Cmd_ClickMenuButton_Execute(COMMAND_ARGS)
{
	UInt32 menuType;
	char name[kMaxMessageLength] = { 0 };

	if (ExtractFormatStringArgs(0, name, paramInfo, arg1, opcodeOffsetPtr, scriptObj, eventList, kCommandInfo_GetMenuFloatValue.numParams, &menuType))
	{
		Menu* menu = GetMenuByType(menuType);
		if (menu && name)
		{
			UInt32 buttonID = 0;
			Tile* parentTile = NULL;

			if (name[0] == '#')		// component specified by ID rather than name
			{
				buttonID = atoi(name + 1);
				parentTile = menu->tile->GetChildByIDTrait(buttonID);
			}
			else		// roundabout way of getting a button tile - look up the <id> trait then take the parent tile
			{
				UInt32 nameLen = strlen(name);
				name[nameLen] = '\\';
				name[nameLen+1] = 'i';
				name[nameLen+2] = 'd';
				name[nameLen+3] = '\0';

				Tile::Value* val = menu->tile->GetValueByName(name);
				if (val)
				{
					buttonID = val->num;
					parentTile = val->parentTile;
				}
			}

			DEBUG_PRINT("Parent Tile: %08x, ID: %d", parentTile, buttonID);
			if (parentTile)
				menu->HandleClick(buttonID, parentTile);
		}
	}

	return true;
}

static bool Cmd_IsGameMessageBox_Execute(COMMAND_ARGS)
{
	*result = 0;
	MessageMenu* menu = (MessageMenu*)GetMenuByType(kMenuType_Message);
	if (menu)	// if not game generated, expression is true
		*result = (menu->buttonCallback == ShowMessageBox_Callback) ? 0 : 1;

	return true;
}

static enum {
	kMsgBox_Unknown,
	kMsgBox_Script,
	kMsgBox_BuyItem,
	kMsgBox_SellItem,
	kMsgBox_GiveItem,
	kMsgBox_BuySpell,
	kMsgBox_PoisonWeapon,
	kMsgBox_OverwriteGame,
	kMsgBox_LoadGame,
	kMsgBox_MissingContent,

	kMsgBox_Max
};

static bool Cmd_GetMessageBoxType_Execute(COMMAND_ARGS)
{
	*result = kMsgBox_Unknown;

	MessageMenu* menu = (MessageMenu*)GetMenuByType(kMenuType_Message);
	if (!menu || !menu->buttonCallback || !menu->messageText)
		return true;

	const char* msg = NULL;
	if (!menu->messageText->GetStringValue(kTileValue_string, &msg) || !msg)
		return true;

	std::string msgText(msg);
	SettingInfo* setting = NULL;

	if (menu->buttonCallback == ShowMessageBox_Callback)
	{
		*result = kMsgBox_Script;
	}
	else if (menu->buttonCallback == ContainerMenuCallback)
	{
		if (GetGameSetting("sBuy", &setting) && msgText.find(setting->s) == 0)
			*result = kMsgBox_BuyItem;
		else if (GetGameSetting("sSell", &setting) && msgText.find(setting->s) == 0)
			*result = kMsgBox_SellItem;
		else if (GetGameSetting("sGiveAway", &setting) && msgText.find(setting->s) == 0)
			*result = kMsgBox_GiveItem;
	}
	else if (menu->buttonCallback == SpellPurchaseCallback)
	{
		if (GetGameSetting("sConfirmBuySpell", &setting) && msgText.find(setting->s) == 0)
			*result = kMsgBox_BuySpell;
	}
	else if (menu->buttonCallback == PoisonConfirmCallback)
	{
		if (GetGameSetting("sPoisonConfirmMessage", &setting) && msgText.find(setting->s) == 0)
			*result = kMsgBox_PoisonWeapon;
	}
	else if (menu->buttonCallback == OverwriteSaveGameCallback)
	{
		if (GetGameSetting("sSaveOverSaveGame", &setting) && msgText.find(setting->s) == 0)
			*result = kMsgBox_OverwriteGame;
	}
	else if (menu->buttonCallback == LoadGameCallback)
	{
		if (GetGameSetting("sLoadFromMainMenu", &setting) && msgText.find(setting->s) == 0)
			*result = kMsgBox_LoadGame;
		else if (GetGameSetting("sLoadWhilePlaying", &setting) && msgText.find(setting->s) == 0)
			*result = kMsgBox_LoadGame;
	}
	else if (menu->buttonCallback == MissingContentCallback)
	{
		if (GetGameSetting("sSaveGameContentIsMissing", &setting) && msgText.find(setting->s) == 0)
			*result = kMsgBox_MissingContent;
	}

	if (IsConsoleMode())
		Console_Print("GetMessageBoxType >> %.0f", *result);

	return true;
}

static Tile* ExtractMenuComponent(COMMAND_ARGS, const char* componentNameBuffer)
{
	UInt32 menuType = 0;
	Tile* tile = NULL;

	if (ExtractArgs(PASS_EXTRACT_ARGS, componentNameBuffer, &menuType))
	{
		Menu* menu = GetMenuByType(menuType);
		if (menu)
			tile = menu->GetComponentByName(componentNameBuffer);
	}

	return tile;
}

static bool Cmd_GetTileTraits_Execute(COMMAND_ARGS)
{
	if (!ExpressionEvaluator::Active())
	{
		ShowRuntimeError(scriptObj, "GetTileTraits must be called within an OBSE expression.");
		return true;
	}

	char buf[kMaxMessageLength] = { 0 };
	UInt32 arrID = g_ArrayMap.Create(kDataType_String, false, scriptObj->GetModIndex());
	*result = arrID;

	Tile* tile = ExtractMenuComponent(PASS_COMMAND_ARGS, buf);
	if (tile)
	{
		for (Tile::ValueList::Node* node = tile->valueList.start; node && node->data; node = node->next)
		{
			const char* traitName = Tile::StrIDToStr(node->data->id);
			if (node->data->bIsNum)
				g_ArrayMap.SetElementNumber(arrID, traitName, node->data->num);
			else
				g_ArrayMap.SetElementString(arrID, traitName, node->data->str.m_data);
		}
	}

	return true;
}

static bool Cmd_GetTileChildren_Execute(COMMAND_ARGS)
{
	if (!ExpressionEvaluator::Active())
	{
		ShowRuntimeError(scriptObj, "GetTileChildren must be called within an OBSE expression.");
		return true;
	}

	char buf[kMaxMessageLength] = { 0 };
	UInt32 arrID = g_ArrayMap.Create(kDataType_Numeric, true, scriptObj->GetModIndex());
	*result = arrID;

	Tile* tile = ExtractMenuComponent(PASS_COMMAND_ARGS, buf);
	if (tile)
	{
		UInt32 idx = 0;
		for (Tile::RefList::Node* node = tile->childList.start; node && node->data; node = node->next)
		{
			g_ArrayMap.SetElementString(arrID, idx, node->data->name.m_data);
			idx++;
		}
	}

	return true;
}

static bool Cmd_PrintTileInfo_Execute(COMMAND_ARGS)
{
	char componentPath[kMaxMessageLength] = { 0 };
	Tile* tile = ExtractMenuComponent(PASS_COMMAND_ARGS, componentPath);

	Console_Print("PrintTileInfo %s", componentPath);
	_MESSAGE("PrintTileInfo %s", componentPath);
	if (!tile)
	{
		Console_Print("   Component not found");
		_MESSAGE("   Component not found");
		return true;
	}


	Console_Print("  Traits:");
	_MESSAGE("  Traits:");
	std::string output;
	for (Tile::ValueList::Node* node = tile->valueList.start; node && node->data; node = node->next)
	{
		output = "   ";
		output += Tile::StrIDToStr(node->data->id);
		output += ": ";
		if (node->data->bIsNum)
		{
			char num[0x30];
			sprintf_s(num, sizeof(num), "%.4f", node->data->num);
			output += num;
		}
		else
			output += node->data->str.m_data;

		Console_Print(output.c_str());
		_MESSAGE(output.c_str());
	}

	Console_Print("  Children:");
	_MESSAGE("  Children:");
	for (Tile::RefList::Node* node = tile->childList.start; node && node->data; node = node->next)
	{
		output = "   ";
		output += node->data->name.m_data;
		Console_Print(output.c_str());
		_MESSAGE(output.c_str());
	}

	return true;
}
		
#include "obse_common/SafeWrite.h"
#include "GameTasks.h"

#include "Hooks_Gameplay.h"
// testing code to hook ExtractArgs to allow string vars to substitute for literal strings

static __declspec(naked) void ExtractStringHook(void)
{
	// This hooks immediately before a call to memcpy().
	// Original code copies a string literal from script data to buffer
	// If string is of format _localVariableName, replace literal with contens of string var

	static char* dest;			// buffer to hold extracted string
	static char* scriptData;	// pointer to beginning of string in script data
	static UInt32 dataLen;		// length of string
	static ScriptEventList* eventList;

	static UInt32 kRetnAddress = 0x004FB1F4;

	__asm {
		// Grab the args to memcpy()
		mov dest, edi
		mov scriptData, eax
		mov dataLen, ebp

		// Grab the eventList
		mov eax, [esp + 0x50]
		mov eventList, eax

		pushad
	}

	memcpy(dest, scriptData, dataLen);
	dest[dataLen] = 0;
	
	if (dataLen && dest[0] == '$' && eventList && eventList->m_script)
	{
		Script::VariableInfo* varInfo = eventList->m_script->GetVariableByName(dest + 1);
		if (varInfo)
		{
			ScriptEventList::Var* var = eventList->GetVariable(varInfo->idx);
			if (var)
			{
				StringVar* strVar = g_StringMap.Get(var->data);
				if (strVar)
					if (strVar->GetLength() < 0x100)
						strcpy_s(dest, strVar->GetLength(), strVar->GetCString());
			}
		}
	}

	Console_Print("String: %s eventList: %08x", dest, eventList);

	__asm {
		popad
		jmp [kRetnAddress]
	}
}

#if OBLIVION_VERSION == OBLIVION_VERSION_1_2_416

const UInt32 ConsolePrintHookAddress = 0x00579B9B;
const UInt32 ConsolePrintHookReturnAddress = 0x00579BA3;

#else
#error Unsupported Oblivion version
#endif

static void __declspec(naked) ConsolePrintHook( )
{
  char* fmt;
  char* args;

  __asm
  {
    mov ecx, [esp + 4] // get the format string
    lea eax, [esp + 8] // get the address of the first arg

    pushad
    mov fmt, ecx
    mov args, eax
  }

  gLog.Log(IDebugLog::kLevel_Message, fmt, args);

  __asm
  {
    popad
    jmp [ConsolePrintHookReturnAddress]
  }
}

#include "GameData.h"
static bool Cmd_Debug_Execute(COMMAND_ARGS)
{
	TESActorBaseData* actorData = OBLIVION_CAST(thisObj->baseForm, TESForm, TESActorBaseData);
	Console_Print("%08X", actorData->flags);
	return true;

	TESAnimation* anim = OBLIVION_CAST(thisObj->baseForm, TESForm, TESAnimation);
	if (anim)
		Console_Print("Anim: %08x", anim);
	return true;

	if (thisObj)
		DumpClass(thisObj->baseForm, sizeof(TESObjectDOOR) / 4);

	if ((*g_thePlayer)->region)
		DumpClass((*g_thePlayer)->region, sizeof(TESRegion) / 4);

	return true;

	Console_Print("%08X", Tile::StrToStrID("_value_scrolled_to"));
	return true;

	if (thisObj && thisObj->parentCell->worldSpace)
	{
		TESWorldSpace* worldspace = thisObj->parentCell->worldSpace;
		DumpClass(worldspace, sizeof(TESWorldSpace) / 4);
	}

	return true;

	DialogMenu* dMenu = (DialogMenu*)GetMenuByType(kMenuType_Dialog);
	if (dMenu && dMenu->speaker && dMenu->speaker->process && dMenu->speaker->process->package)
		DumpClass(dMenu->speaker->process->package, 25);

	return true;

	TESNPC* hasStyle = (TESNPC*)LookupFormByID(0x3DC43);
	TESNPC* default = (TESNPC*)LookupFormByID(0x908B5);
	DumpClass(hasStyle, sizeof(TESNPC) / 4);
	DumpClass(default, sizeof(TESNPC) / 4);
	return true;

	TESCombatStyle* style = NULL;
	if (!ExtractArgs(PASS_EXTRACT_ARGS, &style))
		return true;

	DumpClass(style->extraSettings, 0x60 / 4);
	Console_Print("Settings: %08X", style->extraSettings);
	return true;

	// testing speedo's code
	WriteRelJump(ConsolePrintHookAddress, (UInt32)ConsolePrintHook);
	return true;


	Menu* qMenu = GetMenuByType(kMenuType_Quantity);
	if (qMenu)
		DumpClass(qMenu, 0x20);

	return true;

	UInt32* activationNestDepth = (UInt32*)0x00B35F00;
	* activationNestDepth = 0;
	Console_Print("Debug >> 0");
	return true;

	// attempt to run scripts attached to a reference
	// works
	ThisStdCall(0x004D7190, thisObj);
	return true;

	DataHandler* dataHandler = *g_dataHandler;
	DumpClass(dataHandler, sizeof(DataHandler) / 4);
	return true;

	InterfaceManager::GetSingleton()->menuRoot->DebugDump();
	return true;

	WriteRelJump(0x004FB1EB, (UInt32)ExtractStringHook);
	return true;

	IOManager* ioMan = IOManager::GetSingleton();
	if (!ioMan)
		Console_Print("No IOMan!");
	else
		ioMan->DumpQueuedTasks();
	
	return true;

	static bool done = false;
	if (!done)
	{
		WriteRelJump(0x004FB201, (UInt32)&ExtractStringHook);
		done = true;
	}

	return true;

	InterfaceManager* intfc = InterfaceManager::GetSingleton();
	DumpClass(intfc, sizeof(InterfaceManager)/4);

	return true;

	if (thisObj)
	{
		TESObjectBOOK* book = (TESObjectBOOK*)Oblivion_DynamicCast(thisObj->baseForm, 0, RTTI_TESForm, RTTI_TESObjectBOOK, 0);
		if (book)
		{
			const char* desc = book->description.GetText(0, 0x43534544);
			if (desc)
				_MESSAGE("%s", desc);
			else
				DEBUG_PRINT("Couldn't read book text");
		}
	}

	return true;

	if (arg3)
	{
		TESObjectREFR* arg3Ref = (TESObjectREFR*)arg3;
		Console_Print("Arg3: %s %08x", GetFullName(arg3Ref), arg3Ref->refID);
	}

	return true;
}

#endif






DEFINE_COMMAND(GetActiveMenuMode,
			   returns the code for the menu over which the mouse is positioned,
			   0,
			   0,
			   NULL);

DEFINE_COMMAND(GetEnchMenuEnchItem,
			   returns the selected enchantment item,
			   0,
			   0,
			   NULL);

DEFINE_COMMAND(GetEnchMenuSoulgem,
			   returns the selected soulgem,
			   0,
			   0,
			   NULL);

DEFINE_COMMAND(GetActiveMenuObject,
			   returns the base object from which the active menu is derived,
			   0,
			   1,
			   kParams_OneOptionalInt);

DEFINE_COMMAND(GetActiveMenuRef,
			   returns the ref from which the active menu is derived,
			   0,
			   1,
			   kParams_OneOptionalInt);

DEFINE_COMMAND(GetActiveMenuSelection,
			   returns the selected item in the active menu,
			   0,
			   1,
			   kParams_OneOptionalInt);

DEFINE_COMMAND(GetActiveMenuFilter,
			   returns the filter code for the active menu,
			   0,
			   1,
			   kParams_OneOptionalInt);

DEFINE_COMMAND(IsBarterMenuActive,
			   returns 1 if the barter menu is active,
			   0,
			   0,
			   NULL);

DEFINE_COMMAND(GetAlchMenuIngredient,
			   returns the nth ingredient selected in the Alchemy interface,
			   0,
			   1,
			   kParams_OneInt);

DEFINE_COMMAND(GetAlchMenuIngredientCount,
			   returns the count of the nth ingredient selected in the Alchemy interface,
			   0,
			   1,
			   kParams_OneInt);

DEFINE_COMMAND(GetAlchMenuApparatus,
			   returns the nth apparatus selected in the alchemy interface,
			   0,
			   1,
			   kParams_OneInt);

DEFINE_COMMAND(GetContainerMenuView,
			   returns 1 if the player is looking at his own inventory,
			   0,
			   0,
			   NULL);

static ParamInfo kParams_SetAlchMenuApparatus[2] =
{
	{	"int",		 kParamType_Integer,		 0	},
	{	"apparatus", kParamType_InventoryObject, 0	},
};

/*
DEFINE_COMMAND(SetAlchMenuApparatus,
			   sets the apparatus being used for brewing potions,
			   0,
			   2,
			   kParams_SetAlchMenuApparatus);
*/

DEFINE_COMMAND(CloseAllMenus,
			   closes all open menus,
			   0,
			   0,
			   NULL);

static ParamInfo kParams_GetMenuValue[SIZEOF_FMT_STRING_PARAMS + 1] =
{
	FORMAT_STRING_PARAMS,
	{	"menuType",	kParamType_Integer,	0	},
};

DEFINE_COMMAND(GetMenuFloatValue,
			   returns a float value for the specified menu component,
			   0,
			   NUM_PARAMS(kParams_GetMenuValue),
			   kParams_GetMenuValue);

DEFINE_COMMAND(GetMenuStringValue,
			   returns a string value for the specified menu component,
			   0,
			   NUM_PARAMS(kParams_GetMenuValue),
			   kParams_GetMenuValue);

static ParamInfo kParams_SetMenuFloatValue[SIZEOF_FMT_STRING_PARAMS + 2] =
{
	FORMAT_STRING_PARAMS,
	{	"menuType",		kParamType_Integer,	0	},
	{	"new value",	kParamType_Float,	0	},
};

DEFINE_COMMAND(SetMenuFloatValue,
			   sets a float value for the specified menu component,
			   0,
			   NUM_PARAMS(kParams_SetMenuFloatValue),
			   kParams_SetMenuFloatValue);

DEFINE_COMMAND(SetMenuStringValue,
			   sets a string value for the specified menu component,
			   0,
			   NUM_PARAMS(kParams_GetMenuValue),
			   kParams_GetMenuValue);

DEFINE_COMMAND(GetMenuHasTrait,
			   returns 1 if the specified trait exists,
			   0,
			   NUM_PARAMS(kParams_GetMenuValue),
			   kParams_GetMenuValue);

DEFINE_COMMAND(SetButtonPressed,
			   forces a messagebox button press,
			   0,
			   1,
			   kParams_OneInt);

DEFINE_COMMAND(GetActiveUIComponentName,
			   returns the name of the highlighted UI component such as a button,
			   0,
			   0,
			   NULL);

DEFINE_COMMAND(GetActiveUIComponentFullName,
			   returns the qualified name of the highlighted UI component,
			   0,
			   0,
			   NULL);

DEFINE_COMMAND(GetActiveUIComponentID,
			   returns the ID of the highlighted UI component as defined in the menu XML,
			   0,
			   0,
			   NULL);

DEFINE_COMMAND(ClickMenuButton,
			   simulates a mouse click of the specified button,
			   0,
			   NUM_PARAMS(kParams_GetMenuValue),
			   kParams_GetMenuValue);

DEFINE_COMMAND(IsGameMessageBox, returns 1 if a game-generated messagebox is currently displayed, 0, 0, NULL);
DEFINE_COMMAND(GetMessageBoxType, returns the type of the messagebox, 0, 0, NULL);

DEFINE_COMMAND(GetTileTraits, returns an array of traits for the specified UI component, 0, 2, kParams_OneString_OneInt);
DEFINE_COMMAND(GetTileChildren, returns an array of children for the specified UI component, 0, 2, kParams_OneString_OneInt);
DEFINE_COMMAND(PrintTileInfo, debug command for printing traits and values, 0, 2, kParams_OneString_OneInt);

DEFINE_COMMAND(Debug, nothing, 0, 0, NULL);