local compost = CompostLib:GetInstance('compost-1')
local tablet = TabletLib:GetInstance('1.0')
local dewdrop = DewdropLib:GetInstance('1.0')
local metro = Metrognome:GetInstance('1')

ExperienceFu = FuBarPlugin:new({
	name          = ExperienceFuLocals.NAME,
	description   = ExperienceFuLocals.DESCRIPTION,
	version       = "1.1." .. string.sub("$Revision: 1159 $", 12, -3),
	releaseDate   = string.sub("$Date: 2006-04-29 08:55:48 -0500 (Sat, 29 Apr 2006) $", 8, 17),
	aceCompatible = 103,
	fuCompatible  = 102,
	author        = "Boss",
	email         = "ckknight@gmail.com",
	website       = "http://boss.wowinterface.com/",
	category      = "others",
	db            = AceDatabase:new("ExperienceFuDB"),
	defaults      = {
		showValue = {
			current = false,
			rest = false,
			pet = false,
		},
		showPercent = {
			current = true,
			rest = true,
			pet = true
		},
		showDepletion = {
			current = false,
			rest = false,
			pet = false,
		},
	},
	cmd           = AceChatCmd:new(ExperienceFuLocals.COMMANDS, ExperienceFuLocals.CMD_OPTIONS),
	loc           = ExperienceFuLocals,
	hasIcon       = true,
})

function ExperienceFu:IsShowingValue(setting)
	return self.data.showValue[setting]
end

function ExperienceFu:ToggleShowingValue(setting, loud)
	self.data.showValue[setting] = not self.data.showValue[setting]
	if loud then
		self.cmd:status(self.loc.ARGUMENT_SHOWVALUE, self.data.showValue[setting] and 1 or 0, FuBarUtils.MAP_ONOFF)
	end
	self:UpdateText()
	return self.data.showValue[setting]
end

function ExperienceFu:IsShowingPercent(setting)
	return self.data.showPercent[setting]
end

function ExperienceFu:ToggleShowingPercent(setting, loud)
	self.data.showPercent[setting] = not self.data.showPercent[setting]
	if loud then
		self.cmd:status(self.loc.ARGUMENT_SHOWPERCENT, self.data.showPercent[setting] and 1 or 0, FuBarUtils.MAP_ONOFF)
	end
	self:UpdateText()
	return self.data.showPercent[setting]
end

function ExperienceFu:IsShowingDepletion(setting)
	return self.data.showDepletion[setting]
end

function ExperienceFu:ToggleShowingDepletion(setting, loud)
	self.data.showDepletion[setting] = not self.data.showDepletion[setting]
	if loud then
		self.cmd:status(self.loc.ARGUMENT_SHOWDEPLETION, self.data.showDepletion[setting] and 1 or 0, FuBarUtils.MAP_ONOFF)
	end
	self:UpdateText()
	return self.data.showDepletion[setting]
end

function ExperienceFu:IsShowingXPPerHour()
	return self.data.xpPerHour
end

function ExperienceFu:ToggleShowingXPPerHour(loud)
	self.data.xpPerHour = not self.data.xpPerHour
	if loud then
		self.cmd:status(self.loc.ARGUMENT_XPPERHOUR, self.data.xpPerHour and 1 or 0, FuBarUtils.MAP_ONOFF)
	end
	self:UpdateText()
	return self.data.xpPerHour
end

function ExperienceFu:IsShowingTimeToLevel()
	return self.data.timeToLevel
end

function ExperienceFu:ToggleShowingTimeToLevel(loud)
	self.data.timeToLevel = not self.data.timeToLevel
	if loud then
		self.cmd:status(self.loc.ARGUMENT_TIMETOLEVEL, self.data.timeToLevel and 1 or 0, FuBarUtils.MAP_ONOFF)
	end
	self:UpdateText()
	return self.data.timeToLevel
end

function ExperienceFu:IsShowingByLevel()
	return self.data.byLevel
end

function ExperienceFu:ToggleShowingByLevel(loud)
	self.data.byLevel = not self.data.byLevel
	if loud then
		self.cmd:status(self.loc.ARGUMENT_BYLEVEL, self.data.byLevel and 1 or 0, FuBarUtils.MAP_ONOFF)
	end
	self:UpdateText()
	return self.data.byLevel
end

function ExperienceFu:Initialize()
	self.initialXP = UnitXP("player")
	self.accumulatedXP = 0
	self.sessionXP = 0
	self.entranceTime = time()
	self.levelUpTime = time()
	self.totalTime = time()
	self.levelTime = time()
end

function ExperienceFu:Enable()
	self.timeSinceLastUpdate = 0
	
	self:RegisterEvent("PLAYER_XP_UPDATE", "OnPlayerXpUpdate")
	self:RegisterEvent("PLAYER_LEVEL_UP", "OnPlayerLevelUp")
	self:RegisterEvent("TIME_PLAYED_MSG", "OnTimePlayedMsg")
	
	RequestTimePlayed()
	
	metro:Register(self.name, self.Update, 1, self)
	metro:Start(self.name)
end

function ExperienceFu:Disable()
	metro:Unregister(self.name)
end

function ExperienceFu:ResetSession()
	self.initialXP = UnitXP("player")
	self.accumulatedXP = 0
	self.sessionXP = 0
	self.entranceTime = time()
	RequestTimePlayed()
end

function ExperienceFu:Report()
	local report = compost:Acquire(
		compost:AcquireHash(
			'text', self.loc.ARGUMENT_STYLE,
			'val', self:IsShowingByLevel() and 1 or 0,
			'map', self.loc.MAP_LEVELSESSION
		)
	)
	self.cmd:report(report)
	compost:Reclaim(report, 1)
end

function ExperienceFu:MenuSettings(level, value)
	if level == 1 then
		dewdrop:AddLine(
			'text', self.loc.MENU_RESET_SESSION,
			'arg1', self,
			'func', "ResetSession",
			'closeWhenClicked', true
		)
		
		dewdrop:AddLine(
			'text', self.loc.MENU_SHOW_CURRENT_XP,
			'func', function()
				if self:IsShowingValue("current") or self:IsShowingPercent("current") then
					if self:IsShowingValue("current") then
						self:ToggleShowingValue("current")
					end
					if self:IsShowingPercent("current") then
						self:ToggleShowingPercent("current")
					end
				else
					self:ToggleShowingPercent("current")
				end
			end,
			'checked', self:IsShowingValue("current") or self:IsShowingPercent("current"),
			'hasArrow', self:IsShowingValue("current") or self:IsShowingPercent("current"),
			'value', "current"
		)
		
		dewdrop:AddLine(
			'text', self.loc.MENU_SHOW_REST_XP,
			'func', function()
				if self:IsShowingValue("rest") or self:IsShowingPercent("rest") then
					if self:IsShowingValue("rest") then
						self:ToggleShowingValue("rest")
					end
					if self:IsShowingPercent("rest") then
						self:ToggleShowingPercent("rest")
					end
				else
					self:ToggleShowingPercent("rest")
				end
			end,
			'checked', self:IsShowingValue("rest") or self:IsShowingPercent("rest"),
			'hasArrow', self:IsShowingValue("rest") or self:IsShowingPercent("rest"),
			'value', "rest"
		)
		
		local _,class = UnitClass("player")
		if class == "HUNTER" then
			dewdrop:AddLine(
				'text', self.loc.MENU_SHOW_PET_XP,
				'func', function()
					if self:IsShowingValue("pet") or self:IsShowingPercent("pet") then
						if self:IsShowingValue("pet") then
							self:ToggleShowingValue("pet")
						end
						if self:IsShowingPercent("pet") then
							self:ToggleShowingPercent("pet")
						end
					else
						self:ToggleShowingPercent("pet")
					end
				end,
				'checked', self:IsShowingValue("pet") or self:IsShowingPercent("pet"),
				'hasArrow', self:IsShowingValue("pet") or self:IsShowingPercent("pet"),
				'value', "pet"
			)
		end
		
		dewdrop:AddLine(
			'text', self.loc.MENU_SHOW_XP_PER_HOUR,
			'arg1', self,
			'func', "ToggleShowingXPPerHour",
			'checked', self:IsShowingXPPerHour(),
			'hasArrow', self:IsShowingXPPerHour(),
			'value', "xpPerHour"
		)
		
		dewdrop:AddLine(
			'text', self.loc.MENU_SHOW_TIME_TO_LEVEL,
			'arg1', self,
			'func', "ToggleShowingTimeToLevel",
			'checked', self:IsShowingTimeToLevel(),
			'hasArrow', self:IsShowingTimeToLevel(),
			'value', "timeToLevel"
		)
	elseif level == 2 then
		if value == "current" or value == "rest" or value == "pet" then
			dewdrop:AddLine(
				'text', self.loc.MENU_SHOW_VALUE,
				'arg1', value,
				'func', function(value)
					self:ToggleShowingValue(value)
					if not self:IsShowingValue(value) and not self:IsShowingPercent(value) then
						dewdrop:Refresh(1)
						dewdrop:Close(2)
					end
				end,
				'checked', self:IsShowingValue(value)
			)
			
			dewdrop:AddLine(
				'text', self.loc.MENU_SHOW_PERCENT,
				'arg1', value,
				'func', function(value)
					self:ToggleShowingPercent(value)
					if not self:IsShowingValue(value) and not self:IsShowingPercent(value) then
						dewdrop:Refresh(1)
						dewdrop:Close(2)
					end
				end,
				'checked', self:IsShowingPercent(value)
			)
		end
		if value == "current" or value == "pet" then
			dewdrop:AddLine(
				'text', self.loc.MENU_SHOW_DEPLETION,
				'arg1', self,
				'func', "ToggleShowingDepletion",
				'arg2', value,
				'checked', self:IsShowingDepletion(value)
			)
		end
		if value == "xpPerHour" or value == "timeToLevel" then
			dewdrop:AddLine(
				'text', self.loc.MENU_SHOW_IN_RELATION_TO_SESSION,
				'arg1', self,
				'func', self:IsShowingByLevel() and "ToggleShowingByLevel",
				'checked', not self:IsShowingByLevel(),
				'isRadio', true
			)
			
			dewdrop:AddLine(
				'text', self.loc.MENU_SHOW_IN_RELATION_TO_LEVEL,
				'arg1', self,
				'func', not self:IsShowingByLevel() and "ToggleShowingByLevel",
				'checked', self:IsShowingByLevel(),
				'isRadio', true
			)
		end
	end
end

function ExperienceFu:GetLevelDuration()
	return time() - self.levelUpTime
end

function ExperienceFu:GetTotalDuration()
	return time() - self.totalTime
end

function ExperienceFu:GetSessionDuration()
	return time() - self.entranceTime
end

function ExperienceFu:OnPlayerXpUpdate()
	self.sessionXP = UnitXP("player") - self.initialXP + self.accumulatedXP
end

function ExperienceFu:OnPlayerLevelUp()
	self.accumulatedXP = self.accumulatedXP + UnitXPMax("player") - self.initialXP
	self.initialXP = 0
	self.levelUpTime = time()
end

function ExperienceFu:OnTimePlayedMsg()
	self.totalTime = time() - arg1
	self.levelUpTime = time() - arg2
end

function ExperienceFu:UpdateText()
	if self.entranceTime == nil then
		return
	end
	local t = compost:Acquire()
	if self:IsShowingValue("current") then
		local val = UnitXP("player")
		local max = UnitXPMax("player")
		if self:IsShowingDepletion("current") then
			val = max - val
		end
		table.insert(t, format("|cffffffff%d|r XP", val))
		if self:IsShowingPercent("current") then
			table.insert(t, format("(|cffffffff%.1f%%|r)", val/max * 100))
		end
	elseif self:IsShowingPercent("current") then
		local val = UnitXP("player")
		local max = UnitXPMax("player")
		if self:IsShowingDepletion("current") then
			val = max - val
		end
		table.insert(t, format("|cffffffff%.1f%%|r", val/max * 100))
	end
	if self:IsShowingValue("rest") then
		local val = GetXPExhaustion() or 0
		if val ~= 0 then
			local max = UnitXPMax("player")
			table.insert(t, format("R: |cffffffff%d|r XP", val))
			if self:IsShowingPercent("rest") then
				table.insert(t, format("(|cffffffff%.1f%%|r)", val/max * 100))
			end
		end
	elseif self:IsShowingPercent("rest") then
		local val = GetXPExhaustion() or 0
		if val ~= 0 then
			local max = UnitXPMax("player")
			table.insert(t, format("R: |cffffffff%.1f%%|r", val/max * 100))
		end
	end
	local _,class = UnitClass("player")
	if class == "HUNTER" and UnitExists("pet") then
		if self:IsShowingValue("pet") then
			local val, max = GetPetExperience()
			if self:IsShowingDepletion("pet") then
				val = max - val
			end
			table.insert(t, format("P: |cffffffff%d|r XP", val))
			if self:IsShowingPercent("pet") then
				table.insert(t, format("(|cffffffff%.1f%%|r)", val/max * 100))
			end
		elseif self:IsShowingPercent("pet") then
			local val, max = GetPetExperience()
			if self:IsShowingDepletion("pet") then
				val = max - val
			end
			table.insert(t, format("P: |cffffffff%.1f%%|r", val/max * 100))
		end
	end
	if self:IsShowingXPPerHour() then
		if self:IsShowingByLevel() then
			local xpPerHourByLevel = floor(UnitXP("player") / self:GetLevelDuration() * 3600)
			table.insert(t, format("|cffffffff%d|r XP/h", xpPerHourByLevel))
		else
			local xpPerHourBySession = floor(self.sessionXP / (time() - self.entranceTime) * 3600)
			table.insert(t, format("|cffffffff%d|r XP/h", xpPerHourBySession))
		end
	end
	if self:IsShowingTimeToLevel() then
		if self:IsShowingByLevel() then
			local timeToLevelByLevel = (UnitXPMax("player") - UnitXP("player")) * self:GetLevelDuration() / UnitXP("player")
			table.insert(t, FuBarUtils.FormatDurationShort(timeToLevelByLevel, TRUE))
		else
			local timeToLevelBySession = (UnitXPMax("player") - UnitXP("player")) * (time() - self.entranceTime) / self.sessionXP
			table.insert(t, FuBarUtils.FormatDurationShort(timeToLevelBySession, TRUE))
		end
	end
	self:SetText(table.concat(t, " "))
	compost:Reclaim(t)
end

function ExperienceFu:UpdateTooltip()
	local totalXP = UnitXPMax("player")
	local currentXP = UnitXP("player")
	local toLevelXP = totalXP - currentXP
	local sessionXP = self.sessionXP
	local sessionTime = time() - self.entranceTime
	local levelTime = self:GetLevelDuration()
	local xpPerHourByLevel = floor(currentXP / levelTime * 3600)
	local timeToLevelByLevel = toLevelXP * levelTime / currentXP
	local xpPerHourBySession = floor(sessionXP / sessionTime * 3600)
	local timeToLevelBySession = toLevelXP * sessionTime / sessionXP
	local restXP = GetXPExhaustion() or 0
	
	local cat = tablet:AddCategory(
		'columns', 2,
		'child_textR', 1,
		'child_textG', 1,
		'child_textB', 0,
		'child_text2R', 1,
		'child_text2G', 1,
		'child_text2B', 1
	)
	
	cat:AddLine(
		'text', self.loc.TEXT_TOTAL_TIME_PLAYED .. ":",
		'text2', FuBarUtils.FormatDurationFull(self:GetTotalDuration())
	)
	
	cat:AddLine(
		'text', self.loc.TEXT_TIME_THIS_LEVEL .. ":",
		'text2', FuBarUtils.FormatDurationFull(self:GetLevelDuration())
	)
	
	cat:AddLine(
		'text', self.loc.TEXT_TIME_THIS_SESSION .. ":",
		'text2', FuBarUtils.FormatDurationFull(self:GetSessionDuration())
	)
	
	cat = tablet:AddCategory(
		'columns', 2,
		'child_textR', 1,
		'child_textG', 1,
		'child_textB', 0,
		'child_text2R', 1,
		'child_text2G', 1,
		'child_text2B', 1
	)
	
	cat:AddLine(
		'text', self.loc.TEXT_LEVEL .. ":",
		'text2', UnitLevel("player")
	)
	
	cat:AddLine(
		'text', self.loc.TEXT_TOTAL_XP_THIS_LEVEL .. ":",
		'text2', totalXP
	)
	
	cat:AddLine(
		'text', self.loc.TEXT_GAINED .. ":",
		'text2', format("%d (%.1f%%)", currentXP, currentXP / totalXP * 100)
	)
	
	cat:AddLine(
		'text', self.loc.TEXT_REMAINING .. ":",
		'text2', format("%d (%.1f%%)", toLevelXP, toLevelXP / totalXP * 100)
	)
	
	cat:AddLine(
		'text', self.loc.TEXT_TOTAL_XP_THIS_SESSION .. ":",
		'text2', sessionXP
	)
	
	cat:AddLine(
		'text', self.loc.TEXT_REST_XP .. ":",
		'text2', format("%d (%.1f%%)", restXP, restXP / totalXP * 100)
	)
	
	local _,class = UnitClass("player")
	if class == "HUNTER" and UnitExists("pet") then
		local currXP, nextXP = GetPetExperience()
		if currXP and nextXP and nextXP > 0 then
			cat:AddLine(
				'text', self.loc.TEXT_PET_XP .. ":",
				'text2', format("%d (%.1f%%)", currXP, currXP / nextXP * 100)
			)
		end
	end
	
	local cat = tablet:AddCategory(
		'columns', 2,
		'child_textR', 1,
		'child_textG', 1,
		'child_textB', 0,
		'child_text2R', 1,
		'child_text2G', 1,
		'child_text2B', 1
	)
	
	cat:AddLine(
		'text', self.loc.TEXT_XP_PER_HOUR_THIS_LEVEL .. ":",
		'text2', xpPerHourByLevel
	)
	
	cat:AddLine(
		'text', self.loc.TEXT_XP_PER_HOUR_THIS_SESSION .. ":",
		'text2', xpPerHourBySession
	)
	
	cat:AddLine(
		'text', self.loc.TEXT_TIME_TO_LEVEL_FOR_THIS_LEVEL .. ":",
		'text2', FuBarUtils.FormatDurationFull(timeToLevelByLevel)
	)
	
	cat:AddLine(
		'text', self.loc.TEXT_TIME_TO_LEVEL_FOR_THIS_SESSION .. ":",
		'text2', FuBarUtils.FormatDurationFull(timeToLevelBySession)
	)
end

ExperienceFu:RegisterForLoad()