--[[
##########################################################
S V U I   By: Failcoder
##########################################################
LOCALIZED LUA FUNCTIONS
##########################################################
]]--
--[[ GLOBALS ]]--
local _G = _G;
local unpack    = _G.unpack;
local select    = _G.select;
local pairs     = _G.pairs;
local ipairs    = _G.ipairs;
local type      = _G.type;
local error     = _G.error;
local pcall     = _G.pcall;
local tostring  = _G.tostring;
local tonumber  = _G.tonumber;
local tinsert 	= _G.tinsert;
local tDeleteItem = _G.tDeleteItem;
local string 	= _G.string;
local math 		= _G.math;
local table 	= _G.table;
--[[ STRING METHODS ]]--
local format = string.format;
--[[ MATH METHODS ]]--
local abs, ceil, floor, round = math.abs, math.ceil, math.floor, math.round;
--[[ TABLE METHODS ]]--
local tremove, twipe = table.remove, table.wipe;
--[[
##########################################################
GET ADDON DATA
##########################################################
]]--
local SV = _G['SVUI']
local L = SV.L
local LSM = _G.LibStub("LibSharedMedia-3.0")
local MOD = SV.QuestTracker;
--[[
##########################################################
LOCALS
##########################################################
]]--
local ROW_WIDTH = 300;
local ROW_HEIGHT = 24;
local INNER_HEIGHT = ROW_HEIGHT - 4;
local LARGE_ROW_HEIGHT = ROW_HEIGHT * 2;
local LARGE_INNER_HEIGHT = LARGE_ROW_HEIGHT - 4;
local OBJ_ICON_ACTIVE = [[Interface\COMMON\Indicator-Yellow]];
local OBJ_ICON_COMPLETE = [[Interface\COMMON\Indicator-Green]];
local OBJ_ICON_INCOMPLETE = [[Interface\COMMON\Indicator-Gray]];
local CACHED_BONUS_DATA = {};
local COMPLETED_BONUS_DATA = {};
--[[
##########################################################
DATA CACHE HANDLERS
##########################################################
]]--
local function CacheBonusData(questID, xp, money)
	if(not questID or (questID and questID <= 0)) then return; end

	local data = {};
	data.objectives = {};
	local isInArea, isOnMap, numObjectives = GetTaskInfo(questID);
	local iscomplete = true;

	if(numObjectives and (type(numObjectives) == "number") and numObjectives > 0) then
		for objectiveIndex = 1, numObjectives do
			local text, objectiveType, finished = GetQuestObjectiveInfo(questID, objectiveIndex, true);
			if not finished then iscomplete = false end
			tinsert(data.objectives, text);
			data.objectiveType = objectiveType;
		end
	end

	data.rewards = {};
	if(not xp) then
		xp = GetQuestLogRewardXP(questID);
	end
	if(xp > 0 and UnitLevel("player") < MAX_PLAYER_LEVEL) then
		local t = {};
		t.label = xp;
		t.texture = "Interface\\Icons\\XP_Icon";
		t.count = 0;
		t.font = "NumberFontNormal";
		tinsert(data.rewards, t);
	end

	local numCurrencies = GetNumQuestLogRewardCurrencies(questID);
	for i = 1, numCurrencies do
		local name, texture, count = GetQuestLogRewardCurrencyInfo(i, questID);
		local t = {};
		t.label = name;
		t.texture = texture;
		t.count = count;
		t.font = "GameFontHighlightSmall";
		tinsert(data.rewards, t);
	end

	local numItems = GetNumQuestLogRewards(questID);
	for i = 1, numItems do
		local name, texture, count, quality, isUsable = GetQuestLogRewardInfo(i, questID);
		local t = {};
		t.label = name;
		t.texture = texture;
		t.count = count;
		t.font = "GameFontHighlightSmall";
		tinsert(data.rewards, t);
	end

	if(not money) then
		money = GetQuestLogRewardMoney(questID);
	end
	if(money > 0) then
		local t = {};
		t.label = GetMoneyString(money);
		t.texture = "Interface\\Icons\\inv_misc_coin_01";
		t.count = 0;
		t.font = "GameFontHighlight";
		tinsert(data.rewards, t);
	end
	CACHED_BONUS_DATA[questID] = data;

	if(iscomplete or #data.rewards <= 0) then
		CACHED_BONUS_DATA[questID] = nil;
		COMPLETED_BONUS_DATA[questID] = true;
		PlaySound("UI_Scenario_Stage_End");
	end
end

local function GetBonusCache()
	local cache = GetTasksTable();
	for questID, data in pairs(CACHED_BONUS_DATA) do
		if(questID > 0) then
			local found = false;
			for i = 1, #cache do
				if(cache[i] == questID) then
					found = true;
					break;
				end
			end
			if(not found) then
				tinsert(cache, questID);
			end
		end
	end
	return cache;
end

local function GetCachedTaskInfo(questID)
	if(CACHED_BONUS_DATA[questID]) then
		return true, true, #CACHED_BONUS_DATA[questID].objectives;
	else
		return GetTaskInfo(questID);
	end
end

local function GetCachedQuestObjectiveInfo(questID, objectiveIndex)
	if(CACHED_BONUS_DATA[questID]) then
		return CACHED_BONUS_DATA[questID].objectives[objectiveIndex], CACHED_BONUS_DATA[questID].objectiveType, true;
	else
		return GetQuestObjectiveInfo(questID, objectiveIndex, false);
	end
end

local function GetScenarioBonusStep(index)
	local cachedObjectives = C_Scenario.GetSupersededObjectives();
	for i = 1, #cachedObjectives do
		local pairs = cachedObjectives[i];
		local k,v = unpack(pairs);
		if(v == index) then
			return k;
		end
	end
end
--[[
##########################################################
TRACKER FUNCTIONS
##########################################################
]]--
local GetBonusRow = function(self, index)
	if(not self.Rows[index]) then
		local previousFrame = self.Rows[#self.Rows]
		local index = #self.Rows + 1;
		local yOffset = 0;

		local row = CreateFrame("Frame", nil, self)
		if(previousFrame and previousFrame.Objectives) then
			row:SetPoint("TOPLEFT", previousFrame.Objectives, "BOTTOMLEFT", 0, -6);
			row:SetPoint("TOPRIGHT", previousFrame.Objectives, "BOTTOMRIGHT", 0, -6);
		else
			row:SetPoint("TOPLEFT", self, "TOPLEFT", 0, 0);
			row:SetPoint("TOPRIGHT", self, "TOPRIGHT", 0, 0);
		end
		row:SetHeight(ROW_HEIGHT);

		row.Header = CreateFrame("Frame", nil, row)
		row.Header:SetPoint("TOPLEFT", row, "TOPLEFT", 2, -2);
		row.Header:SetPoint("TOPRIGHT", row, "TOPRIGHT", -2, -2);
		row.Header:SetHeight(INNER_HEIGHT);

		row.Header.Text = row.Header:CreateFontString(nil,"OVERLAY")
		row.Header.Text:SetFontObject(SVUI_Font_Quest_Header);
		row.Header.Text:SetJustifyH('LEFT')
		row.Header.Text:SetTextColor(0.2,0.75,1)
		row.Header.Text:SetText('')
		row.Header.Text:SetPoint("TOPLEFT", row.Header, "TOPLEFT", 0, 0);
		row.Header.Text:SetPoint("BOTTOMRIGHT", row.Header, "BOTTOMRIGHT", 0, 0);

		row.Objectives = MOD.NewObjectiveHeader(row);
		row.Objectives:SetPoint("TOPLEFT", row, "BOTTOMLEFT", 0, 0);
		row.Objectives:SetPoint("TOPRIGHT", row, "BOTTOMRIGHT", 0, 0);
		row.Objectives:SetHeight(1);

		row.RowID = 0;

		self.Rows[index] = row;

		return row;
	end

	return self.Rows[index];
end

local SetCriteriaRow = function(self, index, bonusStepIndex, subCount, hasFailed)
	index = index + 1
	local objective_rows = 0;
	local fill_height = 0;
	local iscomplete = true;

	local row = self:Get(index);
	row.RowID = 0
	row.Header.Text:SetText(TRACKER_HEADER_BONUS_OBJECTIVES)
	row:SetHeight(ROW_HEIGHT);
	row:SetAlpha(1);

	local objective_block = row.Objectives;
	objective_block:Reset()

	for i = 1, subCount do
		local text, category, completed, quantity, totalQuantity, flags, assetID, quantityString, criteriaID, duration, elapsed, failed = C_Scenario.GetCriteriaInfoByStep(bonusStepIndex, i);
		if(text and text ~= '') then
			if not completed then iscomplete = false end
			objective_rows = objective_block:SetInfo(objective_rows, text, completed, failed);
			fill_height = fill_height + (INNER_HEIGHT + 2);
			if(duration > 0 and elapsed <= duration and not (failed or completed)) then
				objective_rows = objective_block:SetTimer(objective_rows, duration, elapsed);
				fill_height = fill_height + (INNER_HEIGHT + 2);
			end
		end
	end

	if(hasFailed) then
		row.Header.Text:SetTextColor(1,0,0)
	elseif(iscomplete) then
		row.Header.Text:SetTextColor(0.1,0.9,0.1)
	else
		row.Header.Text:SetTextColor(1,1,1)
	end

	if(objective_rows > 0) then
		objective_block:SetHeight(fill_height);
	end

	fill_height = fill_height + (ROW_HEIGHT + 2);

	return index, fill_height;
end

local SetBonusRow = function(self, index, questID, subCount)
	index = index + 1
	local objective_rows = 0;
	local fill_height = 0;
	local iscomplete = true;
	local row = self:Get(index);
	local objective_block = row.Objectives;

	for i = 1, subCount do
		local text, category, objective_completed = GetCachedQuestObjectiveInfo(questID, i);
		if not objective_completed then iscomplete = false end
		if(text and text ~= '') then
			objective_rows = objective_block:SetInfo(objective_rows, text, objective_completed);
			fill_height = fill_height + (INNER_HEIGHT + 2);
		end
		if(category and category == 'progressbar') then
			objective_rows = objective_block:SetProgress(objective_rows, questID, objective_completed);
			fill_height = fill_height + (INNER_HEIGHT + 2);
		end
	end

	if(not iscomplete) then
		row.RowID = questID
		row.Header.Text:SetText(TRACKER_HEADER_BONUS_OBJECTIVES)
		row:SetHeight(ROW_HEIGHT);
		row:FadeIn();

		if(objective_rows > 0) then
			objective_block:SetHeight(fill_height);
		end

		fill_height = fill_height + (ROW_HEIGHT + 2);

		return index, fill_height;
	else
		CACHED_BONUS_DATA[questID] = nil;
		COMPLETED_BONUS_DATA[questID] = true;
		PlaySoundKitID(45142);
		return index, 0;
	end
end

local UpdateBonusObjectives = function(self)
	local fill_height = 0;
	local rows = 0;

	if(C_Scenario.IsInScenario()) then
		local tblBonusSteps = C_Scenario.GetBonusSteps();
		local cachedToRemove = {};
		for i = 1, #tblBonusSteps do
			local bonusStepIndex = tblBonusSteps[i];
			local cachedIndex = GetScenarioBonusStep(bonusStepIndex);
			if(cachedIndex) then
				local name, description, numCriteria, stepFailed, isBonusStep, isForCurrentStepOnly = C_Scenario.GetStepInfo(bonusStepIndex);
				local completed = true;
				for criteriaIndex = 1, numCriteria do
					local criteriaString, criteriaType, criteriaCompleted, quantity, totalQuantity, flags, assetID, quantityString, criteriaID, duration, elapsed, criteriaFailed = C_Scenario.GetCriteriaInfoByStep(bonusStepIndex, criteriaIndex);
					if(criteriaString) then
						if(not criteriaCompleted) then
							completed = false;
							break;
						end
					end
				end
				if(not completed) then
					tinsert(cachedToRemove, cachedIndex);
				end
			end
		end
		for i = 1, #cachedToRemove do
			tDeleteItem(tblBonusSteps, cachedToRemove[i]);
		end

		for i = 1, #tblBonusSteps do
			local bonusStepIndex = tblBonusSteps[i];
			local name, description, numCriteria, stepFailed, isBonusStep, isForCurrentStepOnly = C_Scenario.GetStepInfo(bonusStepIndex);
			local add_height = 0;
			rows, add_height = self:SetCriteria(rows, bonusStepIndex, numCriteria, stepFailed)
			fill_height = fill_height + add_height;
		end
	else
		local cache = GetBonusCache();
		for i = 1, #cache do
			local questID = cache[i];
			local completedData = COMPLETED_BONUS_DATA[questID];
			if(not completedData) then
				local existingTask = CACHED_BONUS_DATA[questID];
				local isInArea, isOnMap, numObjectives = GetCachedTaskInfo(questID);
				if(isInArea or (isOnMap and existingTask)) then
					local add_height = 0;
					rows, add_height = self:SetBonus(rows, questID, numObjectives)
					fill_height = fill_height + add_height;
				end
			end
		end
	end

	if(rows == 0 or (fill_height <= 1)) then
		self:SetHeight(1);
		self:SetAlpha(0);
		self:Reset();
	else
		self:SetHeight(fill_height + 2);
		self:FadeIn();
	end
end

local RefreshBonusObjectives = function(self, event, ...)
	-- print('BONUS-------->')
	-- print(event)
	-- print(...)
	if(event == "CRITERIA_COMPLETE") then
		local id = ...;
		if(id > 0) then
			local tblBonusSteps = C_Scenario.GetBonusSteps();
			for i = 1, #tblBonusSteps do
				local bonusStepIndex = tblBonusSteps[i];
				local _, _, numCriteria = C_Scenario.GetStepInfo(bonusStepIndex);
				for criteriaIndex = 1, numCriteria do
					local _, _, _, _, _, _, _, _, criteriaID = C_Scenario.GetCriteriaInfoByStep(bonusStepIndex, criteriaIndex);
					if(id == criteriaID) then
						local questID = C_Scenario.GetBonusStepRewardQuestID(bonusStepIndex);
						if(questID ~= 0) then
							CacheBonusData(questID);
							return;
						end
					end
				end
			end
		end
	end
	self:UpdateAll();
end

local ResetBonusBlock = function(self)
	for x = 1, #self.Rows do
		local row = self.Rows[x]
		if(row) then
			row.RowID = 0;
			row.Header.Text:SetText('');
			row:SetHeight(1);
			row:SetAlpha(0);
			row.Objectives:Reset();
		end
	end
end
--[[
##########################################################
CORE FUNCTIONS
##########################################################
]]--
function MOD:UpdateBonusObjective(event, ...)
	self.Headers["Bonus"]:Reset()
	self.Headers["Bonus"]:Refresh(event, ...)
	self:UpdateDimensions();
end

function MOD:CacheBonusObjective(event, ...)
	CacheBonusData(...)
end

local function UpdateBonusLocals(...)
	ROW_WIDTH, ROW_HEIGHT, INNER_HEIGHT, LARGE_ROW_HEIGHT, LARGE_INNER_HEIGHT = ...;
end

function MOD:InitializeBonuses()
	local scrollChild = self.Docklet.ScrollFrame.ScrollChild;
	local bonus = CreateFrame("Frame", nil, scrollChild)
	bonus:SetWidth(ROW_WIDTH);
	bonus:SetHeight(1);
	bonus:SetPoint("TOPLEFT", self.Headers["Scenario"], "BOTTOMLEFT", 0, -4);

	bonus.Rows = {};

	bonus.Get = GetBonusRow;
	bonus.SetBonus = SetBonusRow;
	bonus.SetCriteria = SetCriteriaRow;
	bonus.Refresh = RefreshBonusObjectives;
	bonus.Reset = ResetBonusBlock;
	bonus.UpdateAll = UpdateBonusObjectives;

	self.Headers["Bonus"] = bonus

	self:RegisterEvent("CRITERIA_COMPLETE", self.UpdateBonusObjective);
	SV.Events:On("QUEST_UPVALUES_UPDATED", UpdateBonusLocals, true);
end
