local _, T = ...
if T.SkipLocalActionBook then return end
local AB = assert(T.ActionBook:compatible(2,21), "A compatible version of ActionBook is required")
local RW = assert(AB:compatible("Rewire",1,14), "A compatible version of Rewire is required")
local KR = assert(AB:compatible("Kindred",1,14), "A compatible version of Kindred is required")
local L, EV = AB:locale(), assert(T.Evie)
local spellFeedback, itemHint, mountHint, mountMap

local NormalizeInRange = {[0]=0, 1, [true]=1, [false]=0}
local _, CLASS = UnitClass("player")

local safequote do
	local r = {u="\\117", ["{"]="\\123", ["}"]="\\125"}
	function safequote(s)
		return (("%q"):format(s):gsub("[{}u]", r))
	end
end

do -- mount: mount ID
	local function summonAction(mountID)
		--return "func", C_MountJournal.SummonByID, mountID
	end
	if CLASS == "DRUID" then
		local clickPrefix do
			local MOONKIN_FORM = GetSpellInfo(24858)
			local bni, bn = 0 repeat
				bn, bni = "AB:M!" .. bni, bni + 1
			until GetClickFrame(bn) == nil
			local b = CreateFrame("Button", bn, nil, "SecureActionButtonTemplate")
			b:SetAttribute("macrotext", "/cancelform [nocombat]")
			b:SetScript("PreClick", function()
				local sf = GetShapeshiftForm()
				local _, _, _, fsid = GetShapeshiftFormInfo(sf ~= 0 and sf or -1)
				local n = GetSpellInfo(fsid or -1)
				if not (InCombatLockdown() or n == MOONKIN_FORM) then
					b:SetAttribute("type", "macro")
				end
			end)
			b:SetScript("PostClick", function(_, btn)
				if not InCombatLockdown() then
					b:SetAttribute("type", nil)
				end
				btn = tonumber(btn)
				if btn then
					--C_MountJournal.SummonByID(btn)
				end
			end)
			clickPrefix = SLASH_CLICK1 .. " " .. bn .. " "
		end
		function summonAction(mountID)
			return "attribute", "type","macro", "macrotext",clickPrefix .. mountID
		end
	end

	local function mountSync()
		-- local changed, myFactionId = false, UnitFactionGroup("player") == "Horde" and 0 or 1
		-- local idm, GetMountInfo, oldID, curID = C_MountJournal.GetMountIDs(), C_MountJournal.GetMountInfoByID
		-- for mid=1,#idm do
		-- 	mid = idm[mid]
		-- 	local _1, sid, _3, _4, _5, _6, _7, factionLocked, factionId, hide, have = GetMountInfo(mid)
		-- 	curID, oldID = not hide and (not factionLocked or factionId == myFactionId) and sid ~= 0
		-- 	               and have and RW:IsSpellCastable(sid) and mid or nil, mountMap[sid]
		-- 	if oldID ~= curID then
		-- 		local sname, srank, rname = GetSpellInfo(sid)
		-- 		rname = (sname .. "(" .. (srank or "") .. ")") -- Paladin/Warlock/Death Knight horses have spell ranks
		-- 		changed, mountMap[sid], mountMap[sname], mountMap[sname:lower()], mountMap[rname], mountMap[rname:lower()] =
		-- 			true, curID, curID, curID, curID, curID
		-- 	end
		-- end
		-- mountMap[150544] = 0
		-- if changed then AB:NotifyObservers("spell") end
  end

	function mountHint(id)
		local usable = (not (InCombatLockdown() or IsIndoors())) and HasFullControl() and not UnitIsDeadOrGhost("player")
		--local cname, sid, icon, active, usable2 = C_MountJournal.GetMountInfoByID(id)
		local time, cdStart, cdLength = GetTime(), GetSpellCooldown(sid)
		--return usable and cdStart == 0 and usable2, active and 1 or 0, icon, cname, 0, (cdStart or 0) > 0 and (cdStart+cdLength-time) or 0, cdLength, GameTooltip.SetMountBySpellID, sid
  end

  local actionMap = {}

	local function createMount(id)
		if type(id) == "number" and not actionMap[id] then
			local _, sid = C_MountJournal.GetMountInfoByID(id)
			if mountMap[sid] then
				actionMap[id] = AB:CreateActionSlot(mountHint, id, summonAction(id))
			end
		end
		return actionMap[id]
  end

	local function describeMount(id)
		local name, sid, icon = C_MountJournal.GetMountInfoByID(id)
		return L"Mount", name, icon, nil, GameTooltip.SetMountBySpellID, sid
	end
	AB:RegisterActionType("mount", createMount, describeMount)
	do -- random
		-- local rname, _, ricon = GetSpellInfo(150544)
		-- actionMap[0] = AB:CreateActionSlot(
    --   function()
    --     return HasFullControl() and not IsIndoors(),
    --     IsMounted() and 1 or 0, ricon,
    --     rname,
    --     0,
    --     0,
    --     0,
    --     GameTooltip.SetMountBySpellID, 150544
    --   end,
    --   nil,
    --   summonAction(0)
    -- )
		-- RW:SetCastEscapeAction(GetSpellInfo(150544), actionMap[0])
		-- RW:SetCastEscapeAction("spell:150544", actionMap[0])
	end
	EV.PLAYER_ENTERING_WORLD = mountSync
	mountMap = {}
end
do -- spell: spell ID + mount spell ID
	local function isCurrentForm(q)
		local id = GetShapeshiftForm()
		if id == 0 then return end
		local _, _, _, sid = GetShapeshiftFormInfo(id)
		return q == sid or q == GetSpellInfo(sid or 0) or (sid and q and ("" .. sid) == q)
	end
	local actionMap, spellMap = {}, {}
	local function SetSpellBookItem(self, id)
		return self:SetSpellBookItem(id, BOOKTYPE_SPELL)
	end
	local function spellHint(n, _modState, target)
		if not n then return end
		local mmID = mountMap[n]
		if mmID then return mountHint(mmID) end
		local time, msid, sname, _, _, _, _, _, sid = GetTime(), spellMap[n], GetSpellInfo(n)
		if not sname then return end
		local inRange, usable, nomana, hasRange = NormalizeInRange[IsSpellInRange(n, target or "target")], IsUsableSpell(n)
		inRange, hasRange = inRange ~= 0, inRange ~= nil
		local cooldown, cdLength, enabled = GetSpellCooldown(n)
		local cdLeft = (cooldown or 0) > 0 and (enabled ~= 0) and (cooldown + cdLength - time) or 0
		local count, charges, maxCharges, chargeStart, chargeDuration = GetSpellCount(n), GetSpellCharges(n)
		local state = ((IsSelectedSpellBookItem(n) or IsCurrentSpell(n) or isCurrentForm(n)  or enabled == 0) and 1 or 0) +
		              (0) + (nomana and 8 or 0) + (inRange and 0 or 16) + (charges and charges > 0 and 64 or 0) +
		              (hasRange and 512 or 0) + (usable and 0 or 1024) + (enabled == 0 and 2048 or 0)
		usable = not not (usable and inRange and (cooldown or 0) == 0 or (enabled == 0))
		if charges and maxCharges and charges < maxCharges and cdLeft == 0 then
			cdLeft, cdLength = chargeStart-time + chargeDuration, chargeDuration
		end
		local sbslot = msid and msid ~= 161691 and FindSpellBookSlotBySpellID(msid)
		return usable, state, GetSpellTexture(n), sname or n, count <= 1 and charges or count, cdLeft, cdLength, sbslot and SetSpellBookItem or (msid or sid) and GameTooltip.SetSpellByID, sbslot or sid or msid
	end
	function spellFeedback(sname, target, spellId)
		spellMap[sname] = spellId or spellMap[sname] or tonumber((GetSpellLink(sname) or ""):match("spell:(%d+)"))
		return spellHint(sname, nil, target)
	end
	local function createSpell(id)
		if type(id) ~= "number" then return end
		local action = mountMap[id]
		if action then
			return AB:GetActionSlot("mount", action)
		end
		
		local castable, rwCastType = RW:IsSpellCastable(id)
		if not castable then
			return
		end
		if rwCastType == "forced-id-cast" then
			action = id
		else
			local s0, r0 = GetSpellInfo(id), GetSpellSubtext(id)
			local o, s = pcall(GetSpellInfo, s0, r0)
			if not (o and s and s0) then return end
			local _, r1 = pcall(GetSpellSubtext, s0)
			action = (r0 and r1 ~= r0 and FindSpellBookSlotBySpellID(id)) and (s0 .. "(" .. r0 .. ")") or s0
		end
		
		if action and not actionMap[action] then
			spellMap[action], actionMap[action] = id, AB:CreateActionSlot(spellHint, action, "attribute", "type","spell", "spell",action, "checkselfcast",true, "checkfocuscast",true)
			if type(action) == "string" then
				spellMap[action:lower()] = id
			end
		end
		return actionMap[action]
	end
	local function describeSpell(id)
		local name2, _, icon2, rank, name, _, icon = nil, nil, nil, GetSpellSubtext(id), GetSpellInfo(id)
		local _, castType = RW:IsSpellCastable(id)
		if name and castType ~= "forced-id-cast" then
			name2, rank, icon2 = GetSpellInfo(name, rank)
		end
		return mountMap[id] and L"Mount" or L"Spell", (name2 or name or "?") .. (rank and rank ~= "" and rank ~= GetSpellSubtext(name) and " (" .. rank .. ")" or ""), icon2 or icon, nil, GameTooltip.SetSpellByID, id
	end
	AB:RegisterActionType("spell", createSpell, describeSpell)
	do -- specials
    local gab = GetSpellInfo(161691)
		-- actionMap[gab] = AB:CreateActionSlot(spellHint, gab, "conditional", "[outpost]", "attribute", "type","spell", "spell",gab)
		-- spellMap[gab], spellMap[gab:lower()] = 161691, 161691
		-- actionMap[150544] = AB:GetActionSlot("mount", 0)
	end
	
	function EV.SPELLS_CHANGED()
		AB:NotifyObservers("spell")
	end
end
do -- item: items ID/inventory slot
	local actionMap, itemIdMap, lastSlot = {}, {}, INVSLOT_LAST_EQUIPPED
	local function containerTip(self, bagslot)
		local slot = bagslot % 100
		self:SetBagItem((bagslot-slot)/100, slot)
	end
	local function playerInventoryTip(self, slot)
		self:SetInventoryItem("player", slot)
	end
	local function GetItemLocation(iid, name, name2)
		local name2, cb, cs, n = name2 and name2:lower()
		for i=1, INVSLOT_LAST_EQUIPPED do
			if GetInventoryItemID("player", i) == iid then
				n = GetItemInfo(GetInventoryItemLink("player", i))
				if n == name or n and name2 and n:lower() == name2 then
					return nil, i
				elseif not cs then
					cb, cs = nil, i
				end
			end
		end
		for i=0,4 do
			for j=1,GetContainerNumSlots(i) do
				if iid == GetContainerItemID(i, j) then
					n = GetItemInfo(GetContainerItemLink(i, j))
					if n == name or n and name2 and n:lower() == name2 then
						return i, j
					elseif not cs then
						cb, cs = i, j
					end
				end
			end
		end
		return cb, cs
	end
	function itemHint(ident, _modState, target, purpose, ibag, islot)
		local name, link, icon, _, bag, slot, tip, tipArg
		if type(ident) == "number" and ident <= lastSlot then
			local invid = GetInventoryItemID("player", ident)
			if invid == nil then return end
			bag, slot, name, link = nil, invid, GetItemInfo(GetInventoryItemLink("player", ident) or invid)
			if name then ident = name end
		else
			name, link, _, _, _, _, _, _, _, icon = GetItemInfo(ident)
		end
		local iid, cdStart, cdLen, enabled, cdLeft = (link and tonumber(link:match("item:([x%x]+)"))) or itemIdMap[ident]
		if iid then
			cdStart, cdLen, enabled = GetItemCooldown(iid)
			local time = GetTime()
			cdLeft = (cdStart or 0) > 0 and (enabled ~= 0) and (cdStart + cdLen - time)
		end
		local inRange, hasRange = NormalizeInRange[IsItemInRange(ident, target or "target")]
		inRange, hasRange = inRange ~= 0, inRange ~= nil
		if ibag and is   then
			bag, slot =   g, islot
		elseif iid then
			bag, slot = GetItemLocation(iid, name, ident)
		end
		if bag and slot then
			tip, tipArg = containerTip, bag * 100 + slot
		elseif slot then
			tip, tipArg = playerInventoryTip, slot
		elseif iid then
			tip, tipArg = GameTooltip.SetItemByID, iid
		end
		local nCharge = GetItemCount(ident, false, true) or 0
    local usable = nCharge > 0 and (GetItemSpell(ident) == nil or IsUsableItem(ident))
    local state = nil
		-- local state = (IsCurrcal rname, _, ricon = GetSpellInfo(150544)
		-- actionMap[0] = AB:CreateActionSlot(
    --   function()
    --     return HasFullControl() and not IsIndoors(),
    --     IsMounted() and 1 or 0, ricon,
    --     rname,
    --     0,
    --     0,
    --     0,
    --     GameTooltip.SetMountBySpellID, 150544
    --   end,
    --   nil,
    --   summonAction(0)
    -- )
		-- RW:SetCastEscapeAction(GetSpellInfo(150544), actionMap[0])
		-- RW:SetCastEentItem(ident) and 1 or 0) + (inRange and 0 or 16) + (slot and IsEquippableItem(ident) and (bag and (purpose == "equip" and 128 or 0) or (slot and 256 or 0)) or 0) + (hasRange and 512 or 0) + (usable and 0 or 1024) + (enabled == 0 and 2048 or 0)
		return not not (usable and inRange and (cdLen or 0) == 0), state, icon or GetItemIcon(ident), name or ident, nCharge,
			cdLeft or 0, cdLen or 0, tip, tipArg
	end
	local function createItem(id, byName, forceShow, onlyEquipped)
		if type(id) ~= "number" then return end
		local name = id <= lastSlot and id or (byName and GetItemInfo(id) or ("item:" .. id))
		if not forceShow and onlyEquipped and not ((id > lastSlot and IsEquippedItem(name)) or (id <= lastSlot and GetInventoryItemLink("player", id))) then return end
		if not forceShow and GetItemCount(name) == 0 then return end
		if not actionMap[name] then
			actionMap[name], itemIdMap[name] = AB:CreateActionSlot(itemHint, name, "attribute", "type","item", "item",name, "checkselfcast",true, "checkfocuscast",true), id
		end
		return actionMap[name]
	end
	local function describeItem(id)
		return L"Item", GetItemInfo(id), GetItemIcon(id), nil, GameTooltip.SetItemByID, tonumber(id)
	end
	AB:RegisterActionType("item", createItem, describeItem, {"byName", "forceShow", "onlyEquipped"})
	function EV.BAG_UPDATE()
		AB:NotifyObservers("item")
	end
	RW:SetCommandHint(SLASH_EQUIP1, 70, function(_, _, clause, target)
		if clause and clause ~= "" and GetItemInfo(clause) then
			return true, itemHint(clause, nil, target, "equip")
		end
	end)
	RW:SetCommandHint(SLASH_EQUIP_TO_SLOT1, 70, function(_, _, clause)
		local item = clause and clause:match("^%s*%d+%s+(.*)")
		if item then
			return RW:GetCommandAction(SLASH_EQUIP1, item)
		end
	end)
end
do -- macrotext
	local map = {}
	local function macroHint(mtext, modLockState)
		return RW:GetMacroAction(mtext, modLockState)
	end
	local function createMacrotext(macrotext)
		if type(macrotext) ~= "string" then return end
		if not map[macrotext] then
			map[macrotext] = AB:CreateActionSlot(macroHint, macrotext, "recall", RW:seclib(), "RunMacro", macrotext)
		end
		return map[macrotext]
	end
	local function describeMacrotext(macrotext)
		if macrotext == "" then return L"Custom Macro", L"New Macro", "Interface/Icons/Temp" end
		local _, _, ico = RW:GetMacroAction(macrotext)
		return L"Custom Macro", "", ico
	end
	AB:RegisterActionType("macrotext", createMacrotext, describeMacrotext)
	local function checkReturn(pri, ...)
		if select("#", ...) > 0 then return pri, ... end
	end
	local function canUseViaSCUI(clause)
		if (tonumber(clause) or 0) > INVSLOT_LAST_EQUIPPED then
			-- SCUI will pass to UseInventoryItem
			return false
		end
		local iid = tonumber(clause:match("^%s*item:([x%x]+)"))
		return true
	end
	RW:SetCommandHint("/use", 100, function(_, _, clause, target, _, _, msg)
		if not clause or clause == "" then return end
		local isItemReturn, link, bag, slot = false, SecureCmdItemParse(clause)
		if (bag and slot) or (link and GetItemInfoInstant(link)) then
			if msg == "castrandom-fallback" or canUseViaSCUI(clause) then
				isItemReturn = true
			end
		end
		if isItemReturn then
			return checkReturn(90, itemHint(link, nil, target, nil, bag, slot))
		end
		local sid = clause:match("^spell:(%d+)$")
		if sid or not tonumber(clause, 10) then
			return checkReturn(true, spellFeedback(sid or clause, target))
		end
	end)
	RW:SetCommandHint("/cast", 100, function(_, _, clause, target, _, _, msg)
		if not clause or clause == "" then return end
		local sex = DoesSpellExist(clause) and not tonumber(clause, 10)
		local sid = not sex and clause:match("^spell:(%d+)$")
		if sex or sid then
			return checkReturn(true, spellFeedback(sid or clause, target))
		else
			local link, bag, slot = SecureCmdItemParse(clause)
			if ((bag and slot) or (link and GetItemInfoInstant(link))) and
			   (msg == "castrandom-fallback" or canUseViaSCUI(clause)) then
				return checkReturn(90, itemHint(link, nil, target, nil, bag, slot))
			end
		end
	end)
	RW:SetCommandHint(SLASH_CASTSEQUENCE1, 100, function(_, _, clause, target)
		if not clause or clause == "" then return end
		local _, item, spell = QueryCastSequence(clause)
		clause = (item or spell)
		if clause then
			return RW:GetCommandAction("/use", clause, target)
		end
	end)
	do -- /userandom
		local f, seed = CreateFrame("Frame", nil, nil, "SecureHandlerBaseTemplate"), math.random(2^30)
		f:SetFrameRef("RW", RW:seclib())
		f:Execute("seed, t, RW = " .. seed .. ", newtable(), self:GetFrameRef('RW'), self:SetAttribute('frameref-RW', nil)")
		f:SetAttribute("RunSlashCmd", [[--
			local cmd, v, target, s = ...
			if v == "" or not v then
				return
			elseif not t[v] then
				local tv, tn = newtable(), 1
				for f in v:gmatch("[^,]+") do
					tv[tn], tn = f:match("^%s*(.-)%s*$"), tn + 1
				end
				t[v], tv[0] = tv, seed
			end
			v = t[v]
			v, v[0] = v[1 + v[0] % #v], (v[0] * 37 + 13) % 2^32
			return RW:RunAttribute("RunSlashCmd", "/cast", v, target, "opt-into-cr-fallback")
		]])
		RW:RegisterCommand(SLASH_USERANDOM1, true, true, f)
		local sc, ic = GetManagedEnvironment(f).t, {}
		RW:SetCommandHint(SLASH_USERANDOM1, 50, function(_, _, clause, target)
			if not clause or clause == "" then return end
			local t1, t, n = sc[clause]
			t = t1 or ic[clause]
			if t1 then
				ic[clause] = nil
			elseif not t then
				t, n = {[0]=seed}, 0
				for s in clause:gmatch("[^,]+") do
					t[n+1], n = s, n + 1
				end
				ic[clause] = t
			end
			if t then
				local nextArg = t[1 + t[0] % #t]
				if tonumber(nextArg) and  GetItemInfo(nextArg) then
					nextArg = "item:" .. nextArg
				end
				return RW:GetCommandAction("/use", nextArg, target, nil, "castrandom-fallback")
			end
		end)
	end
end
do -- macro: name
	local map, f, sm, macroHint = {}, CreateFrame("Frame", nil, nil, "SecureHandlerBaseTemplate") do
		f:SetFrameRef("Rewire", RW:seclib())
		f:Execute('macros, RW = newtable(), self:GetFrameRef("Rewire")')
		f:SetAttribute("RunNamedMacro", [[-- AB_RunStoredMacro_Command
			return RW:RunAttribute('RunMacro', macros[...])
		]])
		sm = GetManagedEnvironment(f).macros
		local pending
		local function sync()
			local s, numGlobal, numChar = "", GetNumMacros()
			for k in rtable.pairs(sm) do
				if not GetMacroInfo(k) then
					s = ("%s\nmacros[%s] = nil"):format(s, safequote(k))
					RW:ClearNamedMacroHandler(k, f)
				end
			end
			local ofs = MAX_ACCOUNT_MACROS - numGlobal
			for i=1,numGlobal + numChar do
				local name, _, text = GetMacroInfo((i > numGlobal and ofs or 0)+i)
				if name and sm[name] ~= text then
					s = ("%s\nmacros[%s] = %s"):format(s, safequote(name), safequote(text))
					RW:SetNamedMacroHandler(name, f, macroHint)
				end
			end
			if s ~= "" then
				f:Execute(s)
				AB:NotifyObservers("macro")
			end
			pending = nil
			return "remove"
		end
		function EV.UPDATE_MACROS()
			if InCombatLockdown() then
				pending = pending or EV.RegisterEvent("PLAYER_REGEN_ENABLED", sync) or 1
			else
				sync()
			end
		end
	end
	local function check(name, pri, ...)
		local _, usable, state, icon, caption, count, cdLeft, cdLength = nil, ...
		if usable == nil then
			if not icon then
				_, icon = GetMacroInfo(name)
			end
			return icon and 10 or false, sm[name] ~= nil, state or 0, icon, caption or name, count or 0, cdLeft or 0, cdLength or 0, select(8, ...)
		end
		return pri, ...
	end
	local function tail(_, ...)
		return ...
	end
	local function namedMacroHint(name, modState)
		return tail(check(name, 10, RW:GetMacroAction(sm[name], modState)))
	end
	function macroHint(name, _target, modState, priLimit)
		return check(name, RW:GetMacroAction(sm[name], modState, priLimit))
	end
	local function createNamedMacro(name, forceShow)
		if type(name) == "string" and (forceShow or sm[name]) then
			if not map[name] then
				map[name] = AB:CreateActionSlot(namedMacroHint, name, "recall", RW:seclib(), "RunSlashCmd", "/runmacro", name)
			end
			return map[name]
		end
	end
	local function describeMacro(name)
		local n, ico = GetMacroInfo(name)
		return L"Macro", n or name, ico
	end
	AB:RegisterActionType("macro", createNamedMacro, describeMacro, {"forceShow"})
end
do -- raidmark
	local map = {}
	local function CanChangeRaidMarkers(unit)
		return not not ((not IsInRaid() or UnitIsGroupLeader("player") or UnitIsGroupAssistant("player")) and not (unit and UnitIsPlayer(unit) and UnitIsEnemy("player", unit)))
	end
	local function click(id)
		if GetRaidTargetIndex("target") == id then id = 0 end
		SetRaidTarget("target", id)
	end
	local function raidmarkHint(i, _, target)
		local target = target or "target"
		return CanChangeRaidMarkers(target), GetRaidTargetIndex(target) == i and 1 or 0, "Interface/TargetingFrame/UI-RaidTargetingIcon_" .. i, _G["RAID_TARGET_" .. i], 0, 0, 0
	end
	local function removeHint()
		return CanChangeRaidMarkers(), 0, "Interface/Icons/INV_Gauntlets_02", REMOVE_WORLD_MARKERS, 0, 0, 0
	end
	map[0] = AB:CreateActionSlot(removeHint, nil, "func", function()
		if not CanChangeRaidMarkers() then return end
		for i=1,8 do
			SetRaidTarget("player", i)
		end
		SetRaidTarget("player", IsInGroup() and 9 or 0)
	end)
	for i=1,8 do
		map[i] = AB:CreateActionSlot(raidmarkHint, i, "func", click, i)
	end
	local function createRaidMark(id)
		return map[id]
	end
	local function describeRaidMark(id)
		if id == 0 then return L"Raid Marker", REMOVE_WORLD_MARKERS, "Interface/Icons/INV_Gauntlets_02" end
		return L"Raid Marker", _G["RAID_TARGET_" .. id], "Interface/TargetingFrame/UI-RaidTargetingIcon_" .. id
	end
	AB:RegisterActionType("raidmark", createRaidMark, describeRaidMark)
	RW:ImportSlashCmd("TARGET_MARKER", true, false, 40, function(_, _, clause, target)
		clause = tonumber(clause)
		if clause == 0 then
			return true, removeHint()
		elseif clause then
			return true, raidmarkHint(clause, nil, target)
		end
	end)
end
do -- petspell: spell ID
	local actionInfo, actionID = { stay={"Interface\\Icons\\Spell_Nature_TimeStop", "PET_ACTION_WAIT"}, move={"Interface\\Icons\\Ability_Hunter_Pet_Goto", "PET_ACTION_MOVE_TO", 1}, follow={"Interface\\Icons\\Ability_Tracking", "PET_ACTION_FOLLOW"}, attack={"Interface\\Icons\\Ability_GhoulFrenzy", "PET_ACTION_ATTACK"},
		defend={"Interface\\Icons\\Ability_Defend", "PET_MODE_DEFENSIVE"}, assist={"Interface\\Icons\\Ability_Hunter_Pet_Assist", "PET_MODE_ASSIST"}, passive={"Interface\\Icons\\Ability_Seal", "PET_MODE_PASSIVE"},
		dismiss={CLASS == "WARLOCK" and "Interface\\Icons\\spell_shadow_sacrificialshield" or "Interface\\Icons\\spell_nature_spiritwolf"}}, {}
	local function petTip(self, slot)
		return self:SetSpellBookItem(slot, "pet")
	end
	local function petHint(sid)
		local info = actionInfo[sid]
		if sid == "dismiss" then
			if CLASS == "HUNTER" and PetCanBeAbandoned() then
				return spellFeedback(2641, nil, 2641)
			end
			return HasFullControl() and UnitExists("pet") and PetCanBeDismissed(), 0, info[1], PET_ACTION_DISMISS
		elseif info then
			local ico, name, slot = info[1], info[2], info[3]
			if GetSpellBookItemTexture(slot or 0, "pet") ~= ico then
				slot = nil
				for i=1,HasPetSpells() or 0 do
					if GetSpellBookItemTexture(i, "pet") == ico and GetSpellBookItemInfo(i, "pet") == "PETACTION" then
						info[3], slot = i, i
						break
					end
				end
			end
			return not not slot, slot and IsSelectedSpellBookItem(slot, "pet") and 1 or 0, ico, _G[name] or (slot and GetSpellBookItemName(slot, "pet")) or "", 0, 0, 0, slot and petTip or nil, slot
		elseif sid then
			return spellFeedback(sid, nil, sid)
		end
	end
	local function createPetAction(id)
		if type(id) == "number" and id > 0 and not actionID[id] then
			actionID[id] = AB:CreateActionSlot(petHint, id, "conditional","[petcontrol,known:" .. id .. "];hide", "attribute", "type","spell", "spell",id)
		end
		return actionID[id]
	end
	local function describePetAction(id)
		if type(id) == "number" then
			local name, _, icon = GetSpellInfo(id)
			return L"Pet Ability", name, icon, nil, GameTooltip.SetSpellByID, id
		elseif actionID[id] then
			local _, _, icon, name, _, _, _, tipf, tipa = petHint(id)
			local _, st = GetSpellBookItemName(tipa or 0, "pet")
			return st or L"Pet Ability", name, icon, nil, tipf, tipa
		end
	end
	AB:RegisterActionType("petspell", createPetAction, describePetAction)
	do
		local cnd, macroMap = "[petcontrol,@pet,help,novehicleui]", {}
		local function check(...)
			if ... ~= nil then
				return true, ...
			end
		end
		local function petmacroHint(slash, _, clause, _target)
			local aid = clause and macroMap[slash]
			if aid then
				return check(petHint(aid))
			end
		end
		local function addPetCommand(cmd, key)
			actionID[key] = AB:CreateActionSlot(petHint, key, "conditional", cnd, "attribute", "type","macro", "macrotext",cmd)
			RW:SetCommandHint(cmd, 75, petmacroHint)
			macroMap[cmd:lower()] = key
		end
		addPetCommand(SLASH_PET_STAY1, "stay")
		addPetCommand(SLASH_PET_MOVE_TO1, "move")
		addPetCommand(SLASH_PET_FOLLOW1, "follow")
		addPetCommand(SLASH_PET_ATTACK1, "attack")
		addPetCommand(SLASH_PET_DEFENSIVE1, "defend")
		addPetCommand(SLASH_PET_ASSIST1, "assist")
		addPetCommand(SLASH_PET_PASSIVE1, "passive")
		if CLASS == "HUNTER" then
			actionID["dismiss"] = AB:CreateActionSlot(petHint, "dismiss", "conditional", cnd, "attribute", "type","macro", "macrotext",SLASH_CAST1.." "..GetSpellInfo(HUNTER_DISMISS_PET))
		else
			actionID["dismiss"] = AB:CreateActionSlot(petHint, "dismiss", "conditional", cnd, "func", PetDismiss)
		end
	end
end
