--- тут мои виджеты с именами _chk _edl входят в конфликт при создании пунктов меню с ихними именами
--- поэтому: передача виджета самого меню через self.value, а имени виджета пункта меню через item.label
---------------------------------------------------------------------------------------------------
-- Interface Manager
---------------------------------------------------------------------------------------------------
local EXLUDE_exObj = { parentObj = 1 }
local borders = { "Border", "Health", "-" }
local _ON_OFF = { "ON", "OFF", }
local insertBorder = function( item, v, fore )
	if not item.valueBorder then
		-- WCD(descr, name, parent, place, show )
		item.valueBorder = WCD( dsc.Border, nil, item.parent.value, { alignX = 3, alignY = 3 }, true )
		item.valueBorder:SetPriority(-1)
	end
	if v == "-" then
		item.valueBorder:Show(false)
	else
		--LogInfo("item.valueBorder:SetBackgroundTexture( common.GetAddonRelatedTexture( item.value ) ) '", v ,"'")
		if fore then item.valueBorder:SetForegroundTexture( common.GetAddonRelatedTexture( v ) )
		else item.valueBorder:SetBackgroundTexture( common.GetAddonRelatedTexture( v ) )
		end
		item.valueBorder:Show(true)
	end
end
local insertBorderF = function( item, v ) insertBorder ( item, v, true ) end
local insertBorderB = function( item, v ) insertBorder ( item, v, false ) end

local GetFunction = {
	Show = function(item) return item.parent.value:IsVisible() and "ON" or "OFF" end,
	Enable = function(item) return item.parent.value:IsEnabled() and "ON" or "OFF" end,
	Fade = function(item) return item.parent.value:GetFade() end,
		 --- тут проверка на эту функцию у виджета идет, поэтому имя сохраняем
		--- и если можно устанавливать то либо берем то что уже было сохранено либо 1
	TextScale = function(item) return item.name and PS[ item.parent.KeyPath .. ":" .. item.name ] or 1 end,
	BackgroundTexture = function() return "-" end,
	BackgroundColor = function(item) return item.parent.value:GetBackgroundColor() end,
	ForegroundTexture = function() return "-" end,
	ForegroundColor = function(item) return item.parent.value:GetForegroundColor() end,
	Move = nil,
	PlacementPlain = function(item) return item.parent.value:GetPlacementPlain() end,
}
local SetFunction = {
	Show = function(item, on) item.parent.value:Show( on == "ON" or on == true ) end,
	Enable = function(item, on) item.parent.value:Enable( on == "ON" or on == true ) end,
	Fade = function(item, v) item.parent.value:SetFade( tonumber(v) or 1) end,
	TextScale = function( item, scale ) item.parent.value:SetTextScale( tonumber(scale) or 1 ) end, 
	BackgroundTexture = insertBorderB,
	BackgroundColor = function(item, p) item.parent.value:SetBackgroundColor(p) end,
	ForeroundTexture = insertBorderF,
	ForegroundColor = function(item, p) item.parent.value:SetForegroundColor(p) end,
	Move = nil,
	PlacementPlain = function(item, p) item.parent.value:SetPlacementPlain(p) end,
}
--------------------------------------------------------------------------------
-- GLOBALS
--------------------------------------------------------------------------------
Global( "names", {})

local ADDONname = common.GetAddonName()
local SLASH_COMMANDS = { ["\/im"]=1, ["\/шь"]=1 }

local ORIG = {}
local w, n, mList
local _R = {}
----------------------------------------------------------------------------
---- FUNCTIONS
----------------------------------------------------------------------------
--[[
local function SetTextScale( w, scale )
---Масштабирует текст внутри текстового контрола.
---Поддерживается только строго центрированный текст (выравнивание: alignx="center", aligny="middle").
	---w:SetFormat( "<html alignx='center' aligny='middle'><r name='value'/><r name='text'/></html>" )
	--- тут уже преобразование к числу произошло
	w:SetTextScale( scale )
end
]]

local function getParamsFromPS( key, w )
	local child
	local i = string.find(key, ":", 1, true )
	if i then
		local name = string.sub(key,1, i-1)
		--LogToChat( name)
		child = w:GetChildUnchecked( name, false)
		if not child then return end --- такой виджет не найден
		return getParamsFromPS( string.sub(key, i+1), child )
	else
		--- тут последнее имя это функция
		--LogToChat("func:"..key)
		---local meta = getmetatable(w)
		---return w, meta and meta[key], key
		return w, SetFunction[key], key
	end
end

local function storeORIG(w, k, funcName)
	if not ORIG[k] and GetFunction[funcName] then
		--- запомним оригинальные значения если их еще не запомнили
		---LogInfo(" get w:",w:GetName()," ORIG.",k,":=",ORIG[k])
		ORIG[k] = GetFunction[funcName]( { parent = { value = w } } )
	end
end

local function funcRun( k, v)
	local w, func, funcName = getParamsFromPS( k, stateMainForm )
	if w and func then
		--LogInfo("funcRun - w=",w:GetName()," func:", func," - ", funcName, " val=",v)
		storeORIG(w, k, funcName)
		func( { parent = { value = w } }, v )
		return true
	end
end

local function updateMenus( obj )
	---LogInfo("update:", obj.name)
	obj:Update()
	if obj.parentObj then updateMenus(obj.parentObj) end
end

local function update( obj )
	local rep
	for k, v in PS do
		--LogInfo("update - ", k, "[]=",v )
		if funcRun( k, v) then
			rep = true
		end
	end
	if obj then updateMenus( obj ) end

	if rep then LogToChat(L("Interface was repaint")) end
end

local function isWidgetChanged( w )
	if not w then return end
	local key = wtGetAllParents( w )
	local len = string.len( key )
	---LogInfo( "in isWidgetChanged:"..key )
	for k, val in PS do
		if string.sub( k, 1, len ) == key then
			---LogInfo(key.." TRUE")
			return true
		end
	end
end

local onEvent, onReact = {}, {}
local DnDitemStart
local reactingDnDWidget

local function allParentsShow( obj, val )
	if not obj then return end
	obj.wtMenu:Show( val )
	allParentsShow ( obj.parentObj, val )
end

onReact["mouse_left_click"] = function( pars )
	if DnD:IsDragging() then return end

	--local parent = reaction.widget:GetParent()
	if false then
	elseif pars.sender == "DnD_End" then
		local obj = DnDitemStart.parent
		--- освободим ДнД
		DnD:Enable(reactingDnDWidget, false )
		--- погпсим служебные виджеты
		obj.borderDnD:Show( false )
		reactingDnDWidget:Show( false )
		--- включим все меню
		allParentsShow( obj, true )
		--- разрешим гашение по выходу мышки
		obj.mouse_overRun = true
		--- сбросим отражение режима ДнД
		DnDitemStart.value = "OFF"
		--- покажем текущее меню
		obj:Show ( true )
		--- заново прочтем положение окна и запомним его
		--- найдем пункт меню с этой функцией
		local nameFunc = "PlacementPlain"
		for i, itemMenu in obj.strucMenu do
			if itemMenu.name == nameFunc then
				GetFunction[nameFunc]( itemMenu )
				obj:setVal(itemMenu)
				local key = wtGetAllParents( obj.value )..":"..itemMenu.name
				PS[ key ] = type(itemMenu.value) == "function" and itemMenu.value(itemMenu) or itemMenu.value
				updateMenus( obj )
				break
			end
		end

	end
end

--- при ДнД по верх карты будет при последующем входе окно так же сдинуто - так как оно запоимнается
--- поэтому надо запомнить свое положение и при запуске переустановить
--- и в ДнД отключили сохранение изменения положения окна - теперь тут будем сами запоминать по событию (см. ниже)
--	{ wt = w, place = { posX = DnD.Place.posX, posY = DnD.Place.posY } }
onEvent["DND_SET_NEW_POS"] = function ( pars )
--[[	if pars.wt:GetInstanceId() ~= wt:GetInstanceId() then return end
	local newPlace = wtMainPanel:GetPlacementPlain()
	local key = overMap and "OverMapPlace" or "initPlace"
	if not _PS[key] or _PS[key].posX ~= newPlace.posX or _PS[key].posY ~= newPlace.posY then
		_PS[key] = { posX = newPlace.posX,  posY = newPlace.posY }
		toConfig()
	end
]]
end

local function fromConfig(cfg, key)
	local len = string.len( key )
	--- сначала очистим все на этом уровне Параметров
	for k in PS do
		if string.sub( k, 1, len ) == key then
			PS[k] = nil
		end
	end
	for k, val in cfg do
		if string.sub( k, 1, len ) == key then
			PS[k] = val
		end
	end
end
local function toConfig( cfg, key )
	local len = string.len( key )
	--- сначала очистим все на этом уровне Конфига
	for k in cfg do
		if string.sub( k, 1, len ) == key then
			local w, func = getParamsFromPS( k, stateMainForm )
			--- не сбрасывать не наши функции и параметры
			if func then cfg[k] = nil end
		end
	end
	for k, val in PS do
		if string.sub( k, 1, len ) == key then
			cfg[k] = val
		end
	end
	userMods.SetGlobalConfigSection( ADDONname, cfg )
end

local function reset( cfg, key )
	local len = string.len( key )
	--LogInfo("reset key:", key)
	for k in cfg do
		if string.sub( k, 1, len ) == key then
			local w, func = getParamsFromPS( k, stateMainForm )
			--- не сбрасывать не наши функции и параметры
			if func then cfg[k] = nil end
		end
	end
	for k in PS do
		if string.sub( k, 1, len ) == key then
			PS[k] = nil
		end
	end
	for k, v in ORIG do
		if string.sub( k, 1, len ) == key then
			--LogInfo(" rub ORIG:", k,"=",v)
			funcRun( k, v)
			ORIG[k] = nil
		end
	end
end

---local ShowWidgets
local function FUNC_mList(item)
	local obj = item.parent
	if item.func then
		local key = wtGetAllParents( obj.value )..":"..item.name
		-- запомним что перед этим было - оригинал
		storeORIG(obj.value, key, item.name)
		if false then
		elseif item.name == "Show" then
			obj.value:Show( not obj.value:IsVisible() )
		elseif item.name == "Enable" then
			obj.value:Enable( not obj.value:IsEnabled() )
		elseif item.name == "Fade" then
			obj.value:SetFade( tonumber(item.value) or 1 )
			--- востановим там функцию
			item.value = GetFunction["Fade"]( item )
		elseif SetFunction[item.name] then
			SetFunction[item.name]( item, item.value )
		end
		obj:setVal(item)
		PS[ key ] = type(item.value) == "function" and item.value(item) or item.value
		updateMenus( obj )
	elseif item.cmd then
		local key = wtGetAllParents( obj.value )
		--LogInfo("key:",key)
		local cfg = userMods.GetGlobalConfigSection( ADDONname ) or {}
		if item.name == "Load" then
			fromConfig( cfg, key )
			update( obj )
		elseif item.name == "Repaint" then
			update( obj )
		elseif item.name == "Save" then
			toConfig( cfg, key )
		elseif item.name == "List" then
			local len = string.len( key )
			for k, val in PS do
				if string.sub( k, 1, len ) == key then
					exObj('PS["'..k..'"]', val)
				end
			end
		elseif item.name == "Reset" then
			reset( cfg, key )
			update( obj )
		elseif item.name == "Move by DnD" then
			if item.value == "ON" then
				DnDitemStart = item
				--- погасим все окна меню родителей иначе они перехватывают мышку
				allParentsShow( item.parent, false )
				item.parent.mouse_overRun = false
				--item.parent.wtMenu:Show( false )
				LogToChat("start DnD with ".. item.parent.name )
				local wt = WCD( dsc.Border, "DnDborder", item.parent.value, { alignX = 3, alignY = 3 }, true )
				wt:SetPriority(-5)
				item.parent.borderDnD = wt

				wtSetPlace( reactingDnDWidget, { alignX = 2, alignY = 2, posX = 0, posY = 0 } )
				item.parent.value:AddChild(reactingDnDWidget)
				local absPos = wtGetAbsPos(reactingDnDWidget)
				mainForm:AddChild(reactingDnDWidget)
				wtSetPlace(reactingDnDWidget, absPos)
				reactingDnDWidget:Show( true )
				item.parent.value:SetPriority(5000)
				item.parent.value:Show( true )

				-- запомним положение что перед этим было - оригинал
				local funcName = "PlacementPlain"
				local key = wtGetAllParents( obj.value )..":".. funcName
				storeORIG(obj.value, key, funcName)

				DnD:Init(reactingDnDWidget, item.parent.value, false, false )
				--DnD:Init(reactingDnDWidget, reactingDnDWidget, false, false )
			else
				--LogToChat("end DnD with ".. item.parent.name )
			end
		end
		---exObj("config", cfg )
	else
		--- get child widget
		if item.childsMenu then
			item.childsMenu:Show(true)
		else
			local m = ShowWidgets( W(item.label, obj.value), obj, nil, item )
			m:Show(true)
			item.childsMenu = m
		end
	end
end

function ShowWidgets( widget, parentObj, cols, parentItem )
	---LogToChat( widget.GetName and widget:GetName() or "stateMainForm" )
	local mList, n
	local strucMenu = {}
	---LogInfo(not parentObj or not parentObj.recurseCount or parentObj.recurseCount)
	local layer = parentObj and parentObj.mainForm or 0
	if layer > 0 then
		n = "Show" table.insert( strucMenu, { name = n, label = n, func = 1, type = "_txt", value = GetFunction[n] } )
		n = "Enable" table.insert( strucMenu, { name = n, label = n, func = 1, type = "_txt", value = GetFunction[n] } )
		n = "Fade" table.insert( strucMenu, { name = n, label = n, func = 1, type = "_edl", value = GetFunction[n] } )
		--- проверим есть ли такая функция у этого виджета
		n = "TextScale"
		if widget["Set"..n] then
			table.insert( strucMenu, { name = n, label = n, func = 1, type = "_edl", value = GetFunction[n], tip= "Resize Text. It worked only for centered text: <html alignx='center' aligny='middle'>" } )
		end
		n = "BackgroundTexture" table.insert( strucMenu, { name = n, label = n, func = 1, type="_txt", listVals = borders } )
		n = "BackgroundColor" table.insert( strucMenu, { name = n, label = n, func = 1, type ="_lst",
			value = GetFunction[n],
			list = { type = "_edl", labels = nil, vals = nil,
				edited = nil, --- можно редактировать список
				numberKeys = nil, --- список состоит из нумерованных списков - очередность элементов влияет на результаты и их можно менять местами
				},
			} )
		n = "ForegroundTexture" table.insert( strucMenu, { name = n, label = n, func = 1, type="_txt", listVals = borders } )
		n = "ForegroundColor" table.insert( strucMenu, { name = n, label = n, func = 1, type ="_lst",
			value = GetFunction[n],
			list = { type = "_edl", labels = nil, vals = nil,
				edited = nil, --- можно редактировать список
				numberKeys = nil, --- список состоит из нумерованных списков - очередность элементов влияет на результаты и их можно менять местами
				},
			} )
		n = "Move by DnD" table.insert( strucMenu, { name = n, label = n, cmd = 1, type="_txt", listVals = _ON_OFF, value = "OFF" } )

		n = "PlacementPlain" table.insert( strucMenu, { name = n, label = n, func = 1, type ="_lst",
			value = GetFunction[n],
			list = { type = "_edl", labels = nil, vals = nil,
				edited = nil, --- можно редактировать список
				numberKeys = nil, --- список состоит из нумерованных списков - очередность элементов влияет на результаты и их можно менять местами
				},
			} )
	elseif layer == 0 then
	end
		table.insert( strucMenu, { token = "1", label = "Commands for:",
			format = "<html alignx='center' fontsize='14' fontname='AllodsWest'><tip_green><r name='value'/></tip_green></html>" } )
		table.insert( strucMenu, { token = "1", label = wtGetAllParents( widget ),
			format = "<html alignx='right' fontsize='14' fontname='AllodsSystem'><tip_golden><r name='value'/></tip_golden></html>" } )
		table.insert( strucMenu, { name = "Repaint", label = "Repaint", cmd=1 } )
		table.insert( strucMenu, { name = "List", label = "List", cmd=1 } )
		table.insert( strucMenu, { name = "Save", label = "Save", cmd=1 } )
		table.insert( strucMenu, { name = "Load", label = "Load", cmd=1 } )
		table.insert( strucMenu, { name = "Reset", label = "Reset", cmd=1 } )
		table.insert( strucMenu, { token = "1", label = "*** Childs ***" } )

	local strucMenu1 = {}
	for i, w in widget:GetNamedChildren() do
		---LogToChat(w:GetName())
		--- тут нелья имя элемента брать как у виджета - иначе конфликт с именми с _chk _edl...
		table.insert( strucMenu1, { name = ""..i, label = w:GetName(), type="_txt", 
			--- тут передача виджета самого меню через self.value, а имени виджета пункта меню через item.label
			value = function(item) return isWidgetChanged( item.parent.value:GetChildUnchecked(item.label, false ) ) and "<--Changed" or "" end,
			offset = function(item) return isWidgetChanged( item.parent.value:GetChildUnchecked(item.label, false ) ) and 20 or 0 end,
			} )
	end
	table.sort( strucMenu1, function( A, B ) return A.label < B.label end )
	for _, item in strucMenu1 do
		table.insert( strucMenu, Clone(item) )
	end
	---exObj("strucMenu", strucMenu)

	mList = menu{ parentObj = parentObj, name= parentObj and widget:GetName() or "List of MainForms", strucMenu = strucMenu }
	mList:init({
		mainForm  = (parentObj and parentObj.mainForm and parentObj.mainForm or 0) + 1,
		RowsColsRate = 10,
		---cols = cols,
		mouse_left_click = FUNC_mList,
		mouse_overRun = true,
		value = widget, --- remember this widget
		fadeOff = 500, fadeVal = 0.2,
		KeyPath = wtGetAllParents( widget ),

	})
	--LogToChat((parentObj and parentObj.recurseCount.."") or "")
	if parentObj and parentObj.mainForm and parentObj.mainForm > 1 then wtChain( mList.wtMenu, widget, 10, 10)
	elseif parentItem then wtChain( mList.wtMenu, parentItem.widgets["mnu_"], -20, -30)
	end
	DnD:Init( mList.wtMenu, mList.wtMenu, false )

	return mList
end


------------------------------------------------------------
--------------------------------------------------------------------------------
-- EVENT HANDLERS
--------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------
_R.EVENT_UNKNOWN_SLASH_COMMAND = function( event )
	local cmd = FromWS(event.text)
	if not SLASH_COMMANDS[string.lower(string.sub(cmd, 1,3)) ] then return	end
	if string.sub(cmd, 4,4) ~= " " then return end
	cmd = string.sub(cmd, 5) ---  в нижний регистр не надо переводить всю строку
	if false then
	elseif string.lower(cmd) == "load" then
		fromConfig()
		update()
	elseif string.lower(cmd) == "save" then
		toConfig()
	elseif string.lower(cmd) == "list" then
		---getAllChildrensOfWidget("",stateMainForm)
	else
		cmd = string.lower(cmd) .. " "
		--- выделим из командной строчки параметры "parameter1:ValueA, parameter2=ValueB ..."
		for k, v in string.gfind(cmd, "%s*(.-)[=:](.-)[%s%p]+" ) do
			---LogInfo("[", k, "]:=", v)
			if tonumber(v) then
				---- тут числовые значения
				PS[k] = tonumber(v)
				LogToChat(ADDONname.." PS."..k.." = "..PS[k] )
			elseif CONST[v] then
				--- тут константные значения 
				PS[k] = CONST[v]
				LogToChat(ADDONname.." PS."..k.." = "..v )
			else --- тут символьные значения 
				PS[k] = v
				LogToChat(ADDONname.." PS."..k.." = "..v )
			end
		end
	end

end

--[[ -- теперь кнопка не нужна
_R.mouse_left_click = function( pars )
	if pars.sender == "Main_btn" then
		if not mList then mList = ShowWidgets(stateMainForm, nil, 3) end
		mList:Show( not mList:IsVisible() )
	end
end
]]
function mainMenuToggle()
	if not mList then mList = ShowWidgets(stateMainForm, nil, 3) end
	mList:Show( not mList:IsVisible() )
end

function AOBRightClick( pars )
	---exObj("pars", pars )
	if pars.reaction.kbFlags == 0 then
		--- по правой мышке на меню - перерисовать все
		update()
	end
end


local timer1v
local function timer1()
	if timer1v <= 0 then
		common.UnRegisterEventHandler( timer1, "EVENT_SECOND_TIMER" )
		update()
	else
		timer1v = timer1v - 1
	end
end
---------------------------------------------------------------------------------------------------
---- INITs
---------------------------------------------
local function reg()
	for k, v in pairs( _R ) do
		if string.find( k, "[A-Z]+_" ) then
			common.RegisterEventHandler( v, k )
		else
			common.RegisterReactionHandler( v, k )
		end
	end
end

function mainInit()
	timer1v = 0

	--- взять глобальные настройки
	---self.config = userMods.GetGlobalConfigSection( ADDONname ) or {}
	reg()
	local cfg = userMods.GetGlobalConfigSection( ADDONname ) or {}
	fromConfig( cfg, "" )
	common.RegisterEventHandler( timer1, "EVENT_SECOND_TIMER" )

	RegisterEventHandlers( onEvent )
	RegisterReactionHandlers( onReact )

	reactingDnDWidget = WCD( dsc.ButtonCornerCross, "DnD_End", nil, { alignX = 2, alignY = 2 }, false )


end