local M = LibStub("AceAddon-3.0"):NewAddon("MBB","AceEvent-3.0","AceTimer-3.0","AceHook-3.0")
if not M then return end

local L = LibStub("AceLocale-3.0"):GetLocale("MBB")
local LDB = LibStub("LibDataBroker-1.1", true)

local db

local panelBottomFrames = {}

local configDialog = LibStub("AceConfigDialog-3.0")

local ignored = {
	["MiniMapTrackingFrame"]=true,
	["MiniMapMeetingStoneFrame"]=true,
	["MiniMapMailFrame"]=true,
	["MiniMapPing"]=true,
	["MinimapBackdrop"]=true,
	["MinimapZoomIn"]=true,
	["MinimapZoomOut"]=true,
	["BookOfTracksFrame"]=true,
	["GatherNote"]=true,
	["FishingExtravaganzaMini"]=true,
	["MiniNotePOI"]=true,
	["RecipeRadarMinimapIcon"]=true,
	["FWGMinimapPOI"]=true,
	["CartographerNotesPOI"]=true,
	["GFW_TrackMenuFrame"]=true,
	["GFW_TrackMenuButton"]=true,
	["TDial_TrackingIcon"]=true,
	["TDial_TrackButton"]=true,
	["MiniMapTracking"]=true,
	["BFGPSButton"]=true,
	["TimeManagerClockButton"]=true,	
	['MiniMapBattlefieldFrame'] = true,
	["GatherMate"] = true,
	["MiniMapLFGFrame"] = true,
	
}

local options = {
	profile = {
		enabled = true,
		enablemousetip = true,
		enablemouseover = false,
		itemperline = 4,		
		excludes = {
			['BigFootMinimapButton'] = true,
			['GameTimeFrame'] = true,
			["MiniMapVoiceChatFrame"] = true,
			["MiniMapWorldMapButton"] = true
		
		},
		
		MiniMap =
		{
			hide = false,
			minimapPos = 192,
			radius = 80,		
		}
	},
}

local function Obj(button)
	local obj
	if type(button)=='string' then 
		obj = _G[button]
	elseif type(button)=='table' then
		obj = button
	end
	return obj
end

local function Name(button)
	local name
	if type(button)=='string' then 
		name = button
	elseif type(button)=='table' then
		name = button:GetName()
	end
	return name
end


local launcher

function IsParent(parent, child)
	if not child then return false end
	if parent == child then return true end
	if child == UIParent then return false end
	return IsParent(parent,child:GetParent())
end

local function IsButtonExcluded(button)
	local name = Name(button)
	local excluded = db.excludes
	if not excluded then return end
	return excluded[name]	
end

local function IsButtonIgnored(button)
	if Name(button)==M.realIcon:GetName() then return true end
	if  ignored[Name(button)] then return true end
	for ignoreName in pairs(ignored) do
		if Name(button):find(ignoreName) then return true end
	end
	return false
end

local function addButton(frame)

	local child = Obj(frame)
	if not child then return end
	if child.added then return end
	child.opoint = {child:GetPoint()};
	if( not child.opoint[1] ) then
		child.opoint = {"TOP", Minimap, "BOTTOM", 0, 0};
	end	
	child.osize = child.osize or {child:GetHeight(),child:GetWidth()};
	child.oisshown = child:IsShown()
	
	M:RawHook(child,"ClearAllPoints",function() end,true)
	M:RawHook(child,"SetPoint",function() end,true)
	child:SetAlpha(1)
	M:RawHook(child,"SetAlpha",function(a,b)end,true)
	M.hooks[child].Hide(child)
	child.added  = true
	M.buttons[child] = true	
	if not child.oldParent  then
		child.oldParent = child:GetParent()
		child.oldFrameLevel = child:GetFrameLevel()
	end
	child:SetParent(M.panel)
end

local function removeButton(frame)
	local child = Obj(frame)
	if not child then return end	
	M:Unhook(child,"ClearAllPoints")
	M:Unhook(child,"SetPoint")
	M:Unhook(child,"SetAlpha")
	child:ClearAllPoints()
	child:SetPoint(unpack(child.opoint))
	child:SetHeight(child.osize[1])
	child:SetWidth(child.osize[2])
	if child.oisshown then
		M.hooks[child].Show(child)
	else
		M.hooks[child].Hide(child)
	end
	child.added = false
	M.buttons[child] = false
	if child.oldParent then
		child:SetParent(child.oldParent)
		child:SetFrameLevel(child.oldFrameLevel)
	end
end

local function doShowButtons()
	M:CancelTimer(M.hideTimer, true)
	M.hideTimer = nil
	M:CancelTimer(M.hideModTimer, true)
	M:showButtons()
end

local function doHideButtons()
	if not M.hideTimer then
		M.hideTimer = M:ScheduleTimer("hideButtons", 1,true)
	end
end

local function prepareModButton(child)
	local button;
	_G.MBB_ButtonAdd:Hide()
	_G.MBB_ButtonRemove:Hide()
	if IsButtonExcluded(child) then
		button = _G.MBB_ButtonAdd
		button:SetScript("OnClick",function(frame) 
			addButton(frame.operator) 
			db.excludes[Name(frame.operator)] = false
			frame:Hide() 
		end)
		button:SetScript("OnLeave",function(frame) frame:Hide() end)
		
	else
		button = _G.MBB_ButtonRemove
		button:SetScript("OnEnter",function(frame) 
			M:CancelTimer(M.hideModTimer, true) 
		end)
		button:SetScript("OnClick",function(frame) 
			removeButton(frame.operator)
			db.excludes[Name(frame.operator)] = true
			M:showButtons() 
			frame:Hide() 
		end)
		button:SetScript("OnLeave",function(frame) 
			frame:Hide() 
		end)
	end
	button.operator = child
	child.operant = button
	button:ClearAllPoints()
	button:SetParent(child)
	button:SetPoint("LEFT", child, "RIGHT", 0, 0);
	return button
end

function M:hideButtons(fade)	
	self:CancelTimer(self.hideTimer, true)
	self.hideTimer = nil
	if fade and self.showed then
		local fadeInfo = {}
		fadeInfo.mode = "OUT";
		fadeInfo.timeToFade = 0.4;
		fadeInfo.startAlpha = 0.9;
		fadeInfo.endAlpha = 0;
		fadeInfo.finishedFunc = function ()  self.panel:Hide() end
		UIFrameFade(self.panel, fadeInfo);
	else
		self.panel:Hide()
	end
	self.showed  = false

--	for button,flag in pairs(M.buttons) do
--		if flag then
--			M.hooks[button].Hide(button)
--		end
--	end
end

function M:showButtons()
	self.showed  = true
	local lastButton
	local itemperline = db.itemperline
	if itemperline ==0 then itemperline = 100 end
	local currentIndex = 0
	local x,y 

	for button,flag in pairs(self.buttons) do
		if flag and button.oisshown then
			self.hooks[button].ClearAllPoints(button)
			x ,y = currentIndex%itemperline, floor(currentIndex/itemperline)
				self.hooks[button].SetPoint(button,"TOPLEFT",self.panel,"TOPLEFT",(32+x*57),(-63-y*40))
				
			if db.keepsize then
				button:SetWidth(button.osize[2])
				button:SetHeight(button.osize[1])
			else
				button:SetWidth(32)
				button:SetHeight(32)
			end
			self.hooks[button].Show(button)
			lastButton = button;
			currentIndex = currentIndex + 1
		end
	end
	local anchorPanel = _G["MBBFrameBottomPanel"]		
	for i,frame in ipairs(panelBottomFrames) do
		x ,y = (i-1)%3, floor((i-1)/3)
		if frame then
			frame:ClearAllPoints()
			frame:SetPoint("TOPLEFT",anchorPanel,"TOPLEFT",(9 + x*80),(-5-y*24))
			frame:Show()
		end
	end
	local lines = ceil(#panelBottomFrames/3)
	anchorPanel:SetHeight(max(lines*24+2,26))
	
	lines = ceil(currentIndex/itemperline)
	local middleHeight = (max(lines*40+16,64))
	if self.panel then 
		self.panel:ClearAllPoints()
		self.panel:SetPoint("TOPRIGHT",Minimap,"BOTTOMLEFT",-34,80)
		self.panel:SetHeight(anchorPanel:GetHeight() + middleHeight + 60)
		self.panel:SetAlpha(0.9)
		self.panel:Show()
		
	end
end

function M:showModButton(child)
	local button = prepareModButton(child)
	
	button:Show();
	
end

function M:hideModButton(child)
	if child.operant then
		child.operant:Hide()
	end
end

local function toggleButtons()
	M:CancelTimer(M.hideTimer, true)
	M.hideTimer = nil
	M:CancelTimer(M.hideModTimer, true)
	if M.showed then
		M:hideButtons()	
	else
		M:showButtons()
	end
	 
end

local function createIcon()
	launcher = LDB:NewDataObject("MBB", {
		type = "launcher",
		icon = [[Interface\AddOns\BigFoot\Mods\MBB\res\icon]],
		OnClick = function(frame,button)
			if button =="RightButton" then
				BToggleDropDownMenu(1, nil, BigFootMiniButtonDropDown, frame:GetName(), 10, 3);
				PlaySound("UChatScrollButton");
			elseif button=="LeftButton" then
				toggleButtons()
			end
		end,
		OnEnter = function(tt)
			BigFoot_ShowNewbieTooltip(L["BigFoot Mini map"],L["Left click to open mini panel"],L["Right click to show dropdown menu"])
		end,		
		OnLeave = function()
			 BigFoot_HideNewbieTooltip()
			doHideButtons()
		end
	})
end

--old child script processing
local function onChildShow(button)
	button.oisshown = true
	--M.hooks[button].Show(button)
end

local function onChildHide(button)
	button.oisshown = false

	--M.hooks[button].Hide(button)
end

function M:onChildClick(frame,button)
end

function M:onChildMouseDown(frame,button)
end

function M:onChildMouseUp(frame,button)
end

function M:onChildLeave(frame)
	M.hideModTimer = M:ScheduleTimer("hideModButton",1,frame)
end

function M:onChildEnter(frame)
	M:CancelTimer(M.hideModTimer, true)
	if( IsControlKeyDown() ) then
		self:showModButton(frame)
	end
end

local function safeHook(obj,methodname,scriptname)
	if not obj:HasScript(methodname) then
		obj:SetScript(methodname,function() end)
	end
	if not M:IsHooked(obj,methodname) then
		M:HookScript(obj,methodname,scriptname)
	end
end
	
local function preparePanel(panel)
	
	panel:SetScript("OnUpdate",function(self,elapsed)
		local frame
		self.time = self.time or 0
		self.time = self.time + elapsed
		if self.time >= 0.3 then
			if self.inBound then return end
			if not M.showed then return end
			frame = GetMouseFocus()
			if frame and (IsParent(self,frame) or M.buttons[frame] or frame==M.realIcon) then 
				doShowButtons()
			else
				doHideButtons()
			end
		end
		self.time = self.time%0.3
	end)
	panel:SetScript("OnEnter",function(self)
		self.inBound = true
		if not M.showed then return end
		doShowButtons()
	end)
	panel:SetScript("OnLeave",function(self)
		self.inBound = false
	end)
end

local function prepareButton(name)
	local buttonframe = Obj(name)
	
	if not M:IsHooked(buttonframe,"Show") then M:RawHook(buttonframe,"Show",onChildShow,true) end
	if not M:IsHooked (buttonframe,"Hide") then M:RawHook(buttonframe,"Hide",onChildHide,true) end

	
	safeHook(buttonframe,"OnClick","onChildClick")
	safeHook(buttonframe,"OnMouseDown","onChildMouseDown")
	safeHook(buttonframe,"OnMouseUp","onChildMouseUp")
	safeHook(buttonframe,"OnLeave","onChildLeave")
	safeHook(buttonframe,"OnEnter","onChildEnter")

	
	--prepare button scripts here
end

local function scanMiniChildren()
	local children = {Minimap:GetChildren()};
	local additional = {MinimapBackdrop:GetChildren()};
	for _,child in ipairs(additional) do
		table.insert(children, child);
	end
	for _, child in ipairs(children) do		
		if( not child:HasScript("OnClick") ) then
			for _,subchild in ipairs({child:GetChildren()}) do
				if( subchild:HasScript("OnClick") ) then
					child = subchild;
					child.hasParentFrame = true;
					break;
				end
			end
		end
		if Name(child) and not IsButtonIgnored(child) then
			if child:IsObjectType("Button")  then
				prepareButton(child)
				if not IsButtonExcluded(child) then
					addButton(child)
				end
			end
		end

	end
end

function M:getDefaults()
	return options
end

function M:Refresh()
	if db.enabled then
		self:Enable()
	else
		self:Disable()
		self.icon:Hide("MBB")
	end
end

function M:OnInitialize()
	M.buttons = {}
	self.db = LibStub("AceDB-3.0"):New("MBBDB", self:getDefaults())
	db = self.db.profile

	local AceConfig = LibStub("AceConfig-3.0")
	
	createIcon()
	self.icon =self.icon or LDB and LibStub("LibDBIcon-1.0", true)
	self.icon:Register("MBB", launcher, self.db.profile.MiniMap)
	self:SetupOptions()
end

function M:OnEnable()
	self.icon:Show("MBB")
	self.panel = _G["MBBFrame"]
	M.realIcon = _G[M.icon.objects["MBB"]:GetName()]
	scanMiniChildren()
	self:ScheduleRepeatingTimer(scanMiniChildren,2)
	preparePanel(M.panel)
end

function M:OnDisable()
	self:CancelAllTimers()
	for button, flag in pairs(M.buttons) do 
		if flag then
			removeButton(button)
		end
	end
	self.icon:Hide("MBB")
end

--------------------------
-- TODO: Add panel elements


--panel related
function M:AddFrame(frame,...)
	frame:SetParent(self.panel)
	frame:SetPoint(...)
end

function M:AddBottomButton(frame)
	tinsert(panelBottomFrames,frame)
	frame:SetParent(_G[M.panel:GetName().."BottomPanel"])
end



