-- модуль для реализации визуальных эффектов на Lua
visual_effects = {}

function visual_effects.EffectsFactory(_effects_group, _effect_pos, _timer)
	local creators = 
	{
		particle = visual_effects.CreateSimpleParticleSystemEffect,
		composite = visual_effects.CreateCompositeEffect,
		sound = visual_effects.CreateSoundEffect
	}
	
    local effect_type = tostring(_effects_group.GetId())
	local creator = creators[effect_type]
	if creator then 
		local new_effect = creator()
		new_effect:Init(_effects_group, _effect_pos, _timer)
		return new_effect
	end
end

visual_effects._effect_list_prototype = 
{
    effects_list = {},
	
	Update = function(_self)
		local size = #(_self.effects_list)
		if size ~= 0 then
			local i = 1
			while i <= size do
				_self.effects_list[i]:Update()
				if _self.effects_list[i].is_dead == true then
					table.remove(_self.effects_list, i)
					size = size - 1
					i = i - 1
				end
				i = i + 1
			end
		end
	end,
	
	PreDraw = function(_self, _renderer)
		for i,o in pairs(_self.effects_list) do
			if o.pre_draw == true or o.pre_draw==nil then
				o:Draw(_renderer, true)
			end
		end
	end,
	
	PostDraw = function(_self, _renderer)
		for i,o in pairs(_self.effects_list) do
			if o.pre_draw == false or o.pre_draw==nil then
				o:Draw(_renderer, false)
			end
		end
	end,
	
	
	CreateClipEffect = function(_self, _clip_id, _effect_pos, _timer, _pre_draw)
		if not visual_effects._disable_effects then
			
			local new_effect = visual_effects._CreateClipEffectById()
			new_effect:Init(_clip_id, _effect_pos, _timer, _pre_draw)
			table.insert(_self.effects_list, new_effect)
			return new_effect

		end
	end,
	
	CreateEffect = function(_self, _effect_id, _effect_pos, _timer)
		if not visual_effects._disable_effects then
			local effect_id = _T(tostring(_effect_id))
			
			local effect = g_App.GetSettings().GetChild(_T("Effects"), false).GetFirstChildRef()
			while effect do
				if effect.GetValue("id").compare(effect_id)==0 then
					local new_effect = visual_effects.EffectsFactory(effect, _effect_pos, _timer)
					table.insert(_self.effects_list, new_effect)
					return new_effect
				end
				effect = effect.GetNextSiblingRef()
			end
		end
		
		local new_effect = visual_effects.CreateDumpEffect()
		new_effect:Init(_effect_id, _effect_pos)
		if not visual_effects._disable_effects then table.insert(_self.effects_list, new_effect) end
		return new_effect
		
	end,
	
	CreateInstantEffect = function( _self, ...)
		local new_effect = _self:CreateEffect(...)
		new_effect:SetFinished(true)
		return new_effect
	end
}

function visual_effects.CreateEffectsList()
    local ret = copy_table(visual_effects._effect_list_prototype) 
    ret.effects_list = {}
    return ret
end

function visual_effects.DisableEffects(_disable)
	visual_effects._disable_effects = _disable
end

-- базовый "класс" для эффектов
visual_effects._base_effect_class = 
{
	id = "",
	is_dead = false,
	is_finished = false,
	pre_draw = false, -- может иметь 3 значения: false, true и nil; nil означает, что эффект отрисовывается и до, и после
	offset = sf.misc.FloatVector(0, 0),
	
	Update = function(_self) 
		return false 
	end,

    GetChildEffect = function(_id)
        return nil
    end,
	
	Draw = function(_self, _renderer, _pre_draw) 
	end,
	
	SetFinished = function(_self, _value)
		_self.is_finished = _value
	end,
	
	Kill = function(_self, _momental)
		if _momental == true then
			_self.is_dead = true
		else
			_self.is_finished = true
		end
	end,
	
	SetPos = function(_self, _pos)
	end,
	
	Init = function(_self, _effect_group, _effect_pos)
		_self.id = tostring(_effect_group.GetValue("id"))
		
		_self.pre_draw = (tostring(_effect_group.GetValue("pre_draw")) == "true")

		_self.offset.X = tonumber(_effect_group.GetValue("offset_x")) or 0
		_self.offset.Y = tonumber(_effect_group.GetValue("offset_y")) or 0

		_self:SetPos(_effect_pos or sf.misc.FloatVector(0, 0))
	end
}

function visual_effects.CreateCompositeEffect()
	return __inherite(
    {
        child_effects_list = {},

        GetChildEffect = function(_self, _id)
            return _self.child_effects_list[_id]
        end,
		
        Update = function(_self)
			if not _self.is_dead then
	            _self.is_dead = true
	            for i,o in pairs(_self.child_effects_list) do
	                if o:Update() == false then
	                    _self.is_dead = false
	                end
	            end
			end
            return _self.is_dead
        end,

        SetEmiterSize = function(_self, _size)
            for i,o in pairs(_self.child_effects_list) do
                if o.SetEmiterSize then
                    o:SetEmiterSize(_size)
                end
            end
        end,

        SetColor = function(_self, _color)
            for i,o in pairs(_self.child_effects_list) do
                if o.SetColor then
                    o:SetColor(_color)
                end
            end
        end,
		
        Kill = function(_self, _momental)
			_self.__base.Kill(_self, _momental)
            for i,o in pairs(_self.child_effects_list) do
                o:Kill(_momental)
            end
        end,
		
        Draw = function(_self, _renderer, _pre_draw)
			if _pre_draw==nil then _pre_draw = false end
            for i,o in pairs(_self.child_effects_list) do
				if _pre_draw == o.pre_draw or o.pre_draw==nil then
					o:Draw(_renderer, _pre_draw)
				end
            end
        end,
		
        SetPos = function(_self, _pos)
            for i,o in pairs(_self.child_effects_list) do
                o:SetPos(_pos + _self.offset)
            end
        end,
		
        Init = function(_self, _effect_group, _effect_pos, _timer)
            local group = _effect_group.GetFirstChildRef()
            while group ~= nil do
                local new_effect = visual_effects.EffectsFactory(group, _effect_pos, _timer)
                --table.insert(_self.child_effects_list, new_effect)
                if new_effect.id ~= "" and new_effect.id ~= nil then
                    _self.child_effects_list[new_effect.id] = new_effect
                else
                    table.insert(_self.child_effects_list, new_effect)      
                end
                
                group = group.GetNextSiblingRef()
            end
			
			_self.__base.Init(_self, _effect_group, _effect_pos)
			
			_self.pre_draw = nil
        end,

        SetFinished = function(_self, _value)
            _self.__base.SetFinished(_self, _value)
            for i,o in pairs(_self.child_effects_list) do
                o:SetFinished(_value)
            end
        end
    }, visual_effects._base_effect_class)
end

function visual_effects.CreateSimpleParticleSystemEffect()
    return __inherite(
    {
        _particle_effect = nil,
		
        Update = function(_self)
			if not _self.is_dead then
				_self._particle_effect.Update()
	            _self.is_dead = _self.is_finished and _self._particle_effect.IsDead() and _self._particle_effect.GetLifeParticlesCount()==0 
			end
            return _self.is_dead
        end,
        
		SetEmiterSize = function(_self, _size)
            _self._particle_effect.SetEmiterSize(_size)
        end,

		GetEmiterSize = function(_self)
            return _self._particle_effect.GetEmiterSize()
        end,

		SetEmission = function(_self, _emission)
            _self._particle_effect.SetEmission(_emission)
        end,

		GetEmission = function(_self)
            return _self._particle_effect.GetEmission()
        end,

		SetColor = function(_self, _color)
            s11.SetParticleSystemColor(_self._particle_effect, _color)
        end,
		
        SetPos = function(_self, _pos)
            if not _self.static_draw then
                _self._particle_effect.SetLocation(_pos.X + _self.offset.X, _pos.Y + _self.offset.Y)
            else
               _self._pos = _pos + _self.offset
            end
        end,
		
        Kill = function(_self, _momental)
            _self.__base.Kill(_self, _momental)
            if not _momental then
                _self._particle_effect.SetFlags( __or(_self._particle_effect.GetFlags(), sf.graphics.CParticleSystem.IsStopEmit) )
                if __and(_self._particle_effect.GetFlags(), sf.graphics.CParticleSystem.IsContinuous) then
                    _self._particle_effect.SetFlags( __xor(_self._particle_effect.GetFlags(), sf.graphics.CParticleSystem.IsContinuous) )
                end
            end
        end,

        Draw = function(_self, _renderer)
            if not _self.static_draw then
                _self._particle_effect.Render(_renderer)
            else
                _renderer.PushState()
                _renderer.ApplyMatrix(sf.misc.MatrixTranslation(_self._pos.X, _self._pos.Y))
                _self._particle_effect.Render(_renderer)
                _renderer.PopState()
            end
            
        end,

        Init = function(_self, _effect_group, _effect_pos, _timer)
            _self.static_draw = (tostring(_effect_group.GetValue("static_draw")) == "true")
			local particle_id = _effect_group.GetValue("particle_id")
			
			_self._particle_effect = visual_effects.CreateParticleSystem(particle_id, _timer or sf.core.g_Application.GetTimeManager().GetTimer(_T("qe_level")).AttachTimer(""))

			_self.__base.Init(_self, _effect_group, _effect_pos)
        end
    }, visual_effects._base_effect_class)
end

visual_effects._particles_cache = {}

function visual_effects.CreateParticleSystem(_id, _timer)
	local chached_base_system = visual_effects._particles_cache[_id.c_str()]
	if not chached_base_system then
		chached_base_system = sf.graphics.CParticleSystem(nil)
		local particle = g_App.GetSettings().GetChild("Particles", false).GetChildByAttribute("particle", "id", _id, false)
		__assert(not particle.GetId().empty(), "effects.xml: particle '"..tostring(_id).."' not found!")
        chached_base_system.Load(particle)
		visual_effects._particles_cache[_id.c_str()] = chached_base_system
		chached_base_system.SetLocation(0, 0)
		chached_base_system.DropOldPos()	
	end

	local new_system = sf.graphics.CParticleSystem(chached_base_system)
	new_system.SetTimer(_timer)
	return new_system
end

function visual_effects.CreateSoundEffect()
    return __inherite(
    {
		_sound = nil,
		
        Update = function(_self)
			if not _self.is_dead then 
				if _self._sound then __sound(_self._sound) end
				_self.is_dead = true
			end
			return true
        end,
		
        Init = function(_self, _effect_group)
			_self._sound = tostring(_effect_group.GetValue("sound_id"))
        end
    }, visual_effects._base_effect_class)
end

function visual_effects._CreateClipEffectById()
    return __inherite(
    {	
        SetPos = function(_self, _pos)
            _self._clip.SetPos(_pos)
        end,
		
        Kill = function(_self, _momental)
            _self.is_dead = true
        end,
		
        Draw = function(_self, _renderer, _pre_draw)
			if _self.is_dead then
				return
			end
			_self._clip.Draw(_renderer)
        end,

        Update = function(_self)
			if not _self.is_dead then
				if  _self._end_time < _self._timer.Get().GetTime() then
					_self.is_dead = true
				else
					_self._clip.Update()
				end
				
			end
            return _self.is_dead
        end,
		
        Init = function(_self, _clip_id, _effect_pos, _timer, _pre_draw)
			_self._timer = _timer or __create_timer()
			
			if _pre_draw then
				_self.pre_draw = _pre_draw
			end
			
			_self._clip = visual_effects.CreateClip(_clip_id)
			_self._clip.Play()
			
			local clip_time = qe.GetClipTime(_self._clip)
			
			_self._end_time = clip_time + _self._timer.Get().GetTime()
			
            _self:SetPos(_effect_pos or sf.misc.FloatVector(0, 0))
        end
    }, visual_effects._base_effect_class)
end

function visual_effects.PreCacheClips()
	visual_effects.CacheClip(town_window._buy_clip)
	visual_effects.CacheClip(town_window._sell_clip)
end

visual_effects._clips_cache = {}

function visual_effects.CacheClip(_id)
	if visual_effects._clips_cache[_id] then
		return visual_effects._clips_cache[_id]
	end
	local chached_clip = sf.misc.anim.CClip()
	chached_clip.Load(_id, true)

	visual_effects._clips_cache[_id] = chached_clip
	chached_clip.Stop()
	return chached_clip
end

function visual_effects.CreateClip(_id)
	local _id = tostring(_id)
	local chached_clip = visual_effects.CacheClip(_id)

	chached_clip.SetTime(0)
	local new_clip = sf.misc.anim.CClip(chached_clip)
	return new_clip
end

function visual_effects.CreateDumpEffect()
    return __inherite(
    {	
		_pos = sf.misc.FloatVector(0, 0),
				
        SetPos = function(_self, _pos)
            _self.pos = _pos
        end,
		
        Kill = function(_self, _momental)
            _self.is_dead = true
        end,
		
        Draw = function(_self, _renderer)
            --_renderer.RenderLine(_self.pos - sf.misc.FloatVector(20,20), _self.pos + sf.misc.FloatVector(20,20), 0xffffffff)
            --_renderer.RenderLine(_self.pos - sf.misc.FloatVector(20,-20), _self.pos + sf.misc.FloatVector(20,-20), 0xffffffff)
        end,
		
        Init = function(_self, _effect_group, _effect_pos)
            _self.SetPos(_effect_pos or sf.misc.FloatVector(0, 0))
        end
    }, visual_effects._base_effect_class)
end

