--- виджет занимается реализацией логики сборки предметов по частям с уровня и отображением соответсвующего виджета
hog_widget = hog_widget or {}

__inherite(hog_widget, null_lua_widget_handler())

function hog_widget.__CreateInstance()
    return "hog_widget"
end

hog_widget._tutorial_object = "tutorial_arrow"
hog_widget._tutorial_object_hider = "plate#hog_03"

hog_widget._self = nil  
hog_widget._objects_table = {}  -- таблица с объектами ХОГа
hog_widget._completed = nil  -- флаг того что хог пройден
hog_widget._take_effects_list = {} -- список текущих эффектов взятия в инвентарь
hog_widget._timer = nil
hog_widget._cell_size = 125   --- расстояние между объектами в инвентаре хога
hog_widget._fly_speed = 750   --- скорость полета объекта в инвентарь
hog_widget._hint_show_time = 4000   --- время отображения хинта

hog_widget._draw_offset = sf.misc.FloatVector(140,700)  -- смещение отрисовки собраных объектов

hog_widget.moves_count = 0                              -- число верных ходов игрока
hog_widget.total_click_count = 0                        -- число кликов

function hog_widget.SetMovesCount(_value)
	hog_widget.moves_count = _value
end

function hog_widget.GetMovesCount()
	return hog_widget.moves_count
end

function hog_widget.SetTotalClickCount(_value)
	hog_widget.total_click_count = _value
end

function hog_widget.GetTotalClickCount()
	return hog_widget.total_click_count
end

function hog_widget._InsertEffect(_effect)
	table.insert(hog_widget._take_effects_list, _effect)
end

--- интерфейс доступа к флагу пройденнсоти хога
function hog_widget._SetCompleted(_value)
	hog_widget._completed = _value
end

function hog_widget.IsCompleted()
	return hog_widget._completed
end

--- сброс ХОГа вызывается каздый раз при запуске нового квеста
function hog_widget.Clear()
    hog_widget._self = nil
	hog_widget._objects_table = {}
	hog_widget._SetCompleted(nil)
	hog_widget._timer = nil
end

--- проверка на то что ХОГ пройден
function hog_widget._CheckCompleted()
	--- обходим все объекты и проверяем что у всех объектов собраны все части
	hog_widget._SetCompleted(true)

	local index = 1
	for _,o in pairs(hog_widget._objects_table) do
		if #o.taked_obj ~= #o.total_obj then
			hog_widget._SetCompleted(false)
			--return
		else
			-- создание если нужно эффекта на полностью собраном объекте
			if o._finish_effect == nil then
				o._finish_effect = hog_widget.visual_effects:CreateEffect("HogFinishedObjectEffect", sf.misc.FloatVector(index * hog_widget._cell_size , 0) + hog_widget._draw_offset)
			end
		end
		index = index + 1
	end	
end

-- убить эффект подсказки
function hog_widget.KillHint()
	if hog_widget._hint_effect then
		hog_widget._hint_effect:Kill(false)
	end
	hog_widget._hint_effect = nil
end

function hog_widget.GetHint()
	
	-- проверяем если есть объект для хинта нужен ли уже новый
	if hog_widget._hint_object then
		-- если объект на который показывал хинт еще не взяли
		if not hog_widget.hint_list[hog_widget._hint_object] then
			hog_widget._hint_object = nil
		end
	end
	
	-- если объекта для хинта нет или нужен новый то ищем рандомный
	if not hog_widget._hint_object then
		local temp_table = {}
		for id,object in pairs(hog_widget.hint_list) do
			table.insert(temp_table, id)
		end
		local size = #temp_table
		if size > 0 then 
			hog_widget._hint_object = temp_table[random(1, size)]
		end
	end
	-- если нашли объект для хинта то показываем его
	if hog_widget._hint_object then
	
		-- смещение объекта в группе
		local offset = hog_widget._GetObjectOffset(hog_widget._hint_object)
        -- позиция на сцене  
		local taked_object_pos = quest.get_object_pos(hog_widget._hint_object)
		local angle = quest.get_object_angle(hog_widget._hint_object)
		
		hog_widget.KillHint()
		local angle_sin = math.sin(angle)
		local angle_cos = math.cos(angle)

		local new_offset = sf.misc.FloatVector(angle_cos * offset.X - angle_sin * offset.Y,
			angle_sin * offset.X + angle_cos * offset.Y) 

		hog_widget._hint_effect = hog_widget.visual_effects:CreateEffect("HogHint", taked_object_pos - new_offset)
		hog_widget._hint_show_delay = hog_widget._hint_show_time
	end
	
end

function hog_widget._GetObjectOffset(_id)
	local offset = sf.misc.FloatVector(0, 0)
	if hog_widget.offset_list[_id] then
		offset = sf.misc.FloatVector(hog_widget.offset_list[_id])
	end
	return offset
end

---- скрипт устанавливаемый в объект, для инициализации его взяти при клике
function hog_widget.HogTake()
	-- увеличиваем число верных ходов
	hog_widget.moves_count = hog_widget.moves_count + 1
	local current_object = g_QuestState.GetScriptObject() 
	if not current_object then return end

	local object_id = current_object.GetId().c_str()
	if object_id == hog_widget._tutorial_object_hider then
		quest.RemoveObject(hog_widget._tutorial_object)
	end
	
	-- если при взятии мы взяли эффект на который подсказывал хинт то прячем эффект
	if object_id == hog_widget._hint_object then
		hog_widget.KillHint()
	end
	
	local offset = hog_widget._GetObjectOffset(object_id)
	
	hog_widget.hint_list[object_id] = nil
	
	local hog_object_name = hog_widget._GetHogObjectName(object_id)
	local hog_object, hog_object_index = hog_widget._GetHogObjectAndIndexById(hog_object_name)
	if not hog_object then
		__message("HogTake. Unknown hog object: "..hog_object_name)
		return 
	end

	local taked_object_pos = quest.get_object_pos(object_id)

	local new_take_effect = hog_widget._CreateTakeEffect()
	local take_time = new_take_effect:Init(current_object, 
	                                       hog_widget._timer.Get().GetTime(),
										   taked_object_pos,
										   sf.misc.FloatVector(hog_object_index * hog_widget._cell_size, 0) + hog_widget._draw_offset,
										   offset)
	hog_widget._InsertEffect(new_take_effect)	

	current_object.SetSize(__cast(current_object, qe.CObject).GetSourceSize() )
	quest.RemoveObject(object_id)			
	--sleep(take_time)
end

--- анализа id на наличиие суффиксов hog и получение имени объекта частью которого этот объект является
function hog_widget._AddTakedObject(_object)
	if not _object then
		__message("hog_widget._AddTakedObject. Empty _object.")
		return
	end
	local hog_object_name = hog_widget._GetHogObjectName(_object.GetId().c_str())
	local hog_object = hog_widget._GetHogObjectAndIndexById(hog_object_name)
	if not hog_object then
		__message("hog_widget._AddTakedObject. Unknown hog object id. ")
		return
	end
	table.insert(hog_object.taked_obj, _object)
end

--- получение id хог объекта по id его части ли подложки
function hog_widget._GetHogObjectName(_full_id, _post_fix)
	if _post_fix == nil or type(_post_fix) ~= "string" then
		_post_fix = "#hog"
	end
	local hog_marker_index = string.find(_full_id, _post_fix)
	if hog_marker_index then
		return string.sub(_full_id, 1, hog_marker_index-1)
	end
	return nil
end

--- получение хог объекта и его индекса в списке объектов по id
function hog_widget._GetHogObjectAndIndexById(_hog_object_id)
	for i,o in ipairs(hog_widget._objects_table) do
		if o.id == _hog_object_id then
			return o, i
		end
	end
	return nil, nil
end

--- инициализация хога
function hog_widget.InitHog()
	
	local hog_info = g_App.GetSettings().GetChild("Constants", false).GetChildRef("hog_options", false)
	if hog_info then
		hog_widget._hint_show_time = tonumber(hog_info.GetValue("hint_show_time").c_str()) or hog_widget._hint_show_time
		hog_widget._fly_speed = tonumber(hog_info.GetValue("object_fly_speed").c_str()) or hog_widget._fly_speed
	end

	hog_widget._hint_show_delay = 0
	hog_widget._hint_effect = nil
	
	hog_widget._hint_object = nil
	
	hog_widget.hint_list = {}
	hog_widget.offset_list = {}
	
	hog_widget.total_list = {} -- список всех объектов для различных проверок

	local parent_window = g_QuestState.GetParentWindow()
	if not parent_window then
		__message("Hog widget. Parent window for quest not found. Can not init hog widget.")
		return 
	end
	hog_widget._self = get_child_widget(parent_window, "hog_widget", sf.gui.CLuaWidget) 
	if not hog_widget._self then
		__message("Hog widget not found.")
		return 
	end

	hog_widget._timer =  sf.core.CTimerWrapper(sf.core.g_Application.GetTimeManager().AttachTimer("", g_QuestState._level_timer.Get()))

	local current_main_scene = g_QuestState.GetCurrentScene()
	__assert(current_main_scene, "hog_widget.InitHog. Current scenme not found.")
	local offset_groups = {}
	local current_child = current_main_scene.GetObjects()
	-- обходим объекты в корне и ищем причастных к хогу
	while not current_child.IsEnd() do
		local object = current_child.Get()
		local object_id  = object.GetId().c_str()
		
		local hog_object_id = hog_widget._GetHogObjectName(object_id)
		--объект участвует в хоге
		if hog_object_id then 
			
			--- добавление нового объекта в список объектов хога
			local hog_object = hog_widget._GetHogObjectAndIndexById(hog_object_id)
			--- если нет объекта, то создаем новый
			if not hog_object then 
				hog_object = {}
				--- список всех частей объекта
				hog_object.id = hog_object_id
				hog_object.total_obj = {}
				--- список взятых частей объекта
				hog_object.taked_obj = {}
				table.insert(hog_widget._objects_table, hog_object)
			end
			
			
			--- проверяем не является ли этот объект подложкой объекта
			local hog_back_marker_index = string.find(object_id, "#hog_back")
			if hog_back_marker_index then
				--- елси да то смотрим не установленна ли уже для этого объекта подложка
				if not hog_object.back_texture then
					--- если нет то устанавливаем ее
					hog_object.back_texture = object
				else
					--- если да то ругаемся что подложки для объекта целых две
					__message("Two back objects with same names: "..hog_object_id)
				end
			else
				---- если объект не подложка
				---- смотрим не добавлена ли уже в объект часть с таким же id
				local find_same_id = false
				for _,o in pairs(hog_object.total_obj) do
					if o.GetId().c_str() == object_id then
						find_same_id = true
						break
					end
				end
				if find_same_id then
					---- если объект с таким id уже добавлен, то ругаемся
					__message("Duplicate object id: "..object_id)
				else
				--- если не добавлена, то устанавливаем скрипт для этой части объекта
					object.SetLuaScript("hog_widget.HogTake()", qe.CBaseSceneObject.ScriptMouseClick)
					--- береме его в общий список частей объекта
					table.insert(hog_object.total_obj, object)
					hog_widget.total_list[object_id] = object
					---- если часть удалена(уже взята), то берем ее в список взятых частей
					if object.ReadObjectFlag(qe.CBaseSceneObject.ObjectFlagRemoved) then
						table.insert(hog_object.taked_obj, object)
					else
						-- если вещь еще не взята то добавляем ее в список хинтов
						hog_widget.hint_list [object_id] = object
					end
				end
			end
		else
			local group = __cast(object, qe.CSceneGroup)
			if group then
				local hog_object_id = hog_widget._GetHogObjectName(object_id, "#offset")
				if hog_object_id then
					offset_groups[hog_object_id] = group
				end
			end
		end
		current_child.Next()
	end   
	-- загрузка смещенией объектов
	for hog_object_id, offset_group in pairs(offset_groups) do
		local group_offset = offset_group.GetPos()
		local current_child =  offset_group.GetChilds() 
		while not current_child.IsEnd() do 
			local child_id = hog_object_id.."#"..current_child.Get().GetId().c_str()
			if not hog_widget.total_list[child_id] then
				__message("try set offset for unknown object: "..child_id)
			end
			if hog_widget.offset_list[child_id] then
				__message("try change offset for object: "..child_id)
			end
			local child_offset = current_child.Get().GetPos() - group_offset
			hog_widget.offset_list[child_id] = child_offset
			current_child.Next()
		end
	end
	local offsets_empty_list_text = ""
	for id,obj in pairs(hog_widget.total_list) do
		if not hog_widget.offset_list[id] then
			offsets_empty_list_text = offsets_empty_list_text.." "..id
		end
	end
	if offsets_empty_list_text ~= "" then
		__message("Offsets unknown for objects: "..offsets_empty_list_text)
	end
	
	hog_widget._CheckCompleted()
end

function hog_widget.DoUpdate(_this)
	--- обновляем список эффектов
	-- апдейт для времени жизни эффекта хинта
	if hog_widget._hint_show_delay > 0 then
		local frame_delta = hog_widget._timer.Get().GetFrameDelta()
		hog_widget._hint_show_delay = hog_widget._hint_show_delay - frame_delta
		if hog_widget._hint_show_delay <=0 then
			if hog_widget._hint_effect then
				hog_widget.KillHint()
			end
		end
	end
	
    if hog_widget._take_effects_list ~= nil then
		local current_time = hog_widget._timer.Get().GetTime()
        local size = #(hog_widget._take_effects_list)
        if size ~= 0 then
            local i = 1
            while i <= size do
                hog_widget._take_effects_list[i]:Update(current_time)
                if hog_widget._take_effects_list[i]:IsDead() then
                    table.remove(hog_widget._take_effects_list, i)
                    size = size - 1
                    i = i - 1
                end
                i = i + 1
            end
        end
    end	

	hog_widget.visual_effects:Update()
	return false
end

function hog_widget.DoDraw(_this, _renderer)
	--- рисуем подложки и взятые обхекты
	for i,o in 	ipairs(hog_widget._objects_table) do
		_renderer.PushState()
		_renderer.ApplyMatrix( sf.misc.MatrixTranslation(i * hog_widget._cell_size + hog_widget._draw_offset.X, hog_widget._draw_offset.Y))
		if o.back_texture then
			o.back_texture.Draw(_renderer)
		end
		for _,taked_obj in pairs(o.taked_obj) do
			taked_obj.Draw(_renderer)
		end	
		_renderer.PopState()
	end	
	hog_widget.visual_effects:PreDraw(_renderer)
	
	--- рисуем эффекты
    if hog_widget._take_effects_list ~= nil then
		for _,o in ipairs(hog_widget._take_effects_list) do
			o:Draw(_renderer)
		end
    end
	hog_widget.visual_effects:PostDraw(_renderer)
	
    return false
end

function hog_widget.Load(_this, _info, _templates)
	hog_widget.visual_effects = visual_effects.CreateEffectsList()
end

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

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

function hog_widget.OnMouseDown(_this, _pos, _button, _state, _broadcast)
	-- подсчет общзего числа кликов
	if _broadcast == false then
		hog_widget.total_click_count = hog_widget.total_click_count + 1
	end
	return false
end

function hog_widget.OnDoubleClick(_this, _pos, _state, _broadcast)
    return true
end

--- простая функция для расчета позиции летящего объекта
function hog_widget._FlyFunction( _start_pos, _end_pos, _t)
    return _start_pos + (_end_pos - _start_pos)*_t
end

--- создание эффекта взятия части хог-объекта
function hog_widget._CreateTakeEffect()
    local new_take_effect = 
    {
        Init = function(_self, _object, _start_time, _start_pos, _target_pos, _effect_offset )
            
			_self._effect_offset = _effect_offset
			_self._object = _object
			
            _self._start_pos = _start_pos
            _self._fly_obj_pos = _start_pos
			_self._target_pos = _target_pos

			_self._total_time = ((sf.misc.CalculateFloatVectorModule(_start_pos - _target_pos))/hog_widget._fly_speed)*1000
			_self._start_time = _start_time
            _self._end_time = _self._start_time + _self._total_time
			_self._particle_effect = hog_widget.visual_effects:CreateEffect("HogObjectPartFly", _start_pos  - _self._effect_offset)            
            return _self._total_time
        end,

        Draw = function(_self, _renderer)
			if not _self._fly_obj_pos then return end
			_renderer.PushState()
			_renderer.ApplyMatrix( sf.misc.MatrixTranslation(_self._fly_obj_pos.X , _self._fly_obj_pos.Y))
			_self._object.Draw(_renderer)
			_renderer.PopState()
        end,

        Update = function(_self, _time)
            local koef = (_time - _self._start_time)/_self._total_time
            _self._fly_obj_pos = hog_widget._FlyFunction( _self._start_pos, _self._target_pos, koef)
			_self._particle_effect:SetPos(_self._fly_obj_pos - _self._effect_offset)
            if _time > _self._end_time then
                _self:InstantApply()
            end
        end,

        InstantApply = function(_self)
            if _self._is_dead then return end
			hog_widget.visual_effects:CreateInstantEffect("HogObjectPartHit", _self._target_pos - _self._effect_offset)
			_self._particle_effect:Kill(false)
			hog_widget._AddTakedObject(_self._object)
			hog_widget._CheckCompleted()
            _self._is_dead = true
        end,
		
		IsDead = function(_self)
			return _self._is_dead
		end,
        
        _is_dead = false,
    }
    return new_take_effect
end

