-- Much of this code is copied from .../AceGUI-3.0/widgets/AceGUIWidget-Dropdown.lua
-- This Dropdown-Pullout widget is modified to fit TSM's theme / needs
local TSM = select(2, ...)
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
local Type, Version = "TSMDropdown-Pullout", 2
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end

-- Lua APIs
local min, max, floor = math.min, math.max, math.floor
local select, pairs, ipairs, type = select, pairs, ipairs, type
local tsort = table.sort

-- WoW APIs
local PlaySound = PlaySound
local UIParent, CreateFrame = UIParent, CreateFrame
local _G = _G


--[[-----------------------------------------------------------------------------
Globals
-------------------------------------------------------------------------------]]

local sliderBackdrop  = {
	bgFile = "Interface\\Buttons\\UI-SliderBar-Background",
	edgeFile = "Interface\\Buttons\\UI-SliderBar-Border",
	tile = true, tileSize = 8, edgeSize = 8,
	insets = { left = 3, right = 3, top = 3, bottom = 3 }
}

local DEFAULT_WIDTH = 200
local DEFAULT_MAX_HEIGHT = 600


--[[-----------------------------------------------------------------------------
Support functions
-------------------------------------------------------------------------------]]

local function fixstrata(strata, parent, ...)
	local i = 1
	local child = select(i, ...)
	parent:SetFrameStrata(strata)
	while child do
		fixstrata(strata, child, child:GetChildren())
		i = i + 1
		child = select(i, ...)
	end
end


--[[-----------------------------------------------------------------------------
Scripts
-------------------------------------------------------------------------------]]

local function OnEnter(item)
	local self = item.pullout
	for k, v in ipairs(self.items) do
		if v.CloseMenu and v ~= item then
			v:CloseMenu()
		end
	end
end

-- See the note in Constructor() for each scroll related function
local function OnMouseWheel(this, value)
	this.obj:MoveScroll(value)
end

local function OnScrollValueChanged(this, value)
	this.obj:SetScroll(value)
end

local function OnSizeChanged(this)
	this.obj:FixScroll()
end


--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]

local methods = {
	["OnAcquire"] = function(self)
		self.frame:SetParent(UIParent)
	end,
	
	["OnRelease"] = function(self)
		self:Clear()
		self.frame:ClearAllPoints()
		self.frame:Hide()
	end,

	["SetScroll"] = function(self, value)
		local status = self.scrollStatus
		local frame, child = self.scrollFrame, self.itemFrame
		local height, viewheight = frame:GetHeight(), child:GetHeight()

		local offset
		if height > viewheight then
			offset = 0
		else
			offset = floor((viewheight - height) / 1000 * value)
		end
		child:ClearAllPoints()
		child:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, offset)
		child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", self.slider:IsShown() and -12 or 0, offset)
		status.offset = offset
		status.scrollvalue = value		
	end,

	["MoveScroll"] = function(self, value)
		local status = self.scrollStatus
		local frame, child = self.scrollFrame, self.itemFrame
		local height, viewheight = frame:GetHeight(), child:GetHeight()

		if height > viewheight then
			self.slider:Hide()
		else
			self.slider:Show()
			local diff = height - viewheight
			local delta = 1
			if value < 0 then
				delta = -1
			end
			self.slider:SetValue(min(max(status.scrollvalue + delta*(1000/(diff/45)),0), 1000))
		end
	end,

	["FixScroll"] = function(self)
		local status = self.scrollStatus
		local frame, child = self.scrollFrame, self.itemFrame
		local height, viewheight = frame:GetHeight(), child:GetHeight()
		local offset = status.offset or 0

		if viewheight < height then
			self.slider:Hide()
			child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, offset)
			self.slider:SetValue(0)
		else
			self.slider:Show()			
			local value = (offset / (viewheight - height) * 1000)
			if value > 1000 then value = 1000 end
			self.slider:SetValue(value)
			self:SetScroll(value)
			if value < 1000 then
				child:ClearAllPoints()
				child:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, offset)
				child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -12, offset)
				status.offset = offset
			end
		end
	end,

	["AddItem"] = function(self, item)
		self.items[#self.items + 1] = item
		
		local h = #self.items * 16
		self.itemFrame:SetHeight(h)
		self.frame:SetHeight(min(h + 20, self.maxHeight))
		
		item.frame:SetPoint("LEFT", self.itemFrame, "LEFT")
		item.frame:SetPoint("RIGHT", self.itemFrame, "RIGHT")
		
		item:SetPullout(self)
		item:SetOnEnter(OnEnter)
	end,
	
	["Open"] = function(self, point, relFrame, relPoint, x, y)		
		local items = self.items
		local frame = self.frame
		local itemFrame = self.itemFrame
		
		frame:SetPoint(point, relFrame, relPoint, x, y)

		local height = 8
		for i, item in pairs(items) do
			if i == 1 then
				item:SetPoint("TOP", itemFrame, "TOP", 0, -2)
			else
				item:SetPoint("TOP", items[i-1].frame, "BOTTOM", 0, 1)
			end
			
			item:Show()
			
			height = height + 16
		end
		itemFrame:SetHeight(height)
		fixstrata("TOOLTIP", frame, frame:GetChildren())
		frame:Show()
		self:Fire("OnOpen")
	end,

	["Close"] = function(self)
		self.frame:Hide()
		self:Fire("OnClose")
	end,

	["Clear"] = function(self)
		local items = self.items
		for i, item in pairs(items) do
			AceGUI:Release(item)
			items[i] = nil
		end
	end,

	["IterateItems"] = function(self)
		return ipairs(self.items)
	end,
	
	["GetMaxWidth"] = function(self)
		maxWidth = 0
		for i, widget in self:IterateItems() do
			itemTextWidth = widget:GetWidth()
			if (itemTextWidth > maxWidth) then
				maxWidth = itemTextWidth
			end
		end
		return maxWidth + 8
	end,

	["SetHideOnLeave"] = function(self, val)
		self.hideOnLeave = val
	end,

	["SetMaxHeight"] = function(self, height)
		self.maxHeight = height or DEFAULT_MAX_HEIGHT
		if self.frame:GetHeight() > height then
			self.frame:SetHeight(height)
		elseif (self.itemFrame:GetHeight()+20) < height then
			self.frame:SetHeight(self.itemFrame:GetHeight()+20) -- see :AddItem
		end
	end,
	
	["GetRightBorderWidth"] = function(self)
		return 6 + (self.slider:IsShown() and 12 or 0)
	end,

	["GetLeftBorderWidth"] = function(self)
		return 6
	end,
}


--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]

local function Constructor()
	local count = AceGUI:GetNextWidgetNum(Type)
	
	local frame = CreateFrame("Frame", "TSMPullout"..count, UIParent)
	TSMAPI.Design:SetContentColor(frame)
	frame:SetFrameStrata("FULLSCREEN_DIALOG")
	frame:SetClampedToScreen(true)
	frame:SetWidth(DEFAULT_WIDTH)
	frame:SetHeight(DEFAULT_MAX_HEIGHT)

	local scrollFrame = CreateFrame("ScrollFrame", nil, frame)
	local itemFrame = CreateFrame("Frame", nil, scrollFrame)
	
	local slider = CreateFrame("Slider", "TSMPulloutScrollbar"..count, scrollFrame)
	slider:SetOrientation("VERTICAL")
	slider:SetHitRectInsets(0, 0, -10, 0)
	slider:SetBackdrop(sliderBackdrop)
	slider:SetWidth(8)
	slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical")
	slider:SetFrameStrata("FULLSCREEN_DIALOG")

	scrollFrame:SetScrollChild(itemFrame)
	scrollFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", 4, -4)
	scrollFrame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -4, 4)
	scrollFrame:EnableMouseWheel(true)
	scrollFrame:SetScript("OnMouseWheel", OnMouseWheel)
	scrollFrame:SetScript("OnSizeChanged", OnSizeChanged)
	scrollFrame:SetToplevel(true)
	scrollFrame:SetFrameStrata("FULLSCREEN_DIALOG")
	
	itemFrame:SetPoint("TOPLEFT", scrollFrame, "TOPLEFT", 0, 0)
	itemFrame:SetPoint("TOPRIGHT", scrollFrame, "TOPRIGHT", -12, 0)
	itemFrame:SetHeight(400)
	itemFrame:SetToplevel(true)
	itemFrame:SetFrameStrata("FULLSCREEN_DIALOG")
	
	slider:SetPoint("TOPLEFT", scrollFrame, "TOPRIGHT", -16, 0)
	slider:SetPoint("BOTTOMLEFT", scrollFrame, "BOTTOMRIGHT", -16, 0)
	
	scrollFrame:Show()
	itemFrame:Show()
	slider:Hide()
	
	local widget = {
		frame = frame,
		slider = slider,
		scrollFrame = scrollFrame,
		itemFrame = itemFrame,
		maxHeight = DEFAULT_MAX_HEIGHT,
		scrollStatus = {scrollvalue=0},
		count = count,
		items = {},
		type = Type,
	}
	for method, func in pairs(methods) do
		widget[method] = func
	end
	frame.obj = widget
	scrollFrame.obj = widget
	itemFrame.obj = widget
	slider.obj = widget
	
	slider:SetScript("OnValueChanged", OnScrollValueChanged)
	slider:SetMinMaxValues(0, 1000)
	slider:SetValueStep(1)
	slider:SetValue(0)
	widget:FixScroll()
	
	return AceGUI:RegisterAsWidget(widget)
end

AceGUI:RegisterWidgetType(Type, Constructor, Version)