--[[---------------------------------------------------------------------------------
  This is a table of ranks, which rank of which buff spell has a required level
----------------------------------------------------------------------------------]]

local L = AceLibrary:GetInstance("AceLocale-2.0"):new("Clique")

local buff_lookup = {
    -- Buff lookups
	[L"BUFF_PWF"]            	= {1,12,24,36,48,60},
	[L"BUFF_PWS"]            	= {6,12,18,24,30,36,42,48,54,60},
	[L"BUFF_SP"]             	= {30,42,56},
	[L"BUFF_DS"]             	= {30,40,50,60},
	[L"BUFF_RENEW"]          	= {8,14,20,26,32,38,44,50,56,60},   
	[L"BUFF_MOTW"]           	= {1,10,20,30,40,50,60},
	[L"BUFF_THORNS"]         	= {6,14,24,34,44,54},
	[L"BUFF_REJUVENATION"]   	= {4,10,16,22,28,34,40,46,52,58,60},
	[L"BUFF_REGROWTH"]       	= {12,18,24,30,36,42,48,54,60},
	[L"BUFF_AI"]             	= {1,14,28,42,56},
	[L"BUFF_DM"]             	= {12,24,34,48,60},
	[L"BUFF_AM"]             	= {18,30,42,54},
	[L"BUFF_BOM"]            	= {4,12,22,32,42,52,60},
	[L"BUFF_BOP"]            	= {10,24,38},
	[L"BUFF_BOW"]            	= {14,24,34,44,54,60},
	[L"BUFF_BOS"]            	= {20,30,40,50,60},
	[L"BUFF_BOL"]            	= {40,50,60},
	[L"BUFF_BOSFC"]          	= {46,54},
}

local cure_lookup = {
    -- Cure lookups
	[L"CURE_CURE_DISEASE"]          = {"Disease"},
	[L"CURE_ABOLISH_DISEASE"]       = {"Disease"},
	[L"CURE_PURIFY"]                = {"Disease", "Poison"},
	[L"CURE_CLEANSE"]               = {"Disease", "Poison", "Magic"},
	[L"CURE_DISPEL_MAGIC"]          = {"Magic"},
	[L"CURE_CURE_POISON"]           = {"Poison"},
	[L"CURE_ABOLISH_POISON"]        = {"Poison"},
	[L"CURE_REMOVE_LESSER_CURSE"]   = {"Curse"},
	[L"CURE_REMOVE_CURSE"]          = {"Curse"},
	["Disease"] = {
		L"CURE_ABOLISH_DISEASE",
		L"CURE_CURE_DISEASE",
		L"CURE_CLEANSE",
		L"CURE_PURIFY",
	},
	["Poison"] = {
		L"CURE_ABOLISH_POISON",
		L"CURE_CURE_POISON",
		L"CURE_CLEANSE",
		L"CURE_PURIFY",
	},
	["Magic"] = {
		L"CURE_DISPEL_MAGIC",
		L"CURE_CLEANSE",
	},
	["Curse"] = {
		L"CURE_REMOVE_LESSER_CURSE",
		L"CURE_REMOVE_CURSE",
	},
}

local dual_lookup = {
    -- Any spell that can be cast on friendly or hostile units
    [L"DUAL_HOLY_SHOCK"]     = true,
    [L"DUAL_MIND_VISION"]    = true,
    [L"CURE_DISPEL_MAGIC"]   = true,
}

--[[---------------------------------------------------------------------------------
  Register all of our utility functions
----------------------------------------------------------------------------------]]

function Clique:RegUtilFuncs()
    self:RegisterCustomFunction("Clique:CastSpell(\"spell\")", "Clique:CastSpell(\"spell\")", "Casts a spell on Clique.unit without changing targets")
    self:RegisterCustomFunction("Clique:IsBuffActive(\"buff\")", "Clique:IsBuffActive(\"buff\")", "Checks whether a buff or debuff is active on Clique.unit")
    self:RegisterCustomFunction("Clique:UnitMenu()", "Clique:UnitMenu()", "Pops up the unit menu for Clique.unit")
    self:RegisterCustomFunction("Clique:CureAny()", "Clique:CureAny()", "Attempts to cure any ailments on Clique.unit")
    self:RegisterCustomFunction("Clique:NewSpell(\"spell\", \"buff\")", "Clique:NewSpell(\"spell\", \"buff\")", "Casts a spell if a given buff is NOT on a target")
    self:RegisterCustomFunction("Clique:TargetUnit()", "Clique:TargetUnit()", "Targets the unit you clicked on.")
    self:RegisterCustomFunction("Clique:AssistUnit()", "Clique:AssistUnit()", "Assists the unit you clicked on.")
end    

--[[---------------------------------------------------------------------------------
  Scans the spellbook to determine which buffs/cures we know
----------------------------------------------------------------------------------]]

function Clique:ScanSpellbook()
    self:LevelDebug(2, "Clique:ScanSpellbok()")
    
    local tabs = GetNumSpellTabs()
    local name,texture,start,offset = GetSpellTabInfo(tabs)
    local numspells = start + offset
    
    self.spellbook = self:ClearTable(self.spellbook)
	self.spellbook.buffs = buff_lookup
    
    -- ace:print("Scanning " .. tabs .. " tabs for spells.")
    for i=1,numspells do
        local name,rank = GetSpellName(i, BOOKTYPE_SPELL)
		-- ace:print("** Scanning spell id: " .. i .. " found " .. name .. ".")
		local _,_,numrank = string.find(rank, L"RANK" .. " (%d+)")
		numrank = tonumber(numrank)
		
        if buff_lookup[name] or cure_lookup[name] then
            if numrank then
                self.spellbook[name] = numrank
            else
                self.spellbook[name] = true
            end
        end
        
        if dual_lookup[name] and numrank then
            local spell = string.format("%s(%s %d)", name, L"RANK", numrank)
            dual_lookup[spell] = true
        end
    end
end

function Clique:IsDualSpell(spell)
    return dual_lookup[spell]
end

--[[---------------------------------------------------------------------------------
  IsBuffActive() - Simple enough
----------------------------------------------------------------------------------]]

function Clique:IsBuffActive(spell, unit)
	if not unit then unit = Clique.unit end
	if not UnitExists(unit) then return end
	
	if string.find(spell, "Interface\\Icons\\") then
		for i=1,32 do
			local texture = UnitBuff(unit, i)
			if texture == spell then return true end
		end
		
		for i=1,16 do
			local texture = UnitDebuff(unit, i)
			if texture == spell then return true end
		end
		
		return false
	end
	
	
	if not self.tooltip then 
		local tt = CreateFrame("GameTooltip", "CliqueTooltip", nil, "GameTooltipTemplate")
		tt:SetOwner(tt, "ANCHOR_NONE")
		tt.name = CliqueTooltipTextLeft1
		self.tooltip = tt
	end

	-- Check buffs
	for i=1,32 do
		if not UnitBuff(unit, i) then return nil end

		self.tooltip:ClearLines()		
		self.tooltip:SetUnitBuff(unit, i)
		if string.find(self.tooltip.name:GetText(), spell) then
			return i
		end
	end

	-- Check debuffs
	for i=1,32 do
		if not UnitDebuff(unit, i) then return nil end

		self.tooltip:ClearLines()		
		self.tooltip:SetUnitDebuff(unit, i)
		if string.find(self.tooltip.name:GetText(), spell) then
			return i
		end
	end
	return false
end	

function Clique:IsBuff(name)
    return buff_lookup[name]
end

function Clique:BuffTest(name, rank, unit)
    if not self:IsBuff(name) then return end

    local level = UnitLevel(unit)
    return (buff_lookup[name][rank] - 10) <= level
end

--[[---------------------------------------------------------------------------------
  Handles the "smart buff" casting, determining which rank of a specified
  spell to cast on the given unit (based on level restrictions)
----------------------------------------------------------------------------------]]

function Clique:BestRank(name, unit)
    -- If we don't know the spell
	if not unit then unit = Clique.unit end
    if not self.spellbook[name] or not buff_lookup[name] or not UnitExists(unit) then return end
    
    local level = UnitLevel(unit)
    local maxrank = self.spellbook[name]
    local castrank = nil
    
    -- Make sure we don't go beyond the rank we currently know

    for i=1,maxrank do
        if self:BuffTest(name, i, unit) then
            castrank = i
        end
    end
    
    if castrank then
		self:LevelDebug(3, "Trying to cast: %s(%s %d)", name, L"RANK", castrank)
        Clique:CastSpell(string.format("%s(%s %d)", name, L"RANK", castrank), unit)
        return castrank
    end
    return true
end

--[[---------------------------------------------------------------------------------
  This is a small unitID cache on unit names which returns the friendly unitID
  of a specified unit.  This helps to convert raid2targettarget into the more
  friendly (and usable) raid14 allowing us to click-cast without changing 
  targets.
----------------------------------------------------------------------------------]]

local unitCache = {}
local RAID_IDS = {}
local PARTY_IDS = {}
for i=1,MAX_RAID_MEMBERS do RAID_IDS[i] = "raid"..i end
for i=1,MAX_PARTY_MEMBERS do PARTY_IDS[i] = "party"..i end

function Clique:GetFriendlyUnit(unit)
    local name = UnitName(unit)
    local cache = unitCache[name]

	--ace:print("Unit: " .. unit .. " Cache: " .. tostring(cache))
	
    if cache then
        if UnitName(cache) == name then
            return cache
        end
    end
    
    local unitID = nil
    local num = GetNumRaidMembers()
    
    local tbl
    if not UnitIsUnit("player", unit) then
        if num > 0 then
            tbl = RAID_IDS
        else
            num = GetNumPartyMembers()
            tbl = PARTY_IDS
        end
        
        for i=1,num do
            local u = tbl[i]
            if UnitIsUnit(u, unit) then
                unitID = u
                break
            end
        end
    else
        unitID = "player"
    end
    
    unitCache[name] = unitID

	--ace:print("UnitID: " .. (tostring(unitID or unit)))

	return unitID
end

--[[---------------------------------------------------------------------------------
  Simple table re-use function.  Prevents memory churn
----------------------------------------------------------------------------------]]

function Clique:ClearTable(tbl)
    if not tbl then tbl = {} end
    for k in pairs(tbl) do tbl[k] = nil end
    table.setn(tbl, 0)
    return tbl
end

--[[---------------------------------------------------------------------------------
  Sets the tooltip OnEnter
----------------------------------------------------------------------------------]]

function Clique:SetTooltip(frame, text)
    local func = function()
        GameTooltip:SetOwner(frame, "ANCHOR_TOPLEFT")
		GameTooltip:SetText(text)
        GameTooltip:Show()
    end
    
    frame:SetScript("OnEnter", func)
    frame:SetScript("OnLeave", function() GameTooltip:Hide() end)
end

--[[---------------------------------------------------------------------------------
  Pops up the user menu
----------------------------------------------------------------------------------]]

function Clique:UnitMenu(unit)
    local type,x = nil,nil
	unit = unit or Clique.unit
    
    if unit == "player" then
        type = PlayerFrameDropDown
    elseif unit == "target" then
        type = TargetFrameDropDown
    elseif unit == "pet" then
        type = PetFrameDropDown
    elseif string.find(unit, "party") then
        _,_,x = string.find(unit, "(%d+)")
        type = getglobal("PartyMemberFrame"..x.."DropDown")
    elseif string.find(unit, "raid") then
        _,_,x = string.find(unit, "(%d+)")
        type = FriendsDropDown
        this.unit = unit
        this.name = UnitName(unit)
        this.id = this:GetID()
        FriendsDropDown.displayMode = "MENU"
        FriendsDropDown.initialize = RaidFrameDropDown_Initialize
    end
    
    if x then this:SetID(x) end
    
    if type then
        type.unit = unit
        type.name = UnitName(unit)
        ToggleDropDownMenu(1, nil, type,"cursor")
        return true
    end
    return true
end	

--[[---------------------------------------------------------------------------------
  Dispels the unit of any ailments you can cure
----------------------------------------------------------------------------------]]

function Clique:CureAny(unit)
	unit = unit or Clique.unit
	local ailments = Clique:ClearTable(self.work)
	
	for i=1,16 do
		local texture,stack,type = UnitDebuff(unit, i)
		if not texture then break end
		if type then 
			ailments[type] = true
		end
	end
	if next(ailments) then
		self:Debug("Checking ailments")
		for type in pairs(ailments) do
			self:Debug("Checking for type: " .. type)
			for i,cure in pairs(cure_lookup[type]) do
				self:Debug("Checking spell: " .. cure)		
				if self.spellbook[cure] then
					self:Debug(cure)
					Clique:CastSpell(cure, unit)
					return cure
				end
			end
		end
	end
    return true
end

function Clique:NewSpell(spell, buff)
    if not Clique:IsBuffActive(buff or spell) then
        if Clique:IsBuff(spell) then
            Clique:BestRank(spell)
        else
            Clique:CastSpell(spell)
        end
    end
    return true
end

function Clique:TargetUnit(unit)
    if not unit then unit = Clique.unit end
    TargetUnit(unit)
    return true
end

function Clique:AssistUnit(unit)
    if not unit then unit = Clique.unit end
    AssistUnit(unit)
    return true
end