-- common functions


require "pluto"

pluto_s = {}

pluto_s.permtable = { 1234 } 
pluto_s.perms = { [coroutine.yield] = 1, [pluto_s.permtable] = 2 }

----------------------------------------------------------------------------------------------------
function pluto_s:serialize_table(tbl)
	return pluto.persist(pluto_s.perms , tbl)
end

function pluto_s:unserialize_table(buf)
	return pluto.unpersist(pluto_s.perms , buf)
end

function pluto_s:save_table(tbl, filename)
	local buf = pluto.persist(pluto_s.perms , tbl)
	local outfile = io.open(filename, "wb")
	outfile:write(buf)
	outfile:close()
end

----------------------------------------------------------------------------------------------------
function pluto_s:load_table(filename)

	local infile, err = io.open(filename, "rb")
	if infile == nil then return nil end
	local buf, err = infile:read("*a")
	infile:close()                                       
    if buf == "" then return nil end
	return pluto.unpersist(pluto_s.perms , buf)

end

----------------------------------------------------------------------------------------------------
function table.copy(t)
  if t == nil then return nil end
  local t2 = {}
  for k,v in pairs(t) do
    t2[k] = v
  end
  return t2
end

----------------------------------------------------------------------------------------------------
function common_event_listeners.to_inventory(name)
	add_element_to_inventory(name, true)
	mouse:overElement("default_cursor")
end

----------------------------------------------------------------------------------------------------
function find_element(clip_name)
	
	log_message("find_element is OBSOLETE !!")
	
	return level_mgr:find_element(clip_name)
end

----------------------------------------------------------------------------------------------------
function find_character(name)

	log_message("find_character is OBSOLETE !!")
	
	local el = find_element(name)
	
	if el == nil then
		return nil
	else
		return el:getCharacter()
	end

end

----------------------------------------------------------------------------------------------------
-- params: { recursive, looped, frame_begin, frame_end, label_begin, label_end }
function play_clip(ch, params)
		
	if ch ~= nil then	
		ch:goto_frame(0)
		
		if params ~= nil and (params.label_begin ~= nil or params.label_end ~= nil) then
			if params.label_begin ~= nil then
				params.frame_begin = ch:get_labeled_frame(params.label_begin)
				--if params.frame_begin == nil or params.frame_end == nil then
				--local k = 0
			    --end
			    assert(params.frame_begin ~= nil)
			end
			
			if params.label_end ~= nil then
				params.frame_end = ch:get_labeled_frame(params.label_end)
			end
			
		end
		
		if params == nil or params.frame_begin == nil or params.frame_end == nil then
			ch:set_frames_for_play(0, -1)
		else
			ch:set_frames_for_play(params.frame_begin, params.frame_end)
		end		
		
		if params ~= nil and params.looped == true then
			ch:set_looped(-1, true)
		else
			ch:set_looped(0, false)
		end
		
		ch:set_visible(true)
		
		ch:set_play_state(0, (params ~= nil and params.recursive ~= nil))		
		
	end
	
end

----------------------------------------------------------------------------------------------------
function draw_text_at(elem, txt)
	if elem ~= nil then
		local pos = elem:getPos()
		timer_mgr:add_timer(function() draw_text(pos.x, pos.y - 30, txt) end, 0, 100)
	end
end

----------------------------------------------------------------------------------------------------
function split_name(full_name)

	local be, en = string.find(full_name, "%$")
	if  be == nil then
		return full_name, nil
	else
		return string.sub(full_name, 1, be - 1), string.sub(full_name, be + 1, #full_name)
	end

end

----------------------------------------------------------------------------------------------------
ee_handler = {} -- elements event handler
ee_handler.handlers = {}

----------------------------------------------------------------------------------------------------
function ee_handler:event(el_name, eh_type)

--    trace('ee_handler:event('..el_name..', '..eh_type..')');
    --if eh_type == eiLeftClick then
    --    decoda_output('ee_handler:event('..el_name..', '..eh_type..')');
    --end

	local h_index = ee_handler:get_handler_index(el_name, eh_type)
	
	if h_index ~= 0 and ee_handler.handlers[h_index].func ~= nil then
		ee_handler.handlers[h_index].func()	
	end

end

----------------------------------------------------------------------------------------------------
function ee_handler:get_handler_index(el_name, eh_type)

	for i = 1, #self.handlers do
		if self.handlers[i].el_name == el_name and self.handlers[i].type == eh_type then
			return i
		end	
	end
	
	return 0
	
end

----------------------------------------------------------------------------------------------------
function get_unique_element_name(elem)
    return elem:getName()..'_'..string.gsub(tostring(elem), 'userdata: ', '') -- ÍÅ ÏÅÐÅÎÏÐÅÄÅËßÒÜ __tostring() ó ýëåìåíòà!!!
end

----------------------------------------------------------------------------------------------------
function ee_handler:add_handler(element, fn, eh_type)

	if element == nil then
		return
	end

    local id = get_unique_element_name(element)

    --trace('adding '..eh_type..' handler to '..id)

	local h_index = ee_handler:get_handler_index(id, eh_type)
	
	if fn == nil and h_index ~= 0 then
		table.remove(ee_handler.handlers, h_index)
		return
	end
	
	if h_index ~= 0 then -- maybe just change, not remove
		table.remove(ee_handler.handlers, h_index)
	end
	
	table.insert(self.handlers, {elem = element, el_name = id, func = fn, type = eh_type})
	element:setEventHandler(eh_type, "ee_handler:event('"..id.."', "..eh_type..")")
	element:setActive(true)

end

----------------------------------------------------------------------------------------------------
mouse_roll = {}

----------------------------------------------------------------------------------------------------
-- params - over, with_element
function mouse_roll:add(element, params)

	if element == nil then
		return
	end

	local onOver = "gear"
	local onOut = "default"
	
	if params ~= nil then
	
		if params.over ~= nil then
			onOver = params.over
		end
		
	end
	
	element:setActive(true)
	
	if params ~= nil and params.with_element ~= nil then
		element:setEventHandler(eiRollOver, "mouse_roll:handle_roll(\""..params.with_element.."\", \""..onOver.."\")")
	else
		element:setEventHandler(eiRollOver, "mouse:setCursorFrame(\""..onOver.."\")")
	end
	
	element:setEventHandler(eiRollOut, "mouse:setCursorFrame(\""..onOut.."\")")
	
end

----------------------------------------------------------------------------------------------------
function mouse_roll:handle_roll(mouse_el_name, frame_on_true, frame_on_false)
	
	if mouse:getConnectedName() == mouse_el_name then
		mouse:setCursorFrame(frame_on_true)
	else
		if frame_on_false ~= nil then
			mouse:setCursorFrame(frame_on_false)
		else
			mouse:setCursorFrame("default")
		end
	end

end

----------------------------------------------------------------------------------------------------
function mouse_roll:remove(element, params)

	element:setEventHandler(eiRollOver, "")
	element:setEventHandler(eiRollOut, "")

end

----------------------------------------------------------------------------------------------------
render_layer = 
{
	parent = 0,
	name = ""
}

----------------------------------------------------------------------------------------------------
function __rl_pr_el(elem) console:print(elem:getName()) end

function render_layer:print_elements(parent_element)
	console:print("------- "..self.parent:getName().." elements:")
	console:print(" ")
	
	self.parent:processElements("__rl_pr_el", parent_element)
end

----------------------------------------------------------------------------------------------------
-- params: see play_clip
function render_layer:play_clip(clip_name, params)

	local el = self:get_element(clip_name)
	
	if el ~= nil then
		play_clip(el:getCharacter(), params)
	end
	
end

----------------------------------------------------------------------------------------------------
function render_layer:add_roll(el_name, params)
	mouse_roll:add(self:get_element(el_name), params)
end

----------------------------------------------------------------------------------------------------
function render_layer:remove_roll(el_name, params)
	mouse_roll:remove(self:get_element(el_name), params)
end

----------------------------------------------------------------------------------------------------
-- params - eh_type, not_activate
function render_layer:set_element_eh(name, func, params)

	log_message("render_layer:set_element_eh "..name)
	local el = self.parent:getElement(name)
	
	if el == nil then
		return
	end
	
	if params == nil or params.not_activate == nil then
		el:setActive(true)
	end
	
	local eh_type = eiLeftClick
	if params ~= nil and params.eh_type ~= nil then
		eh_type = params.eh_type
	end
	
	ee_handler:add_handler(el, func, eh_type)
end

----------------------------------------------------------------------------------------------------
-- params - eh_type, not_activate
function render_layer:_set_element_eh(elem, func, params)

	log_message("render_layer:set_element_eh "..elem:getName())
	
	if params == nil or params.not_activate == nil then
		elem:setActive(true)
	end
	
	local eh_type = eiLeftClick
	if params ~= nil and params.eh_type ~= nil then
		eh_type = params.eh_type
	end
	
	ee_handler:add_handler(elem, func, eh_type)
end

----------------------------------------------------------------------------------------------------
function render_layer:process_elements(func_name, parent_element)

	if parent_element == nil then
		self.parent:processElements(func_name, nil)
	else
		self.parent:processElements(func_name, parent_element)
	end

end

----------------------------------------------------------------------------------------------------
function render_layer:new(layer_name, needToInit)
	local obj = {}
	setmetatable(obj, self)
	self.__index = self

	local layer = new_layer(layer_name)
	
	if needToInit ~= nil or needToInit == true then
		layer:init()
	end

	obj:set_name(layer_name)
	obj:set_parent(layer)
	obj:create_particles_layer()

	
	return obj	
end

----------------------------------------------------------------------------------------------------
function render_layer:get_element_child(elem, childName, isFullTree)
    if isFullTree == nil then
        isFullTree = false
    end
    
    local count = self:get_element_count()
    for i = 0, count - 1 do
        local child = self:get_element_at(i)
        local name = child:getName()
        local isChild = elem:isParentOf(child, isFullTree)
        if child:getName() == childName and isChild then
            return child
        end
    end
end

----------------------------------------------------------------------------------------------------
function render_layer:create_particles_layer()
	self.p_layer = new_layer_particles(self:get_name().."particles")
end

----------------------------------------------------------------------------------------------------
function render_layer:add_effect(effect_name, pos_or_name, only_one)
	
	if only_one == nil then
		only_one = true
	end
	
	if self.p_layer ~= nil then
		if type(pos_or_name) == "string" then
			local el = self:get_element(pos_or_name)
			if el ~= nil then
				local el_pos = self:get_element(pos_or_name):getPos(true)
				self.p_layer:addEffect(el_pos.x, el_pos.y, effect_name, only_one)
			end
		else
			self.p_layer:addEffect(pos_or_name.x, pos_or_name.y, effect_name, only_one)
		end
	end
end

----------------------------------------------------------------------------------------------------
function render_layer:link_effect(effect_name, element_name)
	
	if self.p_layer ~= nil then
		local el = self:get_element(element_name)
		if el ~= nil then
			self.p_layer:linkEffect(effect_name, el:getCharacter())
		end
	end

end

----------------------------------------------------------------------------------------------------
function render_layer:unlink_effect(element_name)
	
	if self.p_layer ~= nil then
		local el = self:get_element(element_name)
		if el ~= nil then
			self.p_layer:removeLinkedEffect(el:getCharacter())
		end
	end

end


----------------------------------------------------------------------------------------------------
function render_layer:stop_linked_effect(element_name)

	if self.p_layer ~= nil then
		local el = self:get_element(element_name)
		if el ~= nil then
			self.p_layer:stopLinkedEffect(el:getCharacter())
		end
	end

end

----------------------------------------------------------------------------------------------------
function render_layer:stop_effects()
	self.p_layer:stopEffects()
end

----------------------------------------------------------------------------------------------------
function render_layer:start_amb_effects() 
    local size = self.parent:getElementCount()
    for i = 0, size-1 do
        local element = self.parent:getElementAt(i)
        if element then
            local effect = element:getMember("effect")
            if effect then
                local type = element:getMember("type")
                self.p_layer:linkEffect(effect,element:getCharacter(), (type == 'shaple'))
            end
        end
    end
end

----------------------------------------------------------------------------------------------------
function render_layer:stop_amb_effects() 
    local size = self.parent:getElementCount()
    for i = 0, size-1 do
        local element = self.parent:getElementAt(i)
        if element then
            local effect = element:getMember("effect")
            if effect then
                self.p_layer:removeLinkedEffect(element:getCharacter())
            end
        end
    end
end

----------------------------------------------------------------------------------------------------
function render_layer:set_parent(parentLayer) self.parent = parentLayer end
function render_layer:get_parent() return self.parent end
function render_layer:set_name(name) self.name = name end
function render_layer:get_name() return self.name end
function render_layer:set_visible(val) self.parent:setVisible(val) end
function render_layer:set_active(val) self.parent:setActive(val) end
function render_layer:get_element(name) return self.parent:getElement(name) end
function render_layer:get_element_at(index) return self.parent:getElementAt(index) end
function render_layer:get_element_count() return self.parent:getElementCount() end

--function render_layer:start_amb_effects() return self.parent:startAmbEffects() end
--function render_layer:stop_amb_effects() return self.parent:stopAmbEffects() end

----------------------------------------------------------------------------------------------------
-- timer manager

timer_mgr = {}
timer_mgr.timers = {}
timer_mgr.current_updated_timer = 0

----------------------------------------------------------------------------------------------------
function timer_mgr:save()
	pluto_s:save_table(self, "timer_mgr.lll")
end

----------------------------------------------------------------------------------------------------
function timer_mgr:load()
	timer_mgr = pluto_s:load_table("timer_mgr.lll")
end

----------------------------------------------------------------------------------------------------
function timer_mgr:add_timer(func_, t_interval_, count_, name_id_)
	if count_ == nil then
		count_ = -1
	end
	
	if name_id_ ~= nil then
		timer_mgr:remove_timer(name_id_)
	end
	
	table.insert(timer_mgr.timers, {func = func_, t_interval = t_interval_, count = count_, name_id = name_id_, cur_time = 0})	
end

----------------------------------------------------------------------------------------------------
function timer_mgr:remove_timer(name_id_)
	for i = 1, #timer_mgr.timers do
		if i > #timer_mgr.timers then
			break
		end		

		while timer_mgr.timers[i].name_id == name_id_ do
			table.remove(timer_mgr.timers, i)
--			log_message("remove timer by id "..name_id_)
			if i > #timer_mgr.timers then
				break
			end
		end
	end
end

----------------------------------------------------------------------------------------------------
function timer_mgr:remove_current_timer()
	
	timer_mgr.timers[current_updated_timer].need_remove = true	

end

----------------------------------------------------------------------------------------------------
function timer_mgr:is_timer_exist(name_id)
	for i = 1, #timer_mgr.timers do
		if timer_mgr.timers[i].name_id == name_id then
			return true
		end
	end
	
	return false
end

----------------------------------------------------------------------------------------------------
function timer_mgr:update(dt)

	local need_second_pass = false
	
	for i = 1, #timer_mgr.timers do
		current_updated_timer = i
		timer_mgr.timers[i].cur_time = timer_mgr.timers[i].cur_time + dt
		
		if (timer_mgr.timers[i].cur_time >= timer_mgr.timers[i].t_interval) then
			timer_mgr.timers[i].func()
			timer_mgr.timers[i].cur_time = 0
			
			if timer_mgr.timers[i].count > 0 then
				timer_mgr.timers[i].count = timer_mgr.timers[i].count - 1
				if timer_mgr.timers[i].count == 0 then
					need_second_pass = true
				end
			end
			
			if timer_mgr.timers[i].need_remove ~= nil then
				log_message("something was deleted")
				need_second_pass = true
			end
			
		end
	end
	
	if need_second_pass == true then
		
		for i = 1, #timer_mgr.timers do
			if i > #timer_mgr.timers then
				break
			end		

			while timer_mgr.timers[i].count == 0 or timer_mgr.timers[i].need_remove ~= nil do
				if timer_mgr.timers[i].name_id ~= nil then
					log_message("remove timer "..timer_mgr.timers[i].name_id)
				else
					log_message("remove timer")
				end
				
				table.remove(timer_mgr.timers, i)
				
				if i > #timer_mgr.timers then
					break
				end
			end
		end
	end

end

----------------------------------------------------------------------------------------------------
-- level flow

----------------------------------------------------------------------------------------------------
level_flow =
{
	funcs = {},
	index = 1,
	index_state_changed = 0,
	state = 0,
	waiting_state = 1,
	need_exec = true
}

----------------------------------------------------------------------------------------------------
function level_flow:new()
	obj = {}
	setmetatable(obj, self)
	self.__index = self
	
	obj.funcs = {}
	obj.index = 1
	
	obj.state = 0
	obj.waiting_state = 1
	obj.need_exec = true
	obj.index_state_changed = 0	
		
	return obj
end

----------------------------------------------------------------------------------------------------
function level_flow:add(func)
	table.insert(self.funcs, func)
end

----------------------------------------------------------------------------------------------------
function level_flow:add_state_check(state)
	table.insert(self.funcs, function() if self.state == state then return 1 else return 0 end end)
end

----------------------------------------------------------------------------------------------------
function level_flow:wait_state(state)
	table.insert(self.funcs, function() if self.state == state then return 1 else return 0 end end)
end

----------------------------------------------------------------------------------------------------
function level_flow:wait_next_state()
	level_flow:wait_state(level_flow:get_state() + 1)
end

----------------------------------------------------------------------------------------------------
function level_flow:set_state(st)
	if self.state > st then
		log_message("SETTING PREVIOUSE STATE IN LEVEL FLOW !")
	end
	
	self.state = st
end

----------------------------------------------------------------------------------------------------
function level_flow:set_next_state()

	if self.index_state_changed == self.index then -- already changed state at this index
		return
	end
	
	self.index_state_change = self.index	
	self:set_state(self.state + 1)
end

----------------------------------------------------------------------------------------------------
function level_flow:savepoint()

	--table.insert(self.funcs, function() end)
	
end

----------------------------------------------------------------------------------------------------
function level_flow:get_state()
	return self.state
end

----------------------------------------------------------------------------------------------------
level_flow_debug = true

function level_flow:update()
	--[[
	if self.index <= #self.funcs and self.funcs[self.index]() == 1 then
		
		if level_flow_debug == true then
			log_message("Exec next func in flow "..self.index)
		end
		
		self.index = self.index + 1
	end
	--]]

	if self.need_exec == true then
		
		if level_flow_debug == true then
			log_message("Exec next func in flow "..self.index)
		end
		
		if self.funcs[self.index]() == 1 then
			self.need_exec = false
		end
	end
	
	if self.index <= #self.funcs and self.state == self.waiting_state then
		
		self.index = self.index + 1
		self.waiting_state = self.waiting_state + 1
		self.need_exec = true		
		
	end
end

----------------------------------------------------------------------------------------------------
function test_timer11()
	
	timer_mgr:add_timer(
	function()
	
			log_message("timer_outer")
			bedroom_scene:get_element("branches_room_anim"):setAlpha(1)
			bedroom_scene:play_clip("bg_light_anim")
			
			timer_mgr:add_timer(function()
				
				log_message("timer_inner")
				bedroom_scene:get_element("branches_room_anim"):setAlpha(bedroom_scene:get_element("branches_room_anim"):getAlpha() - 1/15)
				end, 0, 15, "lalala")
			
			bedroom_scene:set_element_eh("light_turn_on", nil)		
			
	end, 2, 3)

end

----------------------------------------------------------------------------------------------------
render_element =
{
	name = ""
	
	
}

----------------------------------------------------------------------------------------------------

render_element.mt = {}

render_element.mt.__index = 
function(t, k)
	if k == "elem_obj" then
		log_message(k)
		log_message("ASSIGNING !")
		t.elem_obj = "NEW VALUE"
		return "!!!!!!!!!"
	end
end

render_element.mt._persist =
function(tbl)
	return 0
end

setmetatable(render_element, render_element.mt)

----------------------------------------------------------------------------------------------------
function render_element:new(name)

	local re1 = {name = name}
	setmetatable(re1, self)
	self.__index = self
	--self.__persist = self
	
	return re1
end

----------------------------------------------------------------------------------------------------
--params - loop, stream : true\false , group : 0,1,2... , volume: [0..1]
function play_sound(name, params)
	
	params = params or {}

	if params.loop == nil then
		params.loop = false
	end

	if params.stream == nil then
		params.stream = false
	end

	if params.group == nil then
		params.group = group_effect
	end

	if params.volume == nil then
		params.volume = 1
	end
	
	--if sound_props.volume[name] ~= nil then
		--params.volume = params.volume * sound_props.volume[name]
	--end

	media_mgr():playSound(name..".ogg", params.loop, params.volume, params.stream, params.group, params.callback)

end

----------------------------------------------------------------------------------------------------
function stop_sound(name)
    media_mgr():stopSound(name..'.ogg')
end

----------------------------------------------------------------------------------------------------
function clone_table(source)
    local copy = {}
    local i, v = next(source, nil)
    while i do

        local vtype = type(v)
        if vtype == 'table' then
            copy[i] = clone_table(v)
        else
            copy[i] = v
        end
        i, v = next(source, i)
    end

    return copy
end
