--[[
модуль, отвечающий за эффекты окна статистики.

чтобы эффекты работали, надо:
- после загрузки окна вызвать stat_effects.Init(_window, _params), где _window - окно статистикт, _params - таблица описания эффектов;
- в DoUpdate окна вызвать stat_effects.Update();
- если хочется, чтобы по клику все эффекты разом переходили в законченное состояние, надо добавить в OnMouseClick окна stat_effects.End().

настройка эффектов (см. game_stat_window.effects_params в game_stat_window.lua).
виджеты появляются в том порядке, в котором они прописаны в таблице.
таблица содержит описания эффектов. 
один эффект содержит:
- widgets - один или несколько виджетов, которые объеденены одним эффектом и появляются одновременно.
- delay - задержка перед появлением эффекта - если не указана, то берется delay из default;
- duration - длительность эффекта - если не указана, то берется duration из default;
- particle - идентификатор партиклевого эффекта, который создается по ректу виджетов эффекта после полного их появления.
- scale_koef - насколько итоговая партиклевая система будт сжата относитлеьного целевого
один виджет из widgets содержит:
- id - идентификатор виджета;
- counter - описание эффекта "накручивания" - если этот параметр отсутствует у виджета, то используется обычный эффект появления с альфой.
counter содержит:
- min_value - функция, возвращющее значение, ОТ которого начинается "накручивание" - если не указана, то min_value берется равным 0;
- max_value - функция, возвращющее значение, ДО которого продолжается "накручивание" - обязательно должен быть указан;
- str_format - формат выходной строки.

примеры эффекта с описанием:

{
	widgets = 
	{ 
		{ id = "text" }, 
		{ id = "value", counter = {min_value = func_get_min_value, max_value = func_get_max_value, str_format = "%i"} } 
	},
	delay = 500,
	duration = 1000,
	particle = "particle_effect",
	scale_koef = {X = 0.8, Y = 0.8}
}
- это эффект, который обрабатывает 2 виджета. один из них ("text") появлется с альфой, другой ("value") "накручивается" от значечения,
которое вернет функция func_get_min_value, до значения, которое вернет функция  func_get_max_value. перед началом эффекта будет выдержана пауза
500 мс. длительность эффекта - 1000 мс, т.е. "text" изменит свою альфу от 0 до 255 за 1000 мс, и "value" накрутится от начального значения до конечного тоже за 
1000 мс. причем это будет происходит одновременно. по истечению 1000 мс появится партиклевый эффект "particle_effect", который растянется на описывающий
виджеты прямоугольник.
]] 

stat_effects = {}

-- эффект появления с альфой
stat_effects._CIncAlphaEffect = function(_widget)
	return
	{
		_widget = _widget,
		
		Update = function(_self, _k)
			local a = 255.0 * _k
			if a < 0 then a = 0 end
			if a > 255 then a = 255 end
			local c = _self._widget.GetColor()
			_self._widget.SetColor(sf.graphics.Color(a, c.Red, c.Green, c.Blue))
			__visible(_self._widget, a > 0)
		end
	}
end

-- эффект "накручивания"
stat_effects._CIncValueEffect = function(_widget, _min_value, _max_value, _str_format)
	
	local label = __cast(_widget, sf.gui.CLabelWidget)
	assert(label)
	
	return
	{
		_label = label,
		_min_value = _min_value,
		_max_value = _max_value,
		_str_format = _str_format,
		
		Update = function(_self, _k)
			local v = _self._min_value + (_self._max_value - _self._min_value) * _k
			if v < _self._min_value then v = _self._min_value end
			if v > _self._max_value then v = _self._max_value end
			_self._label.SetText(s11.StringFormat(_self._str_format, v))
		end
	}
end

-- эффект вылетающей маски
stat_effects._CMaskEffect = function(_window, _widget)
	
	local clip = __cast(_widget, sf.gui.CClipWidget)
	assert(clip)
	__visible(clip, false)
	
	local window_offset = _window.GetOffset()
	local widget_size = _widget.GetSize()
	_widget.SetOffset(-window_offset.X + widget_size.X/2, -window_offset.Y + widget_size.Y/2)
	
	return
	{
		_clip = clip,
		_last_k = -1,
		
		Update = function(_self, _k)
			if _k >= 0 and _self._last_k < 0 then
				__visible(_self._clip, true)
				_self._clip.GetClip().SetTime(0)
				_self._clip.GetClip().Play()
			end
			if _k >= 1 and _self._last_k < 1 then
				__visible(_self._clip, false)
			end
			_self._last_k = _k
		end
	}
end

-- создать эффект из описания
function stat_effects._CreateEffect(_window, _params)
	local effects = {}
	local lt, rb
	-- получаем все вижеты, которые обрабатывает эффект
	for _, w in pairs (_params.widgets) do
		local widget = __cast(_window.GetWidget(w.id, true), sf.gui.CWidget)
		if widget then
			if __and(widget.FlagHidden, widget.GetFlags()) ~= 0 then
				--__message("hidden widget")
				return nil
			end

			-- определяем крайние точки для определения ректа виджетов (используется при создании партиклевого эффекта)
			local widget_lt = sf.misc.FloatVector(widget.GetOffset())
			local widget_rb = widget_lt + widget.GetSize()
	
			if not lt then lt = widget_lt end
			if not rb then rb = widget_rb end
			if widget_lt.X < lt.X then  lt.X = widget_lt.X end 
			if widget_lt.Y < lt.Y then  lt.Y = widget_lt.Y end 
			if widget_rb.X > rb.X then  rb.X = widget_rb.X end 
			if widget_rb.Y > rb.Y then  rb.Y = widget_rb.Y end 
			
			-- если виджет является маской, то создаем для него соответствующий эффект. 
			-- эффект позаботится о том, чтобы клип в нужное время появился, проигрался и по истечению duration'а спрятался
			if w.id == "mask_clip" then
				table.insert(effects, stat_effects._CMaskEffect(_window, widget))
			-- если есть описания "накручивания", создаем соответствующий эффект - _CIncValueEffect
			elseif w.counter then
				local min_value = 0
				if w.counter.min_value then
					min_value = w.counter.min_value()
				end
				local str_format = "%i"
				if w.counter.str_format then
					str_format = w.counter.str_format
				end
				table.insert(effects, stat_effects._CIncValueEffect(widget, min_value, w.counter.max_value(), str_format))
			else
			-- иначе создаем эффект появления с альфой - _CIncAlphaEffect
				table.insert(effects, stat_effects._CIncAlphaEffect(widget))
			end
		end
	end
	
	local particle_pos, particle_size
	if lt and rb then
		particle_pos = lt + (rb - lt) / 2
		particle_size = rb - lt
	end
	
	return 
	{
		_effects = effects,
		_start_time = _params.start_time,
		_duration = _params.duration,
		_particle = _params.particle,
		_scale_koef = _params.scale_koef,
		_particle_pos = particle_pos,
		_particle_size = particle_size,
		
		_ended = false,

		Update = function(_self, _time)
			local dt = _time - _self._start_time
			local k = dt / _self._duration
			for _, e in pairs(_self._effects) do
				e:Update(k)
			end
			if k >= 1 and not _self._ended then
				-- по завершению эффекта создаем партиклевый эффект, если надо
				_self._ended = true
				if _self._particle and _self._particle_pos and _self._particle_size then
					local particle_effect = visual_effects_widget.GetEffectsList():CreateInstantEffect(_self._particle, _self._particle_pos)
					local rect_effect = particle_effect:GetChildEffect("rect")
					if rect_effect then
						-- изменении эмисии пропрционально площади
						local original_emission = rect_effect:GetEmission()

						local original_rect_size = rect_effect:GetEmiterSize()
						local new_em_size = sf.misc.FloatVector(0, 0)
						--new_em_size.Y = original_rect_size.Y
						new_em_size = _self._particle_size
						
						if _self._scale_koef then
							if _self._scale_koef.X then
								new_em_size.X = new_em_size.X * _self._scale_koef.X
							end
							if _self._scale_koef.Y then
								new_em_size.Y = new_em_size.Y * _self._scale_koef.Y
							end
						end
						local new_square = new_em_size.Y*new_em_size.X

						

						local old_square = original_rect_size.X*original_rect_size.Y
						if old_square < 0.01 then
							old_square = 1
						end
						
						local new_emission = (original_emission * new_square)/old_square
						rect_effect:SetEmission(new_emission)
						rect_effect:SetEmiterSize(new_em_size)
				
					end
				end
			end
		end
	}
end

-- инициализация эффектов окна статистики.
-- _window - окно статистики.
-- _params - таблица с описанием эффектов.
function stat_effects.Init(_window, _params)
	
	local params_id = _window.GetId().c_str()

	-- считаем для каждого эффекта абсолютное время начала.
	local params = {}
	local start_time = 0
	for _, item in ipairs(_params.items) do
		local delay = _params.default.delay or 0
		if item.delay then
			delay = item.delay
		end
		local duration = _params.default.duration or 0
		if item.widgets and item.widgets[1] and item.widgets[1].id == "mask_clip" then
			local clip = __cast(_window.GetWidget("mask_clip", true), sf.gui.CClipWidget)
			assert(clip)
			duration = qe.GetClipTime(clip.GetClip())
		end
		if item.duration then
			duration = item.duration
		end
		start_time = start_time + delay
		table.insert(params, {start_time = start_time, duration = duration, particle = item.particle, scale_koef = item.scale_koef, widgets = item.widgets} )
		start_time = start_time + duration
	end
	
	stat_effects._end_time = start_time
	
	stat_effects._items = {}
	
	for _, p in ipairs(params) do
		local new_effect = stat_effects._CreateEffect(_window, p)
		if new_effect then
			table.insert(stat_effects._items, new_effect)
		end
	end
	
	if not stat_effects._timer then
		stat_effects._timer = __create_timer()
	end
	stat_effects._timer.Get().SetTime(0)
end

function stat_effects.Update()
	local t = stat_effects._timer.Get().GetTime()
	for _, item in pairs(stat_effects._items) do
		item:Update(t)
	end
	return false
end

function stat_effects.End()
	stat_effects._timer.Get().SetTime(stat_effects._end_time)
end
