--
-- MI2_Search.lua
--
-- MobInfo module to control the Mob database search feature.
-- Search option settings and actual search algorithm are located in here.
--

--
-- start up defaults for search options settings
MI2_SearchOptions = {}
MI2_SearchOptions.MinLevel = 1
MI2_SearchOptions.MaxLevel = 65
MI2_SearchOptions.Normal = 1
MI2_SearchOptions.Elite = 0
MI2_SearchOptions.Boss = 0
MI2_SearchOptions.MinLoots = 2
MI2_SearchOptions.MobName = ""
MI2_SearchOptions.ItemName = ""
MI2_SearchOptions.CompactResult = 1
MI2_SearchOptions.ListMode = "Mobs"
MI2_SearchOptions.SortMode = "profit"

local MI2_SearchResultList = {}
local MI2_ItemsIdxList = ":"
MI2_NumMobsFound = 0


-----------------------------------------------------------------------------
-- MI2_SearchForItems()
--
-- Search for all items matching the item name entered in the search dialog.
-- Display the list of items in the result list control (if requested).
-----------------------------------------------------------------------------
local function MI2_SearchForItems( itemName, enterItemsIntoList )
	MI2_ItemsIdxList = ":"
	MI2_NumMobsFound = 0
	if enterItemsIntoList then
		MI2_SearchResultList = {}
	end

	if itemName ~= "" or enterItemsIntoList then
		for idx in MI2_ItemNameTable do
			local itemFound = true
			local itemText, itemColor = MI2_GetLootItemString( idx )
			if itemName ~= "*" then
				itemFound = string.find( string.lower(itemText), string.lower(itemName) ) ~= nil
			end
			if itemFound then
				MI2_ItemsIdxList = MI2_ItemsIdxList..idx..":"
				if enterItemsIntoList then
					MI2_NumMobsFound = MI2_NumMobsFound + 1
					MI2_SearchResultList[MI2_NumMobsFound] = { idx = itemText, val = "", col = itemColor }
				end
			end
		end
	end
	
	MI2_DisplaySearchResult( "Items" )
end -- MI2_SearchForItems()


-----------------------------------------------------------------------------
-- MI2_UpdateSearchResultList()
--
-- Update contents of search result list according to current search options
-- settings. This includes starting a new search run, sorting the result and
-- displaying the result in the scrollable result list.
-----------------------------------------------------------------------------
local function MI2_UpdateSearchResultList( updateItems )
	local startTime = GetTime()

	if updateItems then
		local enterItemsIntoList = MI2_SearchOptions.ListMode == "Items"
		MI2_SearchForItems( MI2_SearchOptions.ItemName, enterItemsIntoList )
	end

	if MI2_SearchOptions.ListMode == "Mobs" then
		MI2_SearchAndSort( "profit" )
	end

	MI2_DisplaySearchResult( MI2_SearchOptions.ListMode )
--	chattext( "<MobInfo> total search time = "..(GetTime()-startTime).." seconds" )
end -- MI2_UpdateSearchResultList()


-----------------------------------------------------------------------------
-- MI2_SearchOptionsOnShow()
--
-- OnShow event handler for search options page
-- Write current search option settings into the search option controls.
-- Validate all values and update colors accordingly.
-- Allow Search only if all search options are valid.
-----------------------------------------------------------------------------
function MI2_SearchOptionsOnShow()
	MI2_OptSearchMinLevel:SetText( tostring(MI2_SearchOptions.MinLevel) )
	MI2_OptSearchMaxLevel:SetText( tostring(MI2_SearchOptions.MaxLevel) )
	MI2_OptSearchMinLoots:SetText( tostring(MI2_SearchOptions.MinLoots) )
	MI2_OptSearchMobName:SetText( MI2_SearchOptions.MobName )
	MI2_OptSearchItemName:SetText( MI2_SearchOptions.ItemName )

	MI2_OptSearchNormal:SetChecked( MI2_SearchOptions.Normal )
	MI2_OptSearchElite:SetChecked( MI2_SearchOptions.Elite )
	MI2_OptSearchBoss:SetChecked( MI2_SearchOptions.Boss )

	MI2_UpdateSearchResultList()
end -- MI2_SearchOptionsOnShow()


-----------------------------------------------------------------------------
-- MI2_ValidateSearchOptions()
--
-- Validate all values and update colors accordingly.
-- Allow Search only if all search options are valid.
-----------------------------------------------------------------------------
local function MI2_ValidateSearchOptions()
	if MI2_SearchOptions.MinLevel < 1 then
		MI2_SearchOptions.MinLevel = 1
		if this:GetText() == "0" then
			this:SetText( "1" )
		end
	end
	if MI2_SearchOptions.MaxLevel < 1 then
		MI2_SearchOptions.MaxLevel = 1
		if this:GetText() == "0" then
			this:SetText( "1" )
		end
	end
end -- MI2_ValidateSearchOptions()


-----------------------------------------------------------------------------
-- MI2_SearchCheckboxClicked()
--
-- OnClicked event handler for checkboxes on search options page
-- Store the checkbox state in the corresponding search options variable.
-----------------------------------------------------------------------------
function MI2_SearchCheckboxClicked()
	local checkboxName = this:GetName()
	local optionName = string.sub( checkboxName, 14 )
	local optionValue = this:GetChecked() or 0

	MI2_SearchOptions[optionName] = optionValue
	MI2_UpdateSearchResultList()
end -- MI2_SearchCheckboxClicked()


-----------------------------------------------------------------------------
-- MI2_SearchValueChanged()
--
-- OnChar event handler for editbox controls on search options page
-- This handler is called whenever the contents of an EditBox control changes.
-- It gets the new value and stores it in the corresponding search options
-- variable
-----------------------------------------------------------------------------
function MI2_SearchValueChanged()
	local editboxName = this:GetName()
	local optionName = string.sub( editboxName, 14 )
	local optionValue = tonumber(this:GetText()) or 0

	if MI2_SearchOptions[optionName] ~= optionValue then
		MI2_SearchOptions[optionName] = optionValue
		MI2_ValidateSearchOptions()
		MI2_UpdateSearchResultList()
	end
end -- MI2_SearchValueChanged()


-----------------------------------------------------------------------------
-- MI2_SearchTextChanged()
--
-- OnChar event handler for textual editbox controls on search options page
-- This handler is called whenever the contents of an EditBox control changes.
-- It gets the new value and stores it in the corresponding search options
-- variable
-----------------------------------------------------------------------------
function MI2_SearchTextChanged()
	local editboxName = this:GetName()
	local optionName = string.sub( editboxName, 14 )

	if MI2_SearchOptions[optionName] ~= this:GetText() then
		MI2_SearchOptions[optionName] = this:GetText()
		MI2_UpdateSearchResultList( true )
	end
end -- MI2_SearchTextChanged()


-----------------------------------------------------------------------------
-- MI2_CalculateRank()
--
-- Calculate ranking and corresponding actual value for a given mob.
-- Ranking depends on search mode. For search mode "profit" ranking is
-- based on the mobs total profit value plus bonus points for rare loot
-- items. For search mode "itemCount" ranking is identical to the overall
-- items count for the loot items being searched for (in this mode
-- rank and value are identical).
-----------------------------------------------------------------------------
local function MI2_CalculateRank( mobData, mobLevel, sortMode )
	local rank, value = 0, 0

	if sortMode == "profit" then
		-- calculate rank based on mob level and loot items quality
		local bonusFactor = mobLevel / 20
		if mobData.loots > 0 then
			value = (mobData.copper or 0) + (mobData.itemValue or 0)
			rank = value + ((mobData.r3 or 0) * 200 * bonusFactor) + ((mobData.r4 or 0) * 1000 * bonusFactor) + ((mobData.r5 or 0) * 2000 * bonusFactor)
			rank = ceil( rank / mobData.loots )
			value = copper2text( ceil(value / mobData.loots) )
		end
	elseif sortMode == "item" and mobData.itemList then
		for idx, val in mobData.itemList do
			local itemFound = string.find( MI2_ItemsIdxList, idx ) ~= nil
			if itemFound then  rank = rank + val  end
		end
		value = rank.."  "
	end

	return rank, value
end -- MI2_CalculateRank()


-----------------------------------------------------------------------------
-- MI2_CheckMob()
--
-- Check a given Mob against the current search criteria. Return the
-- mob data if the mob matches the criteria, or return nil if the Mob
-- does not match.
-----------------------------------------------------------------------------
local function MI2_CheckMob( mobInfo, mobName, mobLevel )
	local levelOk, lootsOk, typeOk, itemsOK, mobData
	local nameOk = true

	-- check name and level of Mob
	if MI2_SearchOptions.MobName ~= "" then
		nameOk = string.find(string.lower(mobName),string.lower(MI2_SearchOptions.MobName),1,true) ~= nil
	end
	if nameOk and mobName ~= "" then
		levelOk = mobLevel >= MI2_SearchOptions.MinLevel and mobLevel <= MI2_SearchOptions.MaxLevel
	end

	-- check mob data related search conditions	
	if levelOk or mobLevel == -1 then
		mobData = {}
		MI2_DecodeBasicMobData( mobInfo, mobData )
		mobData.loots = mobData.loots or 0
		lootsOk = mobData.loots >= MI2_SearchOptions.MinLoots
		typeOk = (MI2_SearchOptions.Normal == 1 and mobData.mobType == 1) or (MI2_SearchOptions.Elite == 1 and mobData.mobType == 2) or (MI2_SearchOptions.Boss == 1 and mobData.mobType == 3)
		if lootsOk and typeOk then
			if MI2_ItemsIdxList ~= ":" then
				MI2_DecodeItemList( mobInfo, mobData )
				if mobData.itemList then
					for idx, val in mobData.itemList do
						itemsOK = string.find( MI2_ItemsIdxList, ":"..idx..":" ) ~= nil
						if itemsOK then break end
					end
				end
				if not itemsOK then mobData = nil end
			end
		else
			mobData = nil
		end
	end

	return mobData
end -- MI2_CheckMob()


-----------------------------------------------------------------------------
-- MI2_SearchAndSort()
--
-- Search for most valuable mobs by comparing the mobs average total value.
-- The function creates a result list to be displayed in the search results
-- list.
-----------------------------------------------------------------------------
function MI2_SearchAndSort( )
	local mobName, mobLevel, insertPos, value, rank, mobIndex, mobInfo

	-- initialise search result list  
	MI2_SearchResultList = {}
	MI2_NumMobsFound = 0
	
	-- create a sorted list of mobs matching the search criteria
	-- loop across all mobs in the MobInfo database
	for mobIndex, mobInfo in MobInfoDB do
		mobName, mobLevel = MI2_GetIndexComponents( mobIndex )
		mobData = MI2_CheckMob( mobInfo, mobName, mobLevel )

		-- if mob is identified as belonging into the search result its
		-- search result sorting position is calculated based on a ranking
		-- value which in turn is based on the search mode
		if mobData then
			rank, value = MI2_CalculateRank( mobData, mobLevel, MI2_SearchOptions.SortMode )
			MI2_NumMobsFound = MI2_NumMobsFound + 1

			-- insert mob at correct sorted position and store all info we need for printing the result list
			MI2_SearchResultList[MI2_NumMobsFound] = { idx=mobIndex, val=value, rank=rank }
			if mobData.mobType and mobData.mobType > 1 then
				MI2_SearchResultList[MI2_NumMobsFound].type = mobData.mobType
			end
		end
	end

	if MI2_NumMobsFound > 1 then
		table.sort( MI2_SearchResultList, function(a,b) return (a.rank > b.rank) end  )
	end
end  -- MI2_SearchAndSort()


-----------------------------------------------------------------------------
-- MI2_DisplaySearchResult()
--
-- Display the result of a search in the search results scrollable list.
-- The mobs to be displayed depend on the current list scroll position.
-----------------------------------------------------------------------------
function MI2_DisplaySearchResult( resultType )
	-- update slider and get slider position
	FauxScrollFrame_Update( MI2_SearchResultSlider, MI2_NumMobsFound, 15, 14 );
	local sliderPos = FauxScrollFrame_GetOffset(MI2_SearchResultSlider)

	if resultType then
		MI2_TxtSearchCount:SetText( mifontSubWhite.."("..MI2_NumMobsFound.." "..resultType..")" )
	end

	-- update 15 search result lines with correct search result data
	local resultLine
	for i = 1, 15 do
		if 	(i + sliderPos) <= MI2_NumMobsFound then
			resultLine = getglobal( "MI2_SearchResult"..i.."Index" )
			resultLine:SetText( i + sliderPos )
			resultLine = getglobal( "MI2_SearchResult"..i.."Value" )
			resultLine:SetText( MI2_SearchResultList[i + sliderPos].val )
			resultLine = getglobal( "MI2_SearchResult"..i.."Name" )
			local mobName = MI2_SearchResultList[i + sliderPos].idx
			if MI2_SearchResultList[i + sliderPos].type then
				mobName = mobName.."+"
			elseif MI2_SearchResultList[i + sliderPos].col then
				mobName = MI2_SearchResultList[i + sliderPos].col..mobName
			end
			resultLine:SetText( mobName )
		else
			resultLine = getglobal( "MI2_SearchResult"..i.."Index" )
			resultLine:SetText( "" )
			resultLine = getglobal( "MI2_SearchResult"..i.."Value" )
			resultLine:SetText( "" )
			resultLine = getglobal( "MI2_SearchResult"..i.."Name" )
			resultLine:SetText( "" )
		end
	end
end  -- MI2_DisplaySearchResult()


-----------------------------------------------------------------------------
-- MI2_SlashAction_SortByValue()
--
-- Sort the search result list by mob profit
-----------------------------------------------------------------------------
function MI2_SlashAction_SortByValue()
	MI2_SearchOptions.SortMode = "profit"
	MI2_UpdateSearchResultList()
end -- end of MI2_SlashAction_SortByValue()


-----------------------------------------------------------------------------
-- MI2_SlashAction_SortByItem()
--
-- Sort the search result list by mob item count
-----------------------------------------------------------------------------
function MI2_SlashAction_SortByItem()
	MI2_SearchOptions.SortMode = "item"
	MI2_UpdateSearchResultList()
end -- end of MI2_SlashAction_SortByItem()


-----------------------------------------------------------------------------
-- MI2_SearchResult_Update()
--
-- Update contents of search results list based on current scroll bar
-- position. Update tooltip for selected mob if tooltip is visible.
-----------------------------------------------------------------------------
function MI2_SearchResult_Update()
	FauxScrollFrame_Update( MI2_SearchResultSlider, MI2_NumMobsFound, 15, 14 );
	MI2_DisplaySearchResult()
end -- end of MI2_SearchResult_Update()


-----------------------------------------------------------------------------
-- MI2_ShowSearchResultTooltip()
--
-- Show mob tooltip for search result mob currently under mouse cursor.
-----------------------------------------------------------------------------
function MI2_ShowSearchResultTooltip()
	local sliderPos = FauxScrollFrame_GetOffset(MI2_SearchResultSlider)
	local selection = tonumber(string.sub(this:GetName(), 17)) + sliderPos
	  
	if selection <= MI2_NumMobsFound then
		GameTooltip_SetDefaultAnchor( GameTooltip, UIParent )

		if MI2_SearchOptions.ListMode == "Mobs" then
			local index = MI2_SearchResultList[selection].idx
			local mobName, mobLevel = MI2_GetIndexComponents( index )
			local mobCaption = mobName.."  L"..mobLevel
			if MI2_SearchResultList[selection].type then
				mobCaption = mobCaption.."+"
			end
			-- create Mob data tooltip with full location info
			GameTooltip:SetText( mobCaption )
			MI2_BuildMobInfoTooltip( mobName, mobLevel, 1 )
		elseif MI2_SearchOptions.ListMode == "Items" then
			local itemName = MI2_SearchResultList[selection].idx
			GameTooltip:SetText( MI2_SearchResultList[selection].col..itemName )
			MI2_BuildItemDataTooltip( itemName )
		end
		GameTooltip:Show()
	end
end  -- end of MI2_ShowSearchResultTooltip()


-----------------------------------------------------------------------------
-- MI2_SearchTab_OnClick()
--
-- The "OnClick" event handler for the TAB buttons on the search result list.
-- These TAB buttons switch the list content between two modes: mob list
-- and item list
-----------------------------------------------------------------------------
function MI2_SearchTab_OnClick()
	PanelTemplates_Tab_OnClick( MI2_SearchResultFrame )
	local selected = MI2_SearchResultFrame.selectedTab
	if selected == 1 then
		MI2_OptSortByValue:Enable()
		MI2_OptSortByItem:Enable()
		if MI2_NumMobsFound > 0 then
			MI2_OptDeleteSearch:Enable()
		else
			MI2_OptDeleteSearch:Disable()
		end
		MI2_SearchOptions.ListMode = "Mobs"
		MI2_UpdateSearchResultList( true )
	elseif selected == 2 then
		MI2_OptSortByValue:Disable()
		MI2_OptSortByItem:Disable()
		MI2_OptDeleteSearch:Disable()
		MI2_SearchOptions.ListMode = "Items"
		MI2_UpdateSearchResultList( true )
	end
end -- MI2_SearchTab_OnClick()


-----------------------------------------------------------------------------
-- MI2_DeleteSearchResultMobs()
--
-- Delete all Mobs in the search result list from the MobInfo database.
-- This function is called when the user confirms the delete.
-----------------------------------------------------------------------------
function MI2_DeleteSearchResultMobs()
	for idx, val in MI2_SearchResultList do
		local mobIndex = val.idx
		MI2_DeleteMobData( mobIndex, true )
	end
	chattext( mifontLightBlue.."<MobInfo> deleted "..MI2_NumMobsFound.." Mobs from the MobInfo databases" )
	MI2_UpdateSearchResultList()
end -- MI2_DeleteSearchResultMobs()


