---------------------------------------------------------------------------------------------------
-- Addon Manager
---------------------------------------------------------------------------------------------------
local tinsert, tremove, unpack, tgetn, tisempty = table.insert, table.remove, unpack, table.getn, table.isempty
local tonumber, toboolean = tonumber, toboolean
local sfind, ssub = string.find, string.sub

local SendEvent = userMods.SendEvent
local GetStateAddons, LoadAddon, UnloadAddon = common.GetStateManagedAddons, common.StateLoadManagedAddon, common.StateUnloadManagedAddon
local GetConfig, SetConfig = userMods.GetAvatarConfigSection, userMods.SetAvatarConfigSection
local RegisterEventHandler, UnRegisterEventHandler = common.RegisterEventHandler, common.UnRegisterEventHandler
local ToWS, W = ToWS, W
local L
---------------------------------------------------------------------------------------------------
local popup, tooltip = AddonManager[ 'Popup' ], AddonManager[ 'Tooltip' ]
local addon = {	
	items = {},
	selected = {},
	sortedBy = {},
	
	isListUpdated = true,
	DND_ID_BUTTON_MAIN = 995,	--1024,
	NAME = common.GetAddonName(),
	PREFIX = 'UserAddon/'
}
local handlers = {}
local textures = { 
	[ 0 ] = common.GetAddonRelatedGroupTexture( 'ButtonSortStates', 'SortNone' ),
	[ 1 ] = common.GetAddonRelatedGroupTexture( 'ButtonSortStates', 'SortUp' ),
	[ 2 ] = common.GetAddonRelatedGroupTexture( 'ButtonSortStates', 'SortDown' )
}
local isFinished = true
---------------------------------------------------------------------------------------------------
local function HasNamePrefix( name ) local _, pos = sfind( name, addon.PREFIX ) return pos end
local function GetName( name ) return ssub( name, tonumber( HasNamePrefix( name ) ) + 1 ) end

local function SetLocale( locale )
	addon.locale = locale or GetGameLocalization()
	L = locales[ addon.locale ]
	if not L then
		L = locales.eng
		addon.locale = 'eng'
	end
	AddonManager[ 'LStrings' ] = L
	W( 'ButtonChangeLocale' ):SetVal( 'value', L[ 'Locale' ] )
	W( 'Label' ):SetVal( 'value', L[ 'Language' ] )
	W( 'Header' ):SetVal( 'value', L[ 'Addon Manager' ] )
	W( 'TabUser' ):SetVal( 'button_label', L[ 'User' ] )
	W( 'TabDefault' ):SetVal( 'button_label', L[ 'Default' ] )
	W( 'ButtonLoadSelected' ):SetVal( 'button_label', L[ 'Load Selected' ] )
	W( 'ButtonUnLoadSelected' ):SetVal( 'button_label', L[ 'Unload Selected' ] )
	W( 'ButtonLoadAll' ):SetVal( 'button_label', L[ 'Load All' ] )
	W( 'ButtonUnLoadAll' ):SetVal( 'button_label', L[ 'Unload All' ] )
	W( 'ButtonSave' ):SetVal( 'button_label', L[ 'Save' ] )
	W( 'ButtonReset' ):SetVal( 'button_label', L[ 'Reset' ] )
	for _, v in { W( 'SortPanel' ), W( 'SortPanelDefault' ) } do
		W( 'ButtonSort', W( 'SortByName', v ) ):SetVal( 'button_label', L[ 'Name' ] )
		W( 'ButtonSort', W( 'SortByDesc', v ) ):SetVal( 'button_label', L[ 'Description' ] )
	end
	SendEvent( 'SCRIPT_ADDON_MANAGER_LOCALIZATION_CHANGED', { locale = addon.locale } )
end
---------------------------------------------------------------------------------------------------
-- events
---------------------------------------------------------------------------------------------------
handlers[ 'EVENT_AVATAR_CREATED' ] = function()	
	SetLocale( addon.locale )
	W( 'TabUser' ):SetVariant( tonumber( not addon.isDefaultShowed ) )
	W( 'TabDefault' ):SetVariant( tonumber( addon.isDefaultShowed ) )
	for _, v in GetStateAddons() do addon:AddItem( v ) end
	local widget = addon.sort[ 'SortPanel' ][ 'SortByName' ]
	W( 'ButtonSort', widget ):SetVariant( 1 )
	W( 'ButtonSortState', widget ):SetBackgroundTexture( textures[ 1 ] )
	addon:SortBy( 'SortByName', true )
	addon.isListUpdated = false
	ToggleUI()
end

handlers[ 'SCRIPT_ADDON_INFO_RESPONSE' ] = function( event )
	local sender = addon.PREFIX .. event.sender
	local item = addon.items[ sender ]	
	if item then
		event = item.state and event or { addonsBlocked = event.addonsBlocked }
		item.description = event.desc
		W( 'Description', item.w ):SetVal( 'value', item.description and ToWS( item.description ) or L[ 'Empty' ] )
		if event.showDNDButton then
			item.isLocked = toboolean( addon.config and addon.config[ sender ] and addon.config[ sender ].isLocked )
			W( 'ButtonToggleDND', item.w ):SetVariant( tonumber( item.isLocked ) )
			SendEvent( 'SCRIPT_TOGGLE_DND', { target = event.sender, state = not item.isLocked } )
		end
		W( 'ButtonToggleDND', item.w ):Show( toboolean( event.showDNDButton ) )
		W( 'ButtonShowHide', item.w ):Show( toboolean( event.showHideButton ) )
		W( 'ButtonSettings', item.w ):Show( toboolean( event.showSettingsButton ) )
		item.addonsBlocked = event.addonsBlocked or item.addonsBlocked
		--
		if item.state == false and item.addonsBlocked then
			SendEvent( 'SCRIPT_ADDONS_BLOCKED_RECEIVED', { name = sender } )
		end
		--
	end
end

handlers[ 'SCRIPT_ADDONS_BLOCKED_RECEIVED' ] = function( params )
	for _, addonName in addon.items[ params.name ].addonsBlocked do
		local config = addon.config[ addonName ]
		if not config or ( config and config.state ~= false ) then LoadAddon( addonName ) end
	end
end

handlers[ 'SCRIPT_ADDON_MEM_REQUEST' ] = function( params )
	if params.target ~= common.GetAddonName() then return end
	userMods.SendEvent( 'SCRIPT_ADDON_MEM_RESPONSE', { sender = params.target, memUsage = gcinfo() } )
end

handlers[ 'SCRIPT_TOGGLE_UI' ] = function( event ) mainForm:Show( event.visible ) end
handlers[ 'SCRIPT_TOGGLE_DND' ] = function( event ) if event.target == addon.NAME then DnD:Enable( W( 'ButtonMain' ), event.state ) end end
handlers[ 'SCRIPT_ADDON_INFO_REQUEST' ] = function( event ) if event.target == addon.NAME then SendEvent( 'SCRIPT_ADDON_INFO_RESPONSE', { sender = event.target, showDNDButton = true } ) end end
handlers[ 'SCRIPT_GET_ADDON_MANAGER_LOCALIZATION' ] = function() SendEvent( 'SCRIPT_ADDON_MANAGER_LOCALIZATION_CHANGED', { locale = addon.locale } ) end
---------------------------------------------------------------------------------------------------
handlers[ 'AOPANEL_START' ] = function()
	local SetVal = { val = ToWS( 'AM' ) }
	local params = { header = SetVal, ptype = 'button', size = 48 } 
	SendEvent( 'AOPANEL_SEND_ADDON', { name = 'Addon Manager', sysName = addon.NAME, param = params } )
	W( 'ButtonMain' ):Show( false )
end

handlers[ 'AOPANEL_BUTTON_LEFT_CLICK' ] = function( params )
	if params.sender ~= addon.NAME then return end
	handlers[ 'mouse_left_click' ]{ sender = 'ButtonMain', widget = W( 'ButtonMain' ) }
end
---------------------------------------------------------------------------------------------------
--[[
handlers[ 'SCRIPT_ADDON_MANAGER_LOCALIZATION_CHANGED' ] = function( params )
	print( 'params.locale = ', params.locale )
end--]]
---------------------------------------------------------------------------------------------------
-- reactions
---------------------------------------------------------------------------------------------------
handlers[ 'mouse_left_click' ] = function( reaction )
	if reaction.sender == 'ButtonChangeLocale' then
		popup:Toggle()
		return
	end
	if popup.state then
		popup:Toggle()
	end
	--
	local parent = reaction.widget:GetParent()
	local parentName = parent:GetName()
	local var = reaction.widget:GetVariant() == 1
	local isSelected = tgetn( addon.selected ) > 0
	if reaction.sender == 'ButtonMain' or reaction.sender == 'ButtonCornerCross' then
		if DnD:IsDragging() then return end
		addon.ToggleMainWindow()
		return
	elseif reaction.sender == 'ButtonItem' then
		if parentName == addon.PREFIX .. addon.NAME then return end
		local arr = addon.selected
		if not var then tinsert( arr, parentName ) else for i, v in ipairs( arr ) do if v == parentName then tremove( arr, i ); break end end end
		reaction.widget:SetVariant( tonumber( not var ) )
		isSelected = tgetn( addon.selected ) > 0
		for i, v in { W( 'ButtonLoadSelected' ), W( 'ButtonUnLoadSelected' ), W( 'ButtonReset' ) } do v:Enable( isSelected ) end
		return
	elseif reaction.sender == 'TabUser' or reaction.sender == 'TabDefault' then
		var = reaction.sender == 'TabDefault'
		W( 'TabDefault' ):SetVariant( tonumber( var ) ); W( 'TabUser' ):SetVariant( tonumber( not var ) )
		W( 'SortPanel' ):Show( not var ); W( 'SortPanelDefault' ):Show( var )
		W( 'ButtonLoadAll' ):Show( not var ); W( 'ButtonUnLoadAll' ):Show( not var )
		addon.isDefaultShowed = var
		var = var and 'SortPanelDefault' or 'SortPanel'
		local sortedBy = addon.sortedBy[ var ] or { 'SortByName', 1 }
		parent = addon.sort[ var ][ sortedBy[ 1 ] ]
		W( 'ButtonSort', parent ):SetVariant( sortedBy[ 2 ] )
		W( 'ButtonSortState', parent ):SetBackgroundTexture( textures[ sortedBy[ 2 ] ] )
		addon:SortBy( sortedBy[ 1 ], sortedBy[ 2 ] == 1 )
		return
	elseif sfind( parentName, 'SortBy' ) then
		local widget = W( 'ButtonSortState', parent )
		local variant = ( { [ 0 ] = 1, [ 1 ] = 2, [ 2 ] = 1 } )[ reaction.widget:GetVariant() ]
		local grandpaName = parent:GetParent():GetName()
		for k, v in addon.sort[ grandpaName ] do
			W( 'ButtonSort', v ):SetVariant( 0 )
			W( 'ButtonSortState', v ):SetBackgroundTexture( textures[ 0 ] )
		end
		reaction.widget:SetVariant( variant )
		widget:SetBackgroundTexture( textures[ variant ] )
		addon.sortedBy[ grandpaName ] = { parentName, variant }
		addon:SortBy( parentName, not var )
		return
	elseif reaction.sender == 'ButtonLoadAll' then
		for k, v in pairs( addon.items ) do if HasNamePrefix( k ) then addon:ToggleAddonState( k, true ) end end
	elseif reaction.sender == 'ButtonUnLoadAll' then
		for k, v in pairs( addon.items ) do if HasNamePrefix( k ) and k ~= addon.PREFIX .. addon.NAME then addon:ToggleAddonState( k ) end end		
	elseif reaction.sender == 'ButtonToggleDND' then
		SendEvent( 'SCRIPT_TOGGLE_DND', { target = GetName( parentName ), state = var } )
		addon.items[ parentName ].isLocked = not var
		reaction.widget:SetVariant( tonumber( not var ) )
		return
	elseif reaction.sender == 'ButtonShowHide' then
		SendEvent( 'SCRIPT_TOGGLE_VISIBILITY', { target = GetName( parentName ), state = var } )
		addon.items[ parentName ].isVisible = var
		reaction.widget:SetVariant( tonumber( not var ) )
		return
	elseif reaction.sender == 'ButtonSettings' then
		SendEvent( 'SCRIPT_SHOW_SETTINGS', { target = GetName( parentName ) } )
		return
	elseif reaction.sender == 'ButtonSave' then
		addon:SaveConfig()
		return
	elseif reaction.sender == 'ButtonReset' then
		for _, v in addon.selected do W( 'ButtonItem', addon.items[ v ].w ):SetVariant( 0 ) end
		for _, v in { W( 'ButtonLoadSelected' ), W( 'ButtonUnLoadSelected' ), W( 'ButtonReset' ) } do v:Enable( false ) end
		addon.selected = {}
		return
	elseif reaction.sender == 'ButtonToggleAddonState' then
		addon:ToggleAddonState( parentName, not var )
		addon.items[ parentName ].state = not var
	end
	if isSelected and ( reaction.sender == 'ButtonLoadSelected' or reaction.sender == 'ButtonUnLoadSelected' ) then
		local state = reaction.sender == 'ButtonLoadSelected' -- or false
		for _, v in addon.selected do
			addon.items[ v ].state = state
			addon:ToggleAddonState( v, state )
		end
	end
	if reaction.widget:GetVariantCount() > 1 then reaction.widget:SetVariant( tonumber( not var ) ) end	
	addon:Update()
end

handlers[ 'item_left_click' ] = function( params )
	addon.locale = params.sender
	SetLocale( params.sender )
	popup:Toggle( false )
end

--[[
handlers[ 'mouse_right_click' ] = function( reaction )
	local parentName = reaction.widget:GetParent():GetName()
end--]]

handlers[ 'mouse_over' ] = function( reaction )
	if addon.isDefaultShowed then return end
	local name = reaction.widget:GetName()
	if reaction.active then
		tooltip:Show( {
			header = GetName( name ),
			desc = addon.items[ name ].description,
			widget = reaction.widget
		} )
	else
		tooltip:Reset()
	end
end
---------------------------------------------------------------------------------------------------
--
---------------------------------------------------------------------------------------------------
addon.AddItem = function( self, v )
	local config = self.config[ v.name ]
	local widget
	local item = { state = true }
	self.items[ v.name ] = item
	if HasNamePrefix( v.name ) then
		if not self.itemDesc then
			widget = W( 'Item' )
			self.itemDesc = widget:GetWidgetDesc()
		else
			widget = mainForm:CreateWidgetByDesc( self.itemDesc )
		end
	else
		if not self.itemDefaultDesc then
			widget = W( 'ItemDefault' )
			self.itemDefaultDesc = widget:GetWidgetDesc()
		else
			widget = mainForm:CreateWidgetByDesc( self.itemDefaultDesc )
		end
	end
	widget:SetName( v.name )
	item.w = widget
	if config and type( config ) == 'table' and config.state == false then
		item.state = config.state
		self:ToggleAddonState( v.name, item.state )
	else
		if HasNamePrefix( v.name ) then SendEvent( 'SCRIPT_ADDON_INFO_REQUEST', { target = GetName( v.name ), locale = addon.locale } ) end
	end
	W( 'ButtonToggleAddonState', item.w ):SetVariant( tonumber( item.state ) )
	if v.name == self.PREFIX .. self.NAME then W( 'ButtonToggleAddonState', item.w ):Show( false ) end
	W( 'Name', item.w ):SetVal( 'value', ToWS( GetName( v.name ) ) )
	W( 'Description', item.w ):SetVal( 'value', L[ 'Empty' ] )
end

addon.Init = function( self )
	self.container = W( 'Frame' ):GetChildChecked( 'Container', false )
	self.config = GetConfig( self.NAME ) or {}
	local global = userMods.GetGlobalConfigSection( self.NAME )
	self.locale = global and global.locale
	DnD:Init( self.DND_ID_BUTTON_MAIN, W( 'ButtonMain' ), nil, true )
	DnD:Init( self.DND_ID_BUTTON_MAIN + 1, W( 'Header' ), W( 'WindowMain' ), true )
	local function GetWidgets( group )
		local tab = {}
		for _, v in W( group ):GetNamedChildren() do tab[ v:GetName() ] = v end
		return tab
	end
	self.sort = { [ 'SortPanel' ] = GetWidgets( 'SortPanel' ), [ 'SortPanelDefault' ] = GetWidgets( 'SortPanelDefault' ) }
	self:RegHandlers()
	if avatar.IsExist() then handlers[ 'EVENT_AVATAR_CREATED' ]() end
	self.Init = nil
	popup:Init()
	tooltip:Init()
end

addon.RegHandlers = function( self ) for k, v in handlers do if sfind( k, '[A-Z]+_' ) then RegisterEventHandler( v, k ) else common.RegisterReactionHandler( v, k ) end end end

addon.SortBy = function( self, name, order )
	local colors = {
		{ r = 255 / 255, g = 185 / 255, b = 255 / 255, a = 1.0 },
		{ r = 255, g = 255, b = 255, a = 1.0 }
	}
	local array, tab, result = {}
	if name == 'SortByState' then
		for k, v in self.items do
			tab = { state = tonumber( v.state ), name = k }
			if self.isDefaultShowed then if not HasNamePrefix( k ) then tinsert( array, tab ) end
			else if HasNamePrefix( k ) then tinsert( array, tab ) end
			end
		end
		result = function( a, b )
			if a.state == b.state then result = a.name < b.name
			else if order then result = a.state > b.state else result = a.state < b.state end
			end
			return result
		end			
	elseif name == 'SortByName' then
		for k, v in self.items do
			if self.isDefaultShowed then if not HasNamePrefix( k ) then tinsert( array, k ) end
			else if HasNamePrefix( k ) then tinsert( array, k ) end
			end
		end
		result = function( a, b ) if order then return a < b else return a > b end end
	elseif name == 'SortByDesc' then
		for k, v in self.items do
			tab = { name = k, description = v.description or '' }
			if self.isDefaultShowed then if not HasNamePrefix( k ) then tinsert( array, tab ) end
			else if HasNamePrefix( k ) then tinsert( array, tab ) end
			end
		end
		result = function( a, b )
			if a.description == b.description then result = a.name < b.name
			else if order then result = a.description > b.description else result = a.description < b.description end
			end
			return result
		end
	elseif name == 'SortByDNDState' then
		for k, v in self.items do
			if HasNamePrefix( k ) then
				tab = { isDnDButtonShowed = tonumber( W( 'ButtonToggleDND', v.w ):IsVisible() and v.state ), isLocked = tonumber( v.isLocked ), name = k }
				tinsert( array, tab )
			end
		end
		result = function( a, b )
			if a.isDnDButtonShowed == b.isDnDButtonShowed then
				if a.isLocked == b.isLocked then result = a.name < b.name
				else if order then result = a.isLocked > b.isLocked else result = a.isLocked < b.isLocked end
				end
			else
				if order then result = a.isDnDButtonShowed > b.isDnDButtonShowed else result = a.isDnDButtonShowed < b.isDnDButtonShowed end
			end
			return result
		end
	elseif name == 'SortByVisibility' then
		for k, v in self.items do
			if HasNamePrefix( k ) then
				tab = { isHideButtonShowed = tonumber( W( 'ButtonShowHide', v.w ):IsVisible() and v.state ), isVisible = tonumber( v.isVisible ), name = k }
				tinsert( array, tab )
			end
		end
		result = function( a, b )
			if a.isHideButtonShowed == b.isHideButtonShowed then
				if a.isVisible == b.isVisible then result = a.name < b.name
				else if order then result = a.isVisible < b.isVisible else result = a.isVisible > b.isVisible end
				end
			else if order then result = a.isHideButtonShowed > b.isHideButtonShowed else result = a.isHideButtonShowed < b.isHideButtonShowed end
			end
			return result
		end
	else
		for k, v in self.items do
			if HasNamePrefix( k ) then
				tab = { isSettingsButtonShowed = tonumber( W( 'ButtonSettings', v.w ):IsVisible() and v.state ), name = k }
				tinsert( array, tab )
			end
		end
		result = function( a, b )
			if a.isSettingsButtonShowed == b.isSettingsButtonShowed then result = a.name < b.name
			else if order then result = a.isSettingsButtonShowed < b.isSettingsButtonShowed else result = a.isSettingsButtonShowed > b.isSettingsButtonShowed end
			end
			return result
		end
	end
	table.sort( array, result )
	self.container:RemoveItems()
	for i, v in array do
		v = type( v ) == 'string' and v or v.name
		local widget = self.items[ v ].w
		self.container:PushBack( widget )
		if not widget:IsVisible() then widget:Show( true ) end
		widget = W( 'ButtonItem', widget )
		if math.mod( i, 2 ) == 0 then widget:SetBackgroundColor( colors[ 1 ] ) else widget:SetBackgroundColor( colors[ 2 ] ) end
	end
end

addon.SaveConfig = function( self )
	for k, v in self.items do
		local val = {}
		if not v.state then val.state = v.state end
		if v.isLocked then val.isLocked = v.isLocked end
		self.config[ k ] = not tisempty( val ) and val or nil
	end
	if self.locale then
		local config = userMods.GetGlobalConfigSection( self.NAME )
		config.locale = self.locale
		userMods.SetGlobalConfigSection( self.NAME, config )
	end
	SetConfig( self.NAME, self.config )
end

addon.Update = function( self )
	W( 'WindowMain' ):SetFade( 0.9 )
	W( 'WindowMain' ):Enable( false )
	local function onSeconEvent()
		if not addon.isListUpdated then
			for _, v in GetStateAddons() do
				W( 'ButtonToggleAddonState', addon.items[ v.name ].w ):SetVariant( tonumber( v.isLoaded ) )
				addon.items[ v.name ].state = v.isLoaded
			end
		end
		W( 'WindowMain' ):SetFade( 1.0 )
		W( 'WindowMain' ):Enable( true )
		UnRegisterEventHandler( onSeconEvent, 'EVENT_SECOND_TIMER' )
	end
	RegisterEventHandler( onSeconEvent, 'EVENT_SECOND_TIMER' )
end

addon.ToggleMainWindow = function()
	local mainPanel = W( 'WindowMain' )
	local isInvisible = not mainPanel:IsVisible()
	if isInvisible then mainPanel:Show( isInvisible ) end
	local params = {
		tonumber( not isInvisible ),
		tonumber( isInvisible ),
		150,
		EA_MONOTONOUS_INCREASE
	}
	local function Effect( params )
		if params.effectType == ET_FADE and params.wtOwner:IsEqual( mainPanel ) then
			mainPanel:Show( isInvisible )
			isFinished = true
			DnD:Enable( W( 'WindowMain' ), true )
			UnRegisterEventHandler( Effect, 'EVENT_EFFECT_FINISHED' )
		end
	end	
	RegisterEventHandler( Effect, 'EVENT_EFFECT_FINISHED' )
	if isFinished then
		mainPanel:PlayFadeEffect( unpack( params ) )
		isFinished = false
		DnD:Enable( W( 'WindowMain' ), false )
	end
end

addon.ToggleAddonState = function( self, name, state )
	self.items[ name ].state = state or state == nil and false
	if HasNamePrefix( name ) then SendEvent( 'SCRIPT_ADDON_INFO_REQUEST', { target = GetName( name ), locale = addon.locale } ) end
	if not state then
		UnloadAddon( name )
		if name == self.PREFIX .. 'AOPanel' then W( 'ButtonMain' ):Show( true ) end
		return
	end
	LoadAddon( name )
end
---------------------------------------------------------------------------------------------------
addon:Init()
---------------------------------------------------------------------------------------------------