RangeHelper = AceLibrary("AceAddon-2.0"):new("AceConsole-2.0", "AceDB-2.0", "AceEvent-2.0")

local L = AceLibrary("AceLocale-2.2"):new("RangeHelper")
local BS = AceLibrary("Babble-Spell-2.2")

RangeHelper:RegisterDB("RangeHelperDB")
RangeHelper:RegisterDefaults('profile', {
	actionbar = false,
	spellbook = true,
	format = "1",
	spellname = false,
	lock = false,
	interact = "2",
	rangelimit = 100,
	exception = {},
	custom = {},
	font = {
		name = "Fonts\\K_Damage.TTF",
		size = 20,
		flags = {
			OUTLINE = false,
			THICKOUTLINE = true,
			MONOCHROME = false,
		},
	},
	point = {
		point = "CENTER",		-- "TOPLEFT",
		relativeto = "UIParent",	-- "TargetFrame",
		relativepoint = "CENTER",	-- "TOPLEFT",
		x = 0,				-- 28,
		y = 0,				-- 2,
		justifyH = "LEFT",
	},
	color = {
		["Within assist"] = { 0.0, 1.0, 0.0 },
		["Out of assist"] = { 1.0, 0.5, 0.0 },
		["Within attack"] = { 1.0, 1.0, 0.0 },
		["Out of attack"] = { 1.0, 0.0, 0.0 },
		["Within interact"] = { 1.0, 1.0, 1.0 },
		["Out of interact"] = { 0.5, 0.5, 0.5 },
	},
})

RangeHelper.interactrangeset = {
--	[1] = {		-- Inspect, 30 yards (prior to Patch 2.1 this was 11.11 yards)
--		name = INSPECT,
--		max = 30,							-- CHECK
--	},
	[2] = {		-- Trade, 11.11 yards
		name = TRADE,
		max = 8.1,							-- CHECK
		tauren = 6.1,
		
	},
	[3] = {		-- Duel, 10 yards
		name = DUEL,
		max = 7,							-- CHECK
		tauren = 5,
	},
	[4] = {		-- Follow, ~28 yards
		name = FOLLOW,
		max = 28,							-- CHECK
		tauren = 25.1,							-- CHECK, >25
	},
}

RangeHelper.targetispet = {
	-- Set of spells that "target" is "pet"
	-- HUNTER
	[BS["Eyes of the Beast"]] = 1,
	[BS["Dismiss Pet"]] = 1,
	[BS["Feed Pet"]] = 1,
	[BS["Mend Pet"]] = 1,
	[BS["Kill Command"]] = 1,
	[BS["Intimidation"]] = 1,
	[BS["Bestial Wrath"]] = 1,

	-- WARLOCK
	[BS["Health Funnel"]] = 1,
	[BS["Dark Pact"]] = 1,
	[BS["Demonic Sacrifice"]] = 1,
	[BS["Soul Link"]] = 1,
}

function RangeHelper:Clear()
	self.actionrangeset = {}
	self.spellrangeset = {}
	self.petrangeset = {}
	self.candidateset = {}
	self.rangeset = {}
end

function RangeHelper:OnInitialize()
	self.rangelimit = 100
	self.melee = 2
	self.timeleft = 0
	self.timeinterval = 0.25
	self.moving = false
	self.exception = {}
	self.custom = {}
	
	self:Clear()
	self:GetExtraOptions()							-- from Options.lua
	
	CreateFrame("Frame", "RangeHelperFrame", UIParent)
	CreateFrame("GameTooltip", "RangeHelperTooltip", UIParent, "GameTooltipTemplate")
	
	RangeHelperFrame:CreateFontString("RangeHelperText", "ARTWORK")
	RangeHelperFrame:SetWidth(91)
	RangeHelperFrame:SetHeight(35)
end

function RangeHelper:OnEnable()
	self:RegisterEvent("PLAYER_LOGIN")
	self:RegisterEvent("CHARACTER_POINTS_CHANGED")
	self:RegisterEvent("UNIT_INVENTORY_CHANGED")
	
	if self.db.profile.actionbar then
		self:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
	elseif self.db.profile.spellbook then
		self:RegisterEvent("LEARNED_SPELL_IN_TAB")
	end
	
	self:SetPoint()
	self:SetFont()
	self:SetFrameLock()
	self:SetException(false)
	self:SetCustom(false)
	
	RangeHelperFrame:SetScript("OnUpdate", function() self:OnUpdate(arg1) end)
	RangeHelperFrame:SetScript("OnMouseDown", function() self:OnMouseDown() end)
	RangeHelperFrame:SetScript("OnMouseUp", function() self:OnMouseUp() end)
	RangeHelperFrame:SetScript("OnHide", function() self:OnMouseUp() end)
end

function RangeHelper:OnDisable()
	self:UnregisterAllEvents()
	
	RangeHelperFrame:SetScript("OnUpdate", nil)
	RangeHelperFrame:SetScript("OnMouseDown", nil)
	RangeHelperFrame:SetScript("OnMouseUp", nil)
	RangeHelperFrame:SetScript("OnHide", nil)
	RangeHelperFrame:SetScript("OnEnter", nil)
	RangeHelperFrame:SetScript("OnLeave", nil)
	RangeHelperText:SetText("")
end

function RangeHelper:OnProfileEnable()
	if self.db.profile.actionbar then
		self:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
	elseif self.db.profile.spellbook then
		self:RegisterEvent("LEARNED_SPELL_IN_TAB")
	end
	
	self:SetPoint()
	self:SetFont()
	self:SetFrameLock()
	self:SetException(false)
	self:SetCustom(true)
end

function RangeHelper:OnProfileDisable()
	if self:IsEventRegistered("ACTIONBAR_SLOT_CHANGED") then
		self:UnregisterEvent("ACTIONBAR_SLOT_CHANGED")
	end
	if self:IsEventRegistered("LEARNED_SPELL_IN_TAB") then
		self:UnregisterEvent("LEARNED_SPELL_IN_TAB")
	end
end

function RangeHelper:PLAYER_LOGIN()
	-- FIXME: It needs about two seconds to update range by talent-point
	self:ScheduleEvent(self.ResetRangeSet, 2, self)
end

function RangeHelper:CHARACTER_POINTS_CHANGED()
	self:ResetRangeSet()
end

function RangeHelper:UNIT_INVENTORY_CHANGED()
	if arg1 == "player" then
		if self.db.profile.actionbar then
			self:UpdateActionRangeSet()
		elseif self.db.profile.spellbook then
			self:UpdateSpellRangeSet()
		end
	end
end

function RangeHelper:ACTIONBAR_SLOT_CHANGED()
	if self.db.profile.actionbar then
		self:UpdateActionRange(arg1)
	end
end

function RangeHelper:LEARNED_SPELL_IN_TAB()
	if self.db.profile.spellbook then
		self:ResetRangeSet()
	end
end

function RangeHelper:OnUpdate(elapsed)
	self.timeleft = self.timeleft - elapsed
	if self.timeleft > 0 then return end
	self.timeleft = self.timeinterval
	
	local t, c = self:GetRangeText()
	if t then
		RangeHelperText:SetText(t)
	else
		RangeHelperText:SetText("")
	end
	if c then
		RangeHelperText:SetTextColor(unpack(c))
	end
end

function RangeHelper:OnEnter()
	GameTooltip:SetOwner(this, "ANCHOR_TOPLEFT")
	GameTooltip:SetText(L["RangeHelper"])
	GameTooltip:AddLine(L["Hint"], 1, 1, 1)
	GameTooltip:Show()
end

function RangeHelper:OnMouseDown()
	if arg1 ~= "LeftButton" then return end
	
	if IsControlKeyDown() then
		self:SetFrameLock(not self.db.profile.lock)
		if self.db.profile.lock then
			self:Print("Frame is locked. (if you want to unlock, type \'/rh2 lock\')")
		else
			self:Print("Frame is unlocked.")
		end
	else
		if not self.db.profile.lock then
			RangeHelperFrame:StartMoving()
			self.moving = true
		end
	end
end

function RangeHelper:OnMouseUp()
	if not self.moving then return end
	
	RangeHelperFrame:StopMovingOrSizing()
	self.moving = false
	
	local point, relativeto, relativepoint, x, y = RangeHelperFrame:GetPoint()
	local justifyH = self.db.profile.point.justifyH
	
	self.db.profile.point = {
		point = point,
		relativeto = relativeto or "UIParent",
		relativepoint = relativepoint,
		x = x,
		y = y,
		justifyH = justifyH,
	}
end

function RangeHelper:SetFont()
	local f = self.db.profile.font
	local flags = {}
	
	if f.flags.OUTLINE then tinsert(flags, "OUTLINE") end
	if f.flags.THICKOUTLINE then tinsert(flags, "THICKOUTLINE") end
	if f.flags.MONOCHROME then tinsert(flags, "MONOCHROME") end
	
	RangeHelperText:SetFont(f.name, f.size, table.concat(flags, ", ") or nil)
end

function RangeHelper:SetPoint()
	local p = self.db.profile.point
	local relativeto = p.relativeto or "UIParent"
	
	if not getglobal(relativeto) then
		self:Print("ERROR! Cannot find frame \""..relativeto.."\". It'll be temporaryly set to \"UIParent\".")
		relativeto = "UIParent"
	end
	
	RangeHelperFrame:ClearAllPoints()
	RangeHelperText:ClearAllPoints()
	RangeHelperFrame:SetPoint(p.point, relativeto, p.relativepoint, p.x, p.y)
	RangeHelperText:SetPoint(p.justifyH, "RangeHelperFrame", p.justifyH, 0, 0)
end

function RangeHelper:GetSpellType()
	if self.db.profile.actionbar then
		return L["Action Bars"]
	elseif self.db.profile.spellbook then
		return L["Spellbook"]
	else
		return L["Interact Only"]
	end
end

function RangeHelper:SetSpellType(v)
	if v == L["Action Bars"] then
		self.db.profile.actionbar = true
		self.db.profile.spellbook = false
	elseif v == L["Spellbook"] then
		self.db.profile.actionbar = false
		self.db.profile.spellbook = true
	else
		self.db.profile.actionbar = false
		self.db.profile.spellbook = false
	end
	
	self:ResetRangeSet()
	
	if self.db.profile.actionbar then
		self:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
	elseif self:IsEventRegistered("ACTIONBAR_SLOT_CHANGED") then
		self:UnregisterEvent("ACTIONBAR_SLOT_CHANGED")
	end
	if self.db.profile.spellbook then
		self:RegisterEvent("LEARNED_SPELL_IN_TAB")
	elseif self:IsEventRegistered("LEARNED_SPELL_IN_TAB") then
		self:UnregisterEvent("LEARNED_SPELL_IN_TAB")
	end
end

function RangeHelper:SetFrameLock(lock)
	if lock ~= nil then
		self.db.profile.lock = lock
	end
	if self.db.profile.lock then
		RangeHelperFrame:EnableMouse(false)
		RangeHelperFrame:SetBackdropBorderColor(1, 1, 1, 0)
		RangeHelperFrame:SetMovable(false)
		GameTooltip:Hide()
		RangeHelperFrame:SetScript("OnEnter", nil)
		RangeHelperFrame:SetScript("OnLeave", nil)
	else
		RangeHelperFrame:EnableMouse(true)
		RangeHelperFrame:SetBackdropBorderColor(1, 1, 1, 0.5)
		RangeHelperFrame:SetMovable(true)
		RangeHelperFrame:SetScript("OnEnter", function() self:OnEnter() end)
		RangeHelperFrame:SetScript("OnLeave", function() GameTooltip:Hide() end)
	end
end

function RangeHelper:AddException(name)
	for k, v in pairs(self.db.profile.exception) do
		if v == name then
			return
		end
	end
	table.insert(self.db.profile.exception, name)
	self:SetException(true)
end

function RangeHelper:RemoveException(name)
	for k, v in pairs(self.db.profile.exception) do
		if v == name then
			table.remove(self.db.profile.exception, k)
			self:SetException(true)
			return
		end
	end
end

function RangeHelper:ClearException()
	if #self.db.profile.exception == 0 then
		return
	end
	
	self.db.profile.exception = {}
	self:SetException(true)
end

function RangeHelper:SetException(reset)
	self.exception = {}
	for k, v in pairs(self.db.profile.exception) do
		self.exception[v] = 1
	end
	if reset then self:ResetRangeSet() end
end

function RangeHelper:AddCustom(text)
	for k, v in pairs(self.db.profile.custom) do
		if v == text then
			return
		end
	end
	string.gsub(text, "(.+)%s*,%s*(%d+)", function(name, range)
		if name and range then
			table.insert(self.db.profile.custom, text)
			self:SetCustom(true)
			return
		end
	end)
end

function RangeHelper:RemoveCustom(text)
	for k, v in pairs(self.db.profile.custom) do
		if v == text then
			table.remove(self.db.profile.custom, k)
			self:SetCustom(true)
			return
		end
	end
end

function RangeHelper:ClearCustom()
	if #self.db.profile.custom == 0 then
		return
	end
	
	self.db.profile.custom = {}
	self:SetCustom(true)
end

function RangeHelper:SetCustom(reset)
	self.custom = {}
	for k, v in pairs(self.db.profile.custom) do
		string.gsub(v, "(.+)%s*,%s*(%d+)", function(name, range)
			if name and range then
				self.custom[name] = range
			end
		end)
	end
	if reset then self:ResetRangeSet() end
end

function RangeHelper:ListRangeSet(detail)
	self:Print("START - ActionRangeSet")
	n = 0
	for k,v in pairs(self.actionrangeset) do
		n = n + 1
		if detail then
			if v.min then
				self:Print(v.min.."-"..v.max.."m - ["..k.."] "..v.name)
			else
				self:Print(v.max.."m - ["..k.."] "..v.name)
			end
		end
	end
	self:Print("ALL - "..n)
	self:Print("END")
	
	self:Print("START - SpellRangeSet")
	n = 0
	for k,v in pairs(self.spellrangeset) do
		n = n + 1
		if detail then
			if v.min then
				self:Print(v.min.."-"..v.max.."m - ["..k.."] "..v.name)
			else
				self:Print(v.max.."m - ["..k.."] "..v.name)
			end
		end
	end
	self:Print("ALL - "..n)
	self:Print("END")
	
	self:Print("START - PetRangeSet")
	for k,v in pairs(self.petrangeset) do
		self:Print(v.max.."m - ["..k.."] "..v.name)
	end
	self:Print("END")
	
	self:Print("START - Exception")
	for k,v in pairs(self.exception) do
		self:Print(v)
	end
	self:Print("END")
	
	self:Print("START - CandidateSet")
	for ctype,_ in pairs(self.candidateset) do
		n = 0
		for k,v in pairs(self.candidateset[ctype]) do
			n = n + 1
			if detail then
				if v.min then
					self:Print(ctype..","..v.min.."-"..v.max.."m - ["..k.."] "..v.name)
				else
					self:Print(ctype..","..v.max.."m - ["..k.."] "..v.name)
				end
			end
		end
		self:Print(ctype.." - "..n)
	end
	self:Print("END")
	
	self:Print("START - RangeSet")
	for ctype,_ in pairs(self.rangeset) do
		for ttype,_ in pairs(self.rangeset[ctype]) do
			for k,v in pairs(self.rangeset[ctype][ttype]) do
				if v.min then
					self:Print(ctype..","..ttype..","..v.min.."-"..k.."m - ["..v.slot.."] "..v.name)
				else
					self:Print(ctype..","..ttype..","..k.."m - ["..v.slot.."] "..v.name)
				end
			end
		end
	end
	self:Print("END")
end

function RangeHelper:ResetRangeSet()
	self:Clear()
	
	if self.db.profile.actionbar then
		self:GetActionRangeSet()
	elseif self.db.profile.spellbook then
		self:GetSpellRangeSet()
	end
end

function RangeHelper:IsActionInRange(slot)
	if self.db.profile.actionbar then
		return IsActionInRange(slot)
	elseif self.db.profile.spellbook then
		return IsSpellInRange(slot, BOOKTYPE_SPELL)
	else
		return nil
	end
end

function RangeHelper:GetActionRange(slot)
	if not slot then return nil, nil, nil end
	
	-- 1.10 bugfix: Can't get tooltip infos if didn't set owner.
	RangeHelperTooltip:SetOwner(UIParent, "ANCHOR_NONE")
	RangeHelperTooltipTextLeft1:SetText("")
	RangeHelperTooltipTextLeft2:SetText("")
	RangeHelperTooltipTextRight2:SetText("")
	RangeHelperTooltip:ClearLines()
	RangeHelperTooltip:SetAction(slot)
	
	local name = RangeHelperTooltipTextLeft1:GetText()
	if self.exception[name] then return name, nil, nil end
	if self.custom[name] then return name, nil, self.custom[name] end
	
	local text2 = {
		left = RangeHelperTooltipTextLeft2:GetText(),
		right = RangeHelperTooltipTextRight2:GetText(),
	}
	local _,race = UnitRace("player")

	for k,v in pairs(text2) do
		if string.find(v, MELEE_RANGE) then
			return name, nil, self.melee
		end
		local _,_,range = string.find(v, L["(%d+) yd range"])
		if range then
			if tonumber(range) <= self.db.profile.rangelimit then
				return name, nil, tonumber(range)
			end
		end
		local _,_,min,max = string.find(v, L["(%d+)-(%d+) yd range"])
		if min and max then
			if tonumber(max) <= self.db.profile.rangelimit then
				return name, tonumber(min), tonumber(max)
			end
		end
	end
	
	return nil, nil, nil
end

function RangeHelper:GetSpellRange(spellId, spellTab)
	if not spellId or not spellTab then return nil, nil, nil end
	
	-- 1.10 bugfix: Can't get tooltip infos if didn't set owner.
	RangeHelperTooltip:SetOwner(UIParent, "ANCHOR_NONE")
	RangeHelperTooltipTextLeft1:SetText("")
	RangeHelperTooltipTextLeft2:SetText("")
	RangeHelperTooltipTextRight2:SetText("")
	RangeHelperTooltip:ClearLines()
	RangeHelperTooltip:SetSpell(spellId, spellTab)
	
	local name = RangeHelperTooltipTextLeft1:GetText()
	if self.exception[name] then return name, nil, nil end
	if self.custom[name] then return name, nil, self.custom[name] end
	
	local text2 = {
		left = RangeHelperTooltipTextLeft2:GetText(),
		right = RangeHelperTooltipTextRight2:GetText(),
	}
	local _,race = UnitRace("player")

	for k,v in pairs(text2) do
		if string.find(v, MELEE_RANGE) then
			return name, nil, self.melee
		end
		local _,_,range = string.find(v, L["(%d+) yd range"])
		if range then
			if tonumber(range) <= self.db.profile.rangelimit then
				return name, nil, tonumber(range)
			end
		end
		local _,_,min,max = string.find(v, L["(%d+)-(%d+) yd range"])
		if min and max then
			if tonumber(max) <= self.db.profile.rangelimit then
				return name, tonumber(min), tonumber(max)
			end
		end
	end
	
	return name, nil, nil
end

function RangeHelper:GetActionRangeSet()
	self.actionrangeset = {}
	
	for i = 1,120,1 do
		local name, min, max = self:GetActionRange(i)
		if name and max then
			if self.targetispet[name] then
				self.petrangeset[i] = { name = name, min = min, max = max }
			else
				self.actionrangeset[i] = { name = name, min = min, max = max }
			end
		end
	end
end

function RangeHelper:GetSpellRangeSet()
	self.spellrangeset = {}
	
	for tab = 1, GetNumSpellTabs() do
		local _, _, offset, numSpells = GetSpellTabInfo(tab)
		local last = {}
		
		for id = 1+offset, numSpells+offset do
			local name, min, max = self:GetSpellRange(id, tab)
			if name and max
				-- remove duplicates
				and (name ~= last.name or max ~= last.max) then
				
				if self.targetispet[name] then
					self.petrangeset[id] = { name = name, min = min, max = max, tab = tab }
				else
					self.spellrangeset[id] = { name = name, min = min, max = max, tab = tab }
				end
			end
			last.name = name
			last.min = min
			last.max = max
		end
	end
end

function RangeHelper:UpdateActionRange(slot)
	if not slot then return end
	if not self.actionrangeset then
		self:GetActionRangeSet()
		return
	end
	
	local reset = false
	local name, min, max = self:GetActionRange(slot)
	
	if name and max then
		local v = self.actionrangeset[slot]
		if v then
			if name ~= v.name or max ~= v.max then
				if not self.targetispet[name] then
					self.actionrangeset[slot] = { name = name, min = min, max = max }
				else
					self.actionrangeset[slot] = nil
					self.petrangeset[slot] = { name = name, min = min, max = max }
				end
				reset = true
			end
		end
		v = self.petrangeset[slot]
		if v then
			if name ~= v.name or max ~= v.max then
				if self.targetispet[name] then
					self.petrangeset[slot] = { name = name, min = min, max = max }
				else
					self.petrangeset[slot] = nil
					self.actionrangeset[slot] = { name = name, min = min, max = max }
				end
				reset = true
			end
		end
	else
		if self.actionrangeset[slot] then
			self.actionrangeset[slot] = nil
			reset = true
		end
		if self.petrangeset[slot] then
			self.petrangeset[slot] = nil
			reset = true
		end
	end
	
	if reset then
		self.candidateset = {}
		self.rangeset = {}
	end
end

function RangeHelper:UpdateActionRangeSet()
	if not self.actionrangeset then
		self:GetActionRangeSet()
		return
	end
	
	local reset = false
	
	for k,v in pairs(self.actionrangeset) do
		local name, min, max = self:GetActionRange(k)
		if name and max then
			if name ~= v.name or max ~= v.max then
				if not self.targetispet[name] then
					self.actionrangeset[k] = { name = name, min = min, max = max }
				else
					self.actionrangeset[k] = nil
				end
				reset = true
			end
		else
			if self.actionrangeset[k] then
				self.actionrangeset[k] = nil
				reset = true
			end
		end
	end
	for k,v in pairs(self.petrangeset) do
		local name, min, max = self:GetActionRange(k)
		if name and max then
			if name ~= v.name or max ~= v.max then
				if self.targetispet[name] then
					self.petrangeset[k] = { name = name, min = min, max = max }
				else
					self.petrangeset[k] = nil
				end
				reset = true
			end
		else
			if self.petrangeset[k] then
				self.petrangeset[k] = nil
				reset = true
			end
		end
	end
	
	if reset then
		self.candidateset = {}
		self.rangeset = {}
	end
end

function RangeHelper:UpdateSpellRangeSet()
	if not self.spellrangeset then
		self:GetSpellRangeSet()
		return
	end
	
	local reset = false
	
	for k,v in pairs(self.spellrangeset) do
		local name, min, max = self:GetSpellRange(k, v.tab)
		if name and max then
			if name ~= v.name or max ~= v.max then
				if not self.targetispet[name] then
					self.spellrangeset[k] = { name = name, min = min, max = max, tab = v.tab }
				else
					self.spellrangeset[k] = nil
				end
				reset = true
			end
		else
			if self.spellrangeset[k] then
				self.spellrangeset[k] = nil
				reset = true
			end
		end
	end
	for k,v in pairs(self.petrangeset) do
		local name, min, max = self:GetSpellRange(k, v.tab)
		if name and max then
			if name ~= v.name or max ~= v.max then
				if self.targetispet[name] then
					self.petrangeset[k] = { name = name, min = min, max = max, tab = v.tab }
				else
					self.petrangeset[k] = nil
				end
				reset = true
			end
		else
			if self.petrangeset[k] then
				self.petrangeset[k] = nil
				reset = true
			end
		end
	end
	
	if reset then
		self.candidateset = {}
		self.rangeset = {}
	end
end

function RangeHelper:GetRangeSet(ctype, ttype)
	if not ctype or not ttype then return end
	
	if not self.candidateset[ctype] then
		self.candidateset[ctype] = {}
		
		if self.db.profile.actionbar then
			for k,v in pairs(self.actionrangeset) do
				self.candidateset[ctype][k] = { name = v.name, min = v.min, max = v.max }
			end
		elseif self.db.profile.spellbook then
			for k,v in pairs(self.spellrangeset) do
				self.candidateset[ctype][k] = { name = v.name, min = v.min, max = v.max }
			end
		end
	end
	
	if not self.rangeset[ctype] then self.rangeset[ctype] = {} end
	if not self.rangeset[ctype][ttype] then self.rangeset[ctype][ttype] = {} end
	
	for k,v in pairs(self.candidateset[ctype]) do
		if self:IsActionInRange(k) then
			local w = self.rangeset[ctype][ttype][v.max]
			if not w or (not w.min and v.min) then
				self.rangeset[ctype][ttype][v.max] = { name = v.name, min = v.min, slot = k }
			end
			
			self.candidateset[ctype][k] = nil
		end
	end
end

function RangeHelper:GetTargetType()
	local ctype, utype, ttype
	
	if UnitIsPlayer("target") then
		ctype = "player"
	else
		ctype = UnitCreatureType("target") or "unkown"
	end
	
	if UnitCanAssist("player", "target") then
		utype = "assist"
	elseif UnitCanAttack("player", "target") then
		utype = "attack"
	else
		utype = "interact"
	end
	
	if UnitIsDead("target") then
		ttype = utype.."dead"
	else
		ttype = utype
	end
	
	return ctype, utype, ttype
end

function RangeHelper:GetRangeText()
	if not UnitExists("target") then return nil, nil end
	
	local color = self.db.profile.color["Within assist"]
	if UnitIsUnit("target", "player") then
		if self.db.profile.format == "1" then
			return L["Within %dyd"]:format(0), color
		elseif self.db.profile.format == "2" then
			return L["%dyd"]:format(0), color
		else
			return L["%d"]:format(0), color
		end
	end
	
	local ctype, utype, ttype = self:GetTargetType()
	local name = nil
	local rmin = 0
	local rmax = self.db.profile.rangelimit+1
	local imax = (CheckInteractDistance("target", 4) == 1 and 28) or self.db.profile.rangelimit+1 -- for 8~30/8~35
	local limit = 0
	
	if self.db.profile.interact == "1" or (self.db.profile.interact == "2" and ttype ~= "attack") then
		local _,class = UnitClass("player")
		local _,race = UnitRace("player")
		local tauren = (race == "Tauren") and ((class ~= "DRUID") or (GetShapeshiftForm(true) == 0))
		
		for k,v in pairs(self.interactrangeset) do
			local r = tonumber((tauren and v.tauren) or v.max)
			if limit < r then limit = r end
			
			if CheckInteractDistance("target", k) == 1 then
				if rmax > r then
					name = v.name
					rmax = r
				end
			else
				if rmin < r and r < rmax then
					rmin = r
				end
			end
		end
	end
	
	self:GetRangeSet(ctype, ttype)
	
	for k,v in pairs(self.rangeset[ctype][ttype]) do
		local inrange = self:IsActionInRange(v.slot)
		
		local r = tonumber(k)
		local r0 = tonumber(v.min)
		if inrange and limit < r then limit = r end
		
		if inrange == 1 then
			if rmax > r then
				name = v.name
				rmax = r
			end
		elseif v.min and inrange == 0 then				-- CHECK, 6-25&8-25
			if r0 < rmax and (rmax < r or imax < r) then
				name = v.name
				rmax = r0
			end
		end
		if inrange == 0 then
			if rmin < r and r < rmax then
				if not (v.min and rmin < r0) then		-- NOTICE: Unknown case
					rmin = r
				end
			end
		elseif v.min and inrange == 1 then
			if rmin < r0 and r0 < rmax then
				rmin = r0
			end
		end
	end
	
	if UnitIsUnit("target", "pet") then
		for k,v in pairs(self.petrangeset) do
			local inrange = self:IsActionInRange(k)
			
			local r = tonumber(v.max)
			if inrange and limit < r then limit = r end
			
			if inrange == 1 then
				if rmax > r then
					name = v.name
					rmax = r
				end
			elseif inrange == 0 then
				if rmin < r  and r < rmax then
					rmin = r
				end
			end
		end
	end
	
	local rangetext = nil
	
	if rmax <= self.db.profile.rangelimit then
		if self.db.profile.format == "1" then
			rangetext = L["Within %dyd"]:format(rmax)
		elseif self.db.profile.format == "2" then
			rangetext = L["%dyd - %dyd"]:format(rmin, rmax)
		else
			rangetext = L["%d - %d"]:format(rmin, rmax)
		end
		color = self.db.profile.color["Within "..utype]
	else
		if self.db.profile.format == "1" then
			rangetext = L["Out of %dyd"]:format(limit)
		elseif self.db.profile.format == "2" then
			rangetext = L["%dyd -"]:format(limit)
		else
			rangetext = L["%d -"]:format(limit)
		end
		color = self.db.profile.color["Out of "..utype]
	end
	
	if self.db.profile.spellname and name then
		rangetext = rangetext.." ("..name..")"
	end
	
	return rangetext, color
end
