-- функция, загружающая все lua файлы из определенной директории
misc = {}

misc.hide_window_delay = 400 -- время сокрытия окна
misc.show_window_delay = 400 -- время проявления окна
misc.shift_before_hide = 100 -- задержка перед началом затемнения

misc._show_time = 300 -- время появления окна и его тени
misc._shadow_color = sf.graphics.Color(150, 0, 0, 0) -- цвет тени


function __AddStatShoweffect(_dialog)
	_dialog.SetColor(sf.graphics.Color(0, 255, 255, 255))
	s11.AddShowToWidget(_dialog, sf.graphics.Color(0, 0, 0, 0), misc._shadow_color, misc._show_time, 0, true)
	s11.AddEffectToWidget(_dialog, sf.graphics.Color(255, 255, 255, 255), misc._show_time, 0)
end

function load_lua_scripts(_path)
	for f in lfs.dir(_path) do
		if string.find(f, ".*%.lua") then
			quest.g_Lua.LoadScript(_path..f)
		end
	end
end

function __assert(_value, _message, _not_break)
	if sf.core.g_Application.GetDebugLevel() < sf.core.g_Application.DebugLight then return end
	
	if not _message then 
        if not _value then 
            error("assertion failed\n"..debug.traceback(), 0) 
        end
	elseif not _value then 
		__message(_message, "assertion failed")
        if not _not_break and not _value 
        then 
            error("assertion failed\n"..debug.traceback(), 0) 
        end
    end
end

function __message(_text)
	if sf.core.g_Application.GetDebugLevel() >= sf.core.g_Application.DebugLight then
		sf.misc.MessageBox(_text, "message", 0)
	end
end

-- функция для формирвоания строки времени
-- _time - время в милисекундах
-- _show_empty_hours - показвать ли значение часов если оно менее часа
function TimeToString(_time, _show_empty_hours)
	if _time < 3600000 and _show_empty_hours == nil then
		return s11.StringFormat("%02d:%02d", _time/60000, (_time%60000)/1000 )
	else
		local hours = _time / 3600000
		local less_hour_time = _time % 3600000
		return s11.StringFormat("%02d:", hours)..s11.StringFormat("%02d:%02d", less_hour_time/60000, (less_hour_time%60000)/1000 )
	end
end

function string_to_color(_string)
    local c = {}
    c.a, c.r, c.g, c.b = string.match(_string, "#(%d+)%s+(%d+)%s+(%d+)%s+(%d+)")
    for k,v in pairs(c) do 
        if not c[k] then return sf.graphics.Color(255, 255, 255, 255) end
        c[k] = tonumber(c[k])
        assert(c[k])
    end
    return sf.graphics.Color(c.a, c.r, c.g, c.b)
end

function string_to_bool_or_numeral(_string)
    if _string == "true" then
        return true
    elseif _string == "false" then
        return false
    else
        return tonumber(_string)
    end
end


function set_widget_flag_value(_widget, _flag, _value)
	if _value then
		_widget.SetFlags(sf.misc.BitwiseOr(_widget.GetFlags(), _flag))
	else
		if sf.misc.BitwiseAnd(_widget.GetFlags(), _flag) == 0 then
		else
			_widget.SetFlags(sf.misc.BitwiseXor(_widget.GetFlags(), _flag))
		end
	end
end

function copy_table(_src)
	local ret = {}
	for k,v in pairs(_src) do
		ret[k] = v
	end
	return ret
end

-- возвращает true, если таблицы отличаются
function compare_tables(_1, _2)
	for k,_ in pairs(_1) do
		if _2[k]~=_1[k] then return true end
	end
	for k,_ in pairs(_2) do
		if _2[k]~=_1[k] then return true end
	end	
	return false
end

function copy_array(_src)
	local ret = {}
	for k,v in ipairs(_src) do
		ret[k] = v
	end
	return ret
end

-- возвращает true, если массивы отличаются
function compare_arrays(_1, _2)
	local s1,s2 = #_1, #_2
	if s1~=s2 then return true end
	for i = 1,s1 do
		if _1[i]~=_2[i] then return true end
	end
	return false
end

-- преобразование строки с вектором в sf.misc.FloatVector
function StringToFloatVector(_str)
	local x_str,y_str = string.match(_str, "^(\-?%d+\.?%d*) (\-?%d+\.?%d*)$")
	local x = tonumber(x_str)
	local y = tonumber(y_str)
	if not x or not y then
		 __message("wrong float vector: ".._str) 
		return sf.misc.FloatVector(0, 0)
	end
	return sf.misc.FloatVector(x, y)
end

-- наследует одну таблицу от другой
function __inherite(_derived, _base)
	for key,value in pairs(_base) do
		if _derived[key]==nil then
			_derived[key] = value
		end
	end
	
	assert(_derived.__base==nil)
	_derived.__base = _base
	return _derived
end

function default_lua_widget_handler(_actions)
	return 
	{
		DoDraw = function(_this, _renderer)
			return false
		end,

		DoUpdate = function(_this)
			return false
		end,

		OnChar = function(_this, _char, _keyboard_state, _state, _broadcast)
			return _this.SuperOnChar(_char, _keyboard_state, _state, _broadcast)
		end,

		OnKeyDown = function(_this, _key, _keyboard_state, _state, _broadcast)
			return _this.SuperOnKeyDown( _key, _keyboard_state, _state, _broadcast)
		end,

		OnKeyUp = function(_this, _key, _keyboard_state, _state, _broadcast)
			return _this.SuperOnKeyUp( _key, _keyboard_state, _state, _broadcast)
		end,

		OnMouseMove = function(_this, _pos, _state, _broadcast)
			return _this.SuperOnMouseMove( _pos, _state, _broadcast)
		end,

		OnMouseDown = function(_this, _pos, _button, _state, _broadcast)
			return _this.SuperOnMouseDown( _pos, _button, _state, _broadcast) 
		end,

		OnMouseUp = function(_this, _pos, _button, _state, _broadcast)
			return _this.SuperOnMouseUp( _pos, _button, _state, _broadcast) 
		end,

		OnDoubleClick = function(_this, _pos, _state, _broadcast)
			return _this.SuperOnDoubleClick( _pos, _state, _broadcast) 
		end,

		OnMouseWheel = function(_this, _pos, _delta, _state, _broadcast)
			return _this.SuperOnMouseWheel( _pos, _delta, _state, _broadcast)
		end,

		OnChildAction = function(_this, _action, _child)
			if not _actions or not _child then return end
			local handler = _actions[_child.GetId().c_str()]
			if handler then handler = handler[_action.c_str()] end
			if handler then handler(_this, _child) end
		end,

		Load = function(_this, _info, _templates)
		    return true
		end
	}
end

function null_lua_widget_handler(_actions)
	return 
	{
		DoDraw = function(_this, _renderer)
			return false
		end,

		DoUpdate = function(_this)
			return false
		end,

		OnChar = function(_this, _char, _keyboard_state, _state, _broadcast)
			return false
		end,

		OnKeyDown = function(_this, _key, _keyboard_state, _state, _broadcast)
			return false
		end,

		OnKeyUp = function(_this, _key, _keyboard_state, _state, _broadcast)
			return false
		end,

		OnMouseMove = function(_this, _pos, _state, _broadcast)
			return false
		end,

		OnMouseDown = function(_this, _pos, _button, _state, _broadcast)
			return false
		end,

		OnMouseUp = function(_this, _pos, _button, _state, _broadcast)
			return false
		end,

		OnDoubleClick = function(_this, _pos, _state, _broadcast)
			return false
		end,

		OnMouseWheel = function(_this, _pos, _delta, _state, _broadcast)
			return false
		end,

		OnChildAction = function(_this, _action, _child)
			if not _actions or not _child then return end
			local handler = _actions[_child.GetId().c_str()]
			if handler then handler = handler[_action.c_str()] end
			if handler then handler(_this, _child) end
			return false
		end,

		Load = function(_this, _info, _templates)
		    return true
		end
	}
end

__or = sf.misc.BitwiseOr
__and = sf.misc.BitwiseAnd
__xor = sf.misc.BitwiseXor

function __visible(_widget, _visible)
	local flags = __or(_widget.FlagHidden, _widget.FlagDisabled)
	if _visible then
		_widget.RemFlags(flags)
	else
		_widget.AddFlags(flags)
	end
end

function get_child_widget(_parent_widget, _child_widget_id, _type_for_cast, _text )
        local casted_widget = nil
        assert(_parent_widget, "родитель не найден ".." Child id :".._child_widget_id)
        assert(_child_widget_id and (_child_widget_id ~= ""), "не указан id запрашиваемого виджета")
        assert(_type_for_cast, "тип неверный")
        local widget = _parent_widget.GetWidget(_child_widget_id, true).get()
        if widget then
            casted_widget = __cast(widget, _type_for_cast)            
		end
		
        if not casted_widget and _text then
            __message(_text, "Ошибка получения виджета")
        end
        return casted_widget
end

function MoveWindowToCenter(_owner, _child_window)
	local owner_size = _owner.GetSize()
	local owner_offset = _owner.GetOffset()
	local child_size = _child_window.GetSize()
	_child_window.SetOffset(owner_size.X/2 - child_size.X/2, owner_size.Y/2 - child_size.Y/2)
end

function random(...)
    return math.random(...)
end

function __create_timer()
	return sf.core.CTimerWrapper(sf.core.g_Application.GetTimeManager().AttachTimer("", nil))
end

function dialog_do_modal(_id, _owner, _pos_0_0)
	
	local dialog = _id
	local child_name = _id
	if type(_id) == "string" then
		dialog = sf.gui.CWindow()
		dialog.Load(
			sf.core.g_Application.GetSettings().GetChild("GUI", false).GetChildByAttribute("window", "id", _id, true), 
			sf.core.g_Application.GetSettings().GetChild("GUITemplates", false))
		dialog.UpdateLayout()
	else
		child_name = tostring(_id.GetId())
	end
	if _pos_0_0 then
		local offset = sf.misc.FloatVector(0, 0)
		local parent = _owner
		while parent do
			offset.X = offset.X - parent.GetOffset().X
			offset.Y = offset.Y - parent.GetOffset().Y
			parent = parent.GetParentWindow()
		end
		dialog.SetOffset(offset.X, offset.Y)
	else
		MoveWindowToCenter(_owner, dialog)
	end
	local dialog_ptr = sf.gui.CBaseWindowPtrT(dialog)
	if misc.do_modal_assert_flag == true then
		--__message("domodal warning!!! "..child_name)
	end
	misc.do_modal_assert_flag = true
	local res = _owner.DoModal(dialog_ptr)
	misc.do_modal_assert_flag = false
	
	return res
end


function child_dialog(_id, _owner, _pos_0_0)
	
	local dialog = _id
	local child_name = _id
	if type(_id) == "string" then
		dialog = sf.gui.CWindow()
		dialog.Load(
			sf.core.g_Application.GetSettings().GetChild("GUI", false).GetChildByAttribute("window", "id", _id, true), 
			sf.core.g_Application.GetSettings().GetChild("GUITemplates", false))
		dialog.UpdateLayout()
	else
		child_name = tostring(_id.GetId())
	end
	if _pos_0_0 then
		local offset = sf.misc.FloatVector(0, 0)
		local parent = _owner
		while parent do
			offset.X = offset.X - parent.GetOffset().X
			offset.Y = offset.Y - parent.GetOffset().Y
			parent = parent.GetParentWindow()
		end
		dialog.SetOffset(offset.X, offset.Y)
	else
		MoveWindowToCenter(_owner, dialog)
	end
	
	_owner.AddChildWindow(dialog)
end


-- обработчик диалогов со стандартным набором кнопок.
-- кнопки должны иметь айдишники-цифры.
-- кнопки с положительным результатом (yes, ok) - "1".
-- кнопки с отрицательным результатом (no, cancel) - "0"
standart_dialog = {}

function standart_dialog.DoDraw(_this, _renderer) 
    return false 
end

function standart_dialog.DoUpdate(_this) 
    return false 
end

function standart_dialog.OnChar(_this, _char, _keyboard_state, _state, _broadcast)
	return _this.SuperOnChar(_char, _keyboard_state, _state, _broadcast)
end

function standart_dialog.OnKeyDown(_this, _key, _keyboard_state, _state, _broadcast)
	if _key == 27 then
		_this.EndModal(0)
		return true
	elseif _key == 13 then
		_this.EndModal(1)
		return false
	end
    return _this.SuperOnKeyDown( _key, _keyboard_state, _state, _broadcast)
end

function standart_dialog.OnKeyUp(_this, _key, _keyboard_state, _state, _broadcast)
	return _this.SuperOnKeyUp( _key, _keyboard_state, _state, _broadcast)
end

function standart_dialog.OnMouseMove(_this, _pos, _state, _broadcast)
	return _this.SuperOnMouseMove(_pos, _state, _broadcast) 
end

function standart_dialog.OnMouseDown(_this, _pos, _button, _state, _broadcast)
	return _this.SuperOnMouseDown( _pos, _button, _state, _broadcast) 
end

function standart_dialog.OnMouseUp(_this, _pos, _button, _state, _broadcast)
	return _this.SuperOnMouseUp( _pos, _button, _state, _broadcast) 
end

function standart_dialog.OnDoubleClick(_this, _pos, _state, _broadcast)
	return _this.SuperOnDoubleClick( _pos, _state, _broadcast) 
end

function standart_dialog.OnMouseWheel(_this, _pos, _delta, _state, _broadcast)
	return _this.SuperOnMouseWheel( _pos, _delta, _state, _broadcast)
end

function standart_dialog.OnChildAction(_this, _action, _child)
	if not _child then return end
	if _action.c_str() ~= "pressed" then return end
	_this.EndModal(tonumber(_child.GetId().c_str()))
end

-- функция для расчета времени движения по отрезку с заданной скоростью
function CalcTimeForVector(_pos1, _pos2, _speed)
	local dist = sf.misc.CalculateFloatVectorModule(_pos1 - _pos2)
	return dist * 1000 / _speed
end

function __sound(_id, _delay)
    if _id ~= nil then
        sf.core.g_Application.GetAudioManager().Play(_id, -2, -2, -2, -2, -2);
        if _delay ~= nil and _delay == true then
            local time = sf.core.g_Application.GetAudioManager().GetLength(_id)
            if time ~= nil then
                sleep(time)            
            end
        end
    end
end 

function __IsPointInRect(_pos, pos, size)
	if pos and size then
		if _pos.X >= pos.X and _pos.Y >= pos.Y and _pos.X <= pos.X + size.X and _pos.Y <= pos.Y + size.Y then
			return true
		end
	end
	return false
end

-- создание эффекта смены главного окна
function __ChangeWindowWithEffect(_no_effect, _unload_resources)
	local window = sf.gui.CWindow()

	if _no_effect then
		return window
	end
    
    if _unload_resources == nil then
        _unload_resources = false
    end
    
	if not s11.AddChangeMainWindowEffect(misc.hide_window_delay, misc.show_window_delay, 
                misc.shift_before_hide, sf.gui.CBaseWindowPtrT(window), _unload_resources) 
    then
		window = nil
		collectgarbage()
		return nil
	end
	return window
end

-- эффект "накручивания"
CScoresEffect = function(_label)
	
	local scores_effect_info = g_App.GetSettings().GetChild("Constants", false).GetChildRef("scores_effect", false)
	assert(scores_effect_info)
	
	local effect_time = 500
	if scores_effect_info.IsValue("time") then
		effect_time = tonumber(scores_effect_info.GetValue("time").c_str())
	end
	
	return
	{
		_label = _label,
		_timer = __create_timer(),
		_effect_time = effect_time,
		
		SetTargetValue = function(_self, _value)
			local t = _self._timer.Get().GetTime()
			_self._values = {_self._current_value or _value, _value}
			_self._times = {t, t + _self._effect_time}
		end,
		
		Update = function(_self)
			if not _self._values then
				return
			end
			local k = (_self._timer.Get().GetTime() - _self._times[1]) / (_self._times[2] - _self._times[1])
			if k > 1 then k = 1 end
			if k < 0 then k = 0 end
			_self._current_value = _self._values[1] + k * (_self._values[2] - _self._values[1])
			_self._label.SetText(tostring(math.floor(_self._current_value)))
		end
	}
end

-- проверка точки текстуры на прозрачность
function __IsTransparentPoint(_anim_tex, _x, _y)
    local texture = _anim_tex.GetTexture(0)
	if not texture.RectSurfaceIsBuffer(0) then
		texture.CreateBuffer()
		texture.LoadBuffer()
	end
	assert(texture.RectSurfaceIsBuffer(0))
	local pixel = texture.Pixel(_x, _y)
	return (sf.graphics.Color(pixel).Alpha == 0)
end