--[[
Copyright (c) 2006, Hendrik Leppkes < mail@nevcairiel.net >
All rights reserved.

Redistribution and use in source and binary forms, with or without 
modification, are permitted provided that the following conditions are 
met:

    * Redistributions of source code must retain the above copyright
       notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above 
       copyright notice, this list of conditions and the following 
       disclaimer in the documentation and/or other materials provided 
       with the distribution.
    * Neither the name of the Bartender3 Development Team nor the names of 
       its contributors may be used to endorse or promote products derived 
       from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
]]

--[[ $Id: Bartender3_BarStates.lua 29831 2007-03-09 18:56:12Z nevcairiel $ ]]

local L = AceLibrary("AceLocale-2.2"):new("Bartender3")

-- values taken from Babble-Spell-2.2
-- not in the AceLocale for simplicity and a better overview
-- and no dep on BS-2.2, that would be overkill imho :p
local STANCES
if GetLocale() == "deDE" then
	STANCES = {
		-- warrior
		["Battle Stance"] = "Kampfhaltung",
		["Defensive Stance"] = "Verteidigungshaltung",
		["Berserker Stance"] = "Berserkerhaltung",
		-- druid
		["Bear Form"] = "B\195\164rengestalt",
		["Dire Bear Form"] = "Terrorb\195\164rengestalt",
		["Cat Form"] = "Katzengestalt",
		["Tree of Life"] = "Baum des Lebens",
		["Moonkin Form"] = "Mondkingestalt",
		-- rogue
		["Stealth"] = "Verstohlenheit",
	}
elseif GetLocale() == "frFR" then
	STANCES = {
		-- warrior
		["Battle Stance"] = "Posture de combat",
		["Defensive Stance"] = "Posture d\195\169fensive",
		["Berserker Stance"] = "Posture berserker",
		-- druid
		["Bear Form"] = "Forme d'ours",
		["Dire Bear Form"] = "Forme d'ours redoutable",
		["Cat Form"] = "Forme de f\195\169lin",
		["Tree of Life"] = "Arbre de vie",
		["Moonkin Form"] = "Forme de s\195\169l\195\169nien",
		-- rogue
		["Stealth"] = "Camouflage",
	}
elseif GetLocale() == "esES" then
	STANCES = {
		-- warrior
		["Battle Stance"] = "Actitud de batalla",
		["Defensive Stance"] = "Actitud defensiva",
		["Berserker Stance"] = "Actitud rabiosa",
		-- druid
		["Bear Form"] = "Forma de oso",
		["Dire Bear Form"] = "Forma de oso temible",
		["Cat Form"] = "Forma felina",
		["Tree of Life"] = "\195\129rbol de vida",
		["Moonkin Form"] = "Forma de lech\195\186cico lunar",
		-- rogue
		["Stealth"] = "Sigilo",
	}
elseif GetLocale() == "zhCN" then
	STANCES = {
		-- warrior
		["Battle Stance"] = "战斗姿态",
		["Defensive Stance"] = "防御姿态",
		["Berserker Stance"] = "狂暴姿态",
		-- druid
		["Bear Form"] = "熊形态",
		["Dire Bear Form"] = "巨熊形态",
		["Cat Form"] = "猎豹形态",
		["Tree of Life"] = "Tree of Life", -- need this
		["Moonkin Form"] = "枭兽形态",
		-- rogue
		["Stealth"] = "潜行",
	}
elseif GetLocale() == "zhTW" then
	STANCES = {
		-- warrior
		["Battle Stance"] = "戰鬥姿態",
		["Defensive Stance"] = "防禦姿態",
		["Berserker Stance"] = "狂暴姿態",
		-- druid
		["Bear Form"] = "熊形態",
		["Dire Bear Form"] = "巨熊形態",
		["Cat Form"] = "獵豹形態",
		["Tree of Life"] = "Tree of Life", -- need this
		["Moonkin Form"] = "梟獸形態",
		-- rogue
		["Stealth"] = "潛行",
	}
elseif GetLocale() == "koKR" then
	STANCES = {
		-- warrior
		["Battle Stance"] = "전투 태세",
		["Defensive Stance"] = "방어 태세",
		["Berserker Stance"] = "광폭 태세",
		-- druid
		["Bear Form"] = "곰 변신",
		["Dire Bear Form"] = "광포한 곰 변신",
		["Cat Form"] = "표범 변신",
		["Tree of Life"] = "생명의 나무",
		["Moonkin Form"] = "달빛야수 변신",
		-- rogue
		["Stealth"] = "은신",
	}
else
	if GetLocale() ~= "enUS" and GetLocale() ~= "enGB" then
		Bartender3:Print("Your Locale is not officially supported by Bartender3. Stances/Shapeshifting will not work correctly.")
		Bartender3:Print("If you want to help and add support for your Locale, please contact the author at mail@nevcairiel.net")
		Bartender3:Print("Your Locale was reported as: " .. GetLocale())
	end
	STANCES = {
		-- warrior
		["Battle Stance"] = "Battle Stance",
		["Defensive Stance"] = "Defensive Stance",
		["Berserker Stance"] = "Berserker Stance",
		-- druid
		["Bear Form"] = "Bear Form",
		["Dire Bear Form"] = "Dire Bear Form",
		["Cat Form"] = "Cat Form",
		["Tree of Life"] = "Tree of Life",
		["Moonkin Form"] = "Moonkin Form",
		-- rogue
		["Stealth"] = "Stealth",
	}
end

-- stances, indexd by their actual name
Bartender3.DefaultStanceMap = { 
	["WARRIOR"] = {
		{
			short = "battle",
			name = STANCES["Battle Stance"],
			defpage = 7,
		},
		{
			short = "def",
			name = STANCES["Defensive Stance"],
			defpage = 8,
		},
		{
			short = "berserker",
			name = STANCES["Berserker Stance"],
			defpage = 9,
		},
	},
	["DRUID"] = {
		{
			short = "bear",
			name = STANCES["Bear Form"],
			defpage = 9,
		},
		{
			short = "bear",
			name = STANCES["Dire Bear Form"],
			defpage = 9,
		},
		{
			short = "cat",
			name = STANCES["Cat Form"],
			defpage = 7,
		},
		{
			short = "moonkin",
			name = STANCES["Moonkin Form"],
		},
		{
			short = "treeoflife",
			name = STANCES["Tree of Life"],
		},
		{
			name = L["Cat Form (Prowl)"],
			short = "prowl",
			defpage = 8,
			dummy = true,
			special = true,
			depend = "cat",
		},
	},
	["ROGUE"] = {
		{
			short = "stealth",
			name = STANCES["Stealth"],
			defpage = 7,
		},
	},
	["PRIEST"] = {
		{
			name = L["Shadowform"],
			short = "shadowform",
			dummy = true,
		},
	}
}

local function tfind(haystack, needle, searchfunc)
	for i,v in pairs(haystack) do
		if (searchfunc and searchfunc(v, needle) or (v == needle)) then return i end
	end
	return nil
end

function Bartender3:CreateStanceMap()
	local class = Bartender3.playerclass
	local stanceinfo = Bartender3.DefaultStanceMap[class]
	if not stanceinfo then return end
	
	self.stancemap = {}
	for i = 1, GetNumShapeshiftForms() do
		local tex, name, isActive = GetShapeshiftFormInfo(i)
		local index = tfind(stanceinfo, name, function(h, n) return h.name == n end)
		if index then
			local v = stanceinfo[index]
			tinsert(self.stancemap, {
				["short"] = v.short,
				["index"] = i,
				["name"] = name,
				["defpage"] = v.defpage,
				["special"] = v.special,
			})
		end
	end
	
	-- look for  dummys and add those
	for i,v in pairs(stanceinfo) do
		if v.dummy and (not v.depend or tfind(self.stancemap, v.depend, function(h, n) return h.short == n end)) then
			tinsert(self.stancemap, {
				["short"] = v.short,
				["index"] = i,
				["name"] = v.name,
				["defpage"] = v.defpage,
				["dummy"] = true,
				["special"] = v.special,
			})
		end
	end
end

function Bartender3.Class.ActionBar.prototype:InitStates()
	if not Bartender3.stancemap then Bartender3:CreateStanceMap() end
	local class, frame = Bartender3.playerclass, self.frame
	local stanceinfo = Bartender3.stancemap
	
	-- setup states
	if self.config.States == nil then self.config.States = {} end
	
	-- Setup Stances
	if stanceinfo ~= nil then
		-- stance config is nil and we are bar1, setup default stances.
		if self.config.Stances == nil then self.config.Stances = {} end
		for _,v in pairs(Bartender3.DefaultStanceMap[class]) do 
		-- set default stance page if we are on the mainbar
			if self.mainbar and not v.hidden and self.config.Stances[v.short] == nil and v.defpage then
				self.config.Stances[v.short] = v.defpage
			end
		end
		
		local form = tfind(stanceinfo, GetShapeshiftForm(true), function(a,b) return a.index == b end)
		
		if form and self.config.StatesEnabled then
			local page
			if class == "DRUID" and stanceinfo[form].short == "cat" and IsStealthed() then 
				page = self.config.Stances["prowl"]
			else
				page = self.config.Stances[stanceinfo[form].short]
			end
			frame:SetAttribute("state", page or 0)
		end
	end
	
	if Bartender3.stancemap and self.optionstable then
		local options = self.optionstable
		for i,v in pairs(Bartender3.stancemap) do
			if not options.args.paging.args.stance.args[v.short] then
				options.args.paging.args.stance.args[v.short] = {
					order = i,
					name = v.name,
					type = "range",
					desc = string.format(L["Configure which bar to switch to in %s."], v.name),
					min = 0, max = 10, step = 1,
					get = function() return Bartender3:GetConfig(self.id).Stances[v.short] or 0 end,
					set = function(b) Bartender3:GetBarObject(self.id):SetStancePage(v.short, b) end,
				}
			end
		end
	end
	
	self:UpdateStates()
end

function Bartender3.Class.ActionBar.prototype:UpdateStates()
	if not Bartender3.stancemap then Bartender3:CreateStanceMap() end
	local frame = self.frame
	
	-- init the statebutton with our 10 bars
	for i=0,10 do
		self:AddButtonStates(i, i)
	end
	
	local tmptrans, transition = {}, ""
	if self.mainbar and self.config.StatesEnabled then
		for i=0,10 do
			table.insert(tmptrans, format("%s-%s:%s;",2 * 10000 + i * 100, 2 * 10000 + i * 100 + 10, i))
		end
		transition = table.concat(tmptrans, "")
		frame:SetAttribute("statemap-actionbar-1", transition)
		for i=2,6 do
			frame:SetAttribute("statemap-actionbar-"..i, self:CreateManualTransitionMap(i))
		end
		for k in pairs(tmptrans) do
			tmptrans[k] = nil
		end
		
		local oldState = frame:GetAttribute("state-actionbar")
		local curState = GetActionBarPage()
		if oldState ~= curState then
			frame:SetAttribute("state-actionbar", curState)
		end
	elseif self.mainbar then
		frame:SetAttribute("state-actionbar", 1)
		for i=1,6 do
			frame:SetAttribute("statemap-actionbar-"..i, nil)
		end
	end
	
	self:RefreshStatemap()
end

function Bartender3.Class.ActionBar.prototype:GetStanceState(stance)
	local state
	if type(stance) == "table" then 
		state = self.config.Stances[stance.short]
	else
		state = self.config.Stances[stance]
	end
	if state == 0 or not self.config.StatesEnabled then state = nil end
	return state
end

function Bartender3.Class.ActionBar.prototype:RefreshStatemap()
	local class, frame = Bartender3.playerclass, self.frame
	local states = self.config.StatesEnabled
	
	-- setup statemaps for modifiers
	for _,v in pairs(Bartender3.statemodifiers) do
		local page = self.config.States[v]
		if page == 0 then page = nil end
		if not states or not page then
			frame:SetAttribute("statemap-"..v.."-0", nil)
			frame:SetAttribute("statemap-"..v.."-1", nil)
		else
			local trans1, trans2 = self:CreateModifierTransitionMap(page)
			frame:SetAttribute("statemap-"..v.."-0", trans2)
			frame:SetAttribute("statemap-"..v.."-1", trans1)
		end
	end
	
	-- HUGE class based block for stances -- it looks somewhat weird, try to optimize without breaking sometime
	-- optimized now - somewhat at least
	if Bartender3.stancemap ~= nil then
		frame:SetAttribute("statemap-stance-0", self.mainbar and self:CreateStanceTransitionMap(0) or "0")
		for i,v in pairs(Bartender3.stancemap) do
			if not v.special then
				local map = self:GetStanceState(v)
				if self.mainbar then
					map = self:CreateStanceTransitionMap(map)
				end
				frame:SetAttribute("statemap-stance-"..v.index, map)
			end
		end
		if class == "DRUID" then
			-- stealthing options
			if self:GetStanceState("cat") and self:GetStanceState("prowl") then
				local map1, map2
				if self.mainbar then
					map1 = self:CreateStanceTransitionMap(self:GetStanceState("cat"), self:GetStanceState("prowl"))
					map2 = self:CreateStanceTransitionMap(self:GetStanceState("prowl"), self:GetStanceState("cat"))
				else
					map1 = self:GetStanceState("prowl")..":"..self:GetStanceState("cat")
					map2 = self:GetStanceState("cat")..":"..self:GetStanceState("prowl")
				end
				frame:SetAttribute("statemap-stealth-0", map1)
				frame:SetAttribute("statemap-stealth-1", map2)
			else
				frame:SetAttribute("statemap-stealth-0", nil)
				frame:SetAttribute("statemap-stealth-1", nil)
			end
		end
	end
	self:ApplyStateButton()
end

function Bartender3.Class.ActionBar.prototype:CreateModifierTransitionMap(target)
	local prefix = 1
	local manprefix = 2
	-- maps[1] is for entering the target -- save the state in the new state
	-- maps[2] is for going back to where we came -- recall state
	local map1, map2 = {}, {}
	for i=0,10 do
		local id = prefix * 10000 + i * 100 + target
		self:AddButtonStates(id, target)
		table.insert(map1, format("%s:%s;", i, id))
		if self.mainbar then table.insert(map1, format("%s-%s:%s;", manprefix * 10000 + i * 100, manprefix * 10000 + i * 100 + 10, id)) end
		table.insert(map2, format("%s-%s:%s;", id - target, id - target + 10, i))
	end
	return table.concat(map1, ""), table.concat(map2, "")
end

function Bartender3.Class.ActionBar.prototype:CreateStanceTransitionMap(stance, source)
	if not stance then return nil end
	local prefix = 2
	-- maps[1] is for entering the target -- save the state in the new state
	-- maps[2] is for going back to where we came -- recall state
	local map = {}
	if source then
		for i=2,10 do
			local id = prefix * 10000 + source * 100 + i
			local id2 = prefix * 10000 + stance * 100 + i
			--self:AddButtonStates(id, i)
			table.insert(map, format("%s:%s;", id, id2))
		end
		table.insert(map, format("%s:%s", source, stance))
	else
		for i=2,10 do
			local id2 = prefix * 10000 + stance * 100 + i
			for k=0,10 do
				local id = prefix * 10000 + k * 100 + i
				--self:AddButtonStates(id, i)
				table.insert(map, format("%s:%s;", id, id2))
			end
		end
		table.insert(map, format("*:%s", stance))
	end
	return table.concat(map, "")
end

function Bartender3.Class.ActionBar.prototype:CreateManualTransitionMap(target)
	local prefix = 2
	-- maps[1] is for entering the target -- save the state in the new state
	-- maps[2] is for going back to where we came -- recall state
	local map = {}
	for i=0,10 do
		local id = prefix * 10000 + i * 100 + target
		self:AddButtonStates(id, target)
		table.insert(map, format("%s:%s;", i, id))
		table.insert(map, format("%s-%s:%s;", id - target, id - target + 10, id))
	end
	return table.concat(map, "")
end

function Bartender3.Class.ActionBar.prototype:AddButtonStates(state, page)
	for _,v in pairs(self.buttonobjects) do
		local action = (page == 0) and v.id or (v.rid + ((page - 1) * 12))
		v:SetStateAction(state, action)
	end
	self:AddRightClickState(state)
	self:AddToStateButton(state)
end

function Bartender3.Class.ActionBar.prototype:AddToStateButton(state)
	if not self.statebutton then self.statebutton = {} end
	state = tonumber(state)
	if not tfind(self.statebutton, state) then 
		table.insert(self.statebutton, state)
	end
end

function Bartender3.Class.ActionBar.prototype:ApplyStateButton()
	local states1, states2 = {}, {}
	for _,v in pairs(self.statebutton) do
		table.insert(states1, format("%s:S%s1;", v, v))
		table.insert(states2, format("%s:S%s2;", v, v))
	end
	self.frame:SetAttribute("statebutton", table.concat(states1, ""))
	self.frame:SetAttribute("statebutton2", table.concat(states2, ""))
	--self.statebutton = nil
end
