--[[
Миниигра - убей тараканов
]]
beat_cockroach = {} 
local self = beat_cockroach 

self.params = {
	max_interval = 2000, -- максимальный промежуток между появлениями объектов
	min_interval = 600, -- минимальный промежуток между появлениями объектов
	max_objects = 5, -- максимальное количество объектов на сцене
	
	progress_seconds = 30, -- сколько дается времени, секунды
	progress_click = 1, -- на сколько уменьшается время при неверном клике, секунды
	progress_blink = 10, -- при каком значении прогрессбар начинает мигать
	progress_blink_speed = 300, -- мигает раз в 300 мс
	blink_color = sf.graphics.Color(255, 255, 0, 0), -- цвет подкрашивания при мигании
	hide_upper_time = 1, -- прятать верхнюю полосочку, когда останется секунд
	
	bug_body_id = "clip bug01", -- id объекта внутри клипа, по которому надо кликать. выставляется в клипмейкере. 
	
	delay_show_beated = 250, -- время, за которое обычный таракан переходит с альфой в состояние с качающейся башкой, мс
	delay_after_click = 200, -- время качания головой, мс
	delay_for_alpha = 200, -- время, за которое клип с качающим головой переходит в таракана на спине, мс
	delay_after_lose = 50, -- время затемнения экрана при поражении, мс
	speed = 8, -- скорость, с которой тараканы летят в панель внизу, пикселей в секунду
	
	objects_layer = 1, -- слой, на котором появляются тараканы
	progressbar_layer = 3, -- слой, на котором прогрессбар
	
	panel_success = 
	{ -- панель, на которой складируются тараканы
		left_angle = sf.misc.FloatVector(0, 505), -- левый верхний угол
		width = 700, -- ширина в пикселях
		objects = 10 -- сколько входит объектов ( = сколько надо убить на уровне )
	},
	
	sound_click = "mini_game_mini_game_beat_cockroach_true_01", -- звук при клике
	sound_click_empty = "mini_game_mini_game_birds_and_fishes_false_01", -- клик мимо
	sound_danger = "mini_game_mini_game_beat_cockroach_danger_01", -- когда остается мало времени
	sound_run_away = "mini_game_mini_game_beat_cockroach_false_01", -- звук при проигрыше
	sound_fade_time = 600, -- время затухания звуков
	
	click_clip = "", --  клип для клика
	objects = 
	{ -- виды таракашек
		{ -- 1
			look_out = "bug01_01", -- клип, где выглядывает
			run_away = "bug02_01", -- убегает
			run_left = true, -- убегает слева
			beated = "bug02", -- ударенный качает головой
			on_back = "bug03" -- на спине
		}, 
		{ -- 2
			look_out = "bug01_01", -- клип, где выглядывает
			run_away = "bug02_02", -- убегает
			run_left = false, -- убегает справа
			beated = "bug02", -- ударенный качает головой
			on_back = "bug03" -- на спине
		},
		{ -- 3
			look_out = "bug01_01", -- клип, где выглядывает
			run_away = "bug02_03", -- убегает
			run_left = true, -- убегает слева
			beated = "bug02", -- ударенный качает головой
			on_back = "bug04" -- на спине
		},
		{ -- 4
			look_out = "bug01_01", -- клип, где выглядывает
			run_away = "bug02_04", -- убегает
			run_left = false, -- убегает справа
			beated = "bug02", -- ударенный качает головой
			on_back = "bug04" -- на спине
		}
	},
	cursor = "gui_cursors_slipper", -- курсор 
	cursor_size = sf.misc.FloatVector(20, 20), -- активный прямоугольник вокруг центра курсора
	positions = { -- возможные координаты появления тараканов
		sf.misc.FloatVector(175, 568), -- 1
		sf.misc.FloatVector(678, 568), -- 2
		sf.misc.FloatVector(630, 378), -- 3
		sf.misc.FloatVector(175, 378), -- 4
		sf.misc.FloatVector(242, 116), -- 5
		sf.misc.FloatVector(550, 180), -- 6
		sf.misc.FloatVector(400, 378) -- 7
	}
}

function self.Init(_owner) 
	sminigames.SetStandartScriptName(_owner, beat_cockroach) 
	
	self.pos_amount = #self.params.positions -- количество позиций, в которых может появиться пламя
	self.timer = __create_timer("", "qe_level") -- создаем таймер
	
	self.darkness = __cast(_owner.GetWidget("darkness", true), sf.gui.CImageWidget)
	self.progress_upper = __cast(_owner.GetWidget("progressbar_up", true), sf.gui.CImageWidget)
	local img = __cast(_owner.GetWidget("progressbar", true), sf.gui.CImageWidget)
	__assert("Нет виджета для отображения прогресса: image_widget, id = progressbar")
	local layer = self.params.progressbar_layer or 1
	img.SetLayer(layer)
	if self.progress_upper then
		self.progress_upper.SetLayer(layer)
	end
	
	local img1 = __cast(_owner.GetWidget("progress_back", true), sf.gui.CImageWidget)
	if img1 then 
		img1.SetLayer(layer)
	end
	
	self.progressbar = 
	{
		image = img, 
		init_size = {X = img.GetSize().X, Y = img.GetSize().Y}
	}
	self.offset_bottom = self.progressbar.image.GetOffset().Y + self.progressbar.image.GetSize().Y
	
	self.Load(_owner)	
end

-- запускает при поражении и при инициализации
function self.Load(_owner)
	self.pos_state = {} -- массив отметок, есть ли таракан в этой позиции или нет
	for i = 1, #self.params.positions do
		table.insert(self.pos_state, 0)
	end
	
	self.bugs = self.AllBugs(_owner) -- создали огонь
	self.progress = self.params.progress_seconds*1000
	
	self.clicks = {} -- виджеты с кликом

	self.SetProgress()
	
	self.last_update = self.timer.Get().GetTime()
	
	if self.darkness then
		self.darkness.AddFlags(sf.gui.CWidget.FlagHidden)
	end
	
	self.beated = 
	{-- здесь инфа о прогрессе игры
		amount = 0, -- количество убито
		bugs = {} -- жуки в панели и жуки, которые скоро туда попадут
	} 
	self.progress_upper.RemFlags(sf.gui.CWidget.FlagHidden)
end
-- обновление прогрессбара
function self.UpdateProgress(_owner)
	local now = self.timer.Get().GetTime()
	self.progress = self.progress - (now - self.last_update)
	self.last_update = now
	self.SetProgress()
	self.last_update = self.timer.Get().GetTime()
	
	-- мигаем
	if self.progress <= self.params.progress_blink*1000 then
		if self.params.sound_danger then
			sound(self.params.sound_danger)
		end
		
		local now = self.timer.Get().GetTime()
		if not self.last_blink or now - self.last_blink >= self.params.progress_blink_speed then
			local color = sf.graphics.Color(self.progressbar.image.GetColor())
			local blink_color = self.params.blink_color 
			local new_color = sf.graphics.Color(255, 255, 255, 255)
			if color ~= blink_color then
				new_color = blink_color
			end
			self.progressbar.image.SetColor(new_color)
			self.progress_upper.SetColor(new_color)
			
			self.last_blink = now
		end
	else
		local new_color = sf.graphics.Color(255, 255, 255, 255)
		self.progressbar.image.SetColor(new_color)
		self.progress_upper.SetColor(new_color)
	end
	if self.params.hide_upper_time and self.progress <= self.params.hide_upper_time*1000 then
		self.progress_upper.AddFlags(sf.gui.CWidget.FlagHidden)
	end
end

-- установка размера прогрессбара
function self.SetProgress()
	if self.progress > 0 then
		local value = self.progress/(self.params.progress_seconds*1000)
		if value > 1 then
			value = 1
			self.progress = self.params.progress_seconds*1000
		end
		local value1 = 1 - value

		self.progressbar.image.SetSize(self.progressbar.init_size.X, self.progressbar.init_size.Y*value1)
		-- выделяем прямоугольник для отрисовки
		local rect = sf.misc.FloatRect(0.0, math.ceil(self.progressbar.init_size.Y*value1), self.progressbar.init_size.X, self.progressbar.init_size.Y)
		self.progressbar.image.GetImage().SetRect(rect)
		self.progressbar.image.GetImage().SetSourceRect(rect)
		
		-- вычисляем новое положение полосочки
		local up_y = self.offset_bottom - self.progressbar.init_size.Y + math.ceil(self.progressbar.init_size.Y*value1) 
		local up_x = self.progressbar.image.GetOffset().X
		self.progress_upper.SetOffset(up_x, up_y)
	end
end

-- обновление всей игры
function self.OnUpdate(_owner)
	if self.bugs then
		self.bugs:Update() -- обновление ползающих тараканов
	end
	self.UpdateBeatedBugs(_owner) -- обновление побитых тараканов
	if not self.lose then
		self.CheckEnd(_owner) -- проверка окончания игры и обнуление параметров, если время закончилось
		self.UpdateProgress(_owner) -- обновление прогрессбара
	end
	self.RemoveClickClips(_owner)

	if self.lose and self.AllBeatedInState(6) then
		if not self.start_darkness then
			if self.darkness then
				self.darkness.RemFlags(sf.gui.CWidget.FlagHidden)
			end
			self.start_darkness = self.timer.Get().GetTime()
		else
			if self.timer.Get().GetTime() - self.start_darkness >= 
				self.params.delay_after_lose then 
					if self.darkness then
						self.darkness.AddFlags(sf.gui.CWidget.FlagHidden)
					end
					self.start_darkness = nil
					self.lose = nil
					self.Load(_owner)
			end
		end
	end
end

-- удаляет клипы на клик, которые уже проигрались
function self.RemoveClickClips(_owner)
	local i, n = 1, #self.clicks
	while i <= n do
		local click = self.clicks[i]
		local now = self.timer.Get().GetTime()
		local clip_time = qe.GetClipTime(click.clip.GetClip())
		if now - click.start >= clip_time then
			_owner.RemoveWidget(click.clip)
			table.remove(self.clicks, i)
			n = n - 1
		else
			i = i + 1
		end
	end
end

function self.CheckEnd(_owner)
	if self.progress <= 0 and not self.win then
		-- проиграли
		self.DeleteBugs(_owner)
		self.SetBeatedToRunAway()
	elseif self.beated.amount == self.params.panel_success.objects then
		if self.AllBeatedInState(4) then
			self.win = nil
			self.lose = nil
			_owner.OnEndGame()
		else
			self.win = true
		end
	end
end

-- проверяет, все ли тараканы в панели в состоянии _num
function self.AllBeatedInState(_num)
	for i, bug in ipairs(self.beated.bugs) do
		if bug.state ~= _num then
			return nil
		end
	end
	return true
end

-- удаляет жуков, которые появляются на поле
function self.DeleteBugs(_owner)
	for i, bug in ipairs(self.bugs.bugs) do
		_owner.RemoveWidget(bug.clip)
		if bug.beated_clip then
			_owner.RemoveWidget(bug.beated_clip)
		end
	end
	self.bugs = nil
end
-- переводит тараканов из панели в состояние "бежать!!!"
function self.SetBeatedToRunAway()
	local now = self.timer.Get().GetTime()
	for i, bug in ipairs(self.beated.bugs) do
		bug.state = 5
		bug.state_start = now
	end
	self.lose = true
	if self.params.sound_danger and self.params.sound_fade_time then
		stop_sound(self.params.sound_danger, self.params.sound_fade_time)
	end
	-- звук не издается, если нет ни одного таракана
	if self.params.sound_run_away and #self.beated.bugs > 0 then
		sound(self.params.sound_run_away)
	end
end
-- меняем курсор
function self.OnMouseMove(_owner, _pos, _keyboard_state, _broadcast)
	local cursor = sf.gui.CImageWidget(self.params.cursor, "cursor", 1, 0)
	if self.params.cursor ~= "" then
		sf.gui.g_Cursor.Instance().SetCustomCursor(cursor.GetImage().GetTexture())
	end
	if self.win or self.lose then
		cursor = sf.gui.CImageWidget("gui_cursors_hourglass", "cursor", 1, 0)
		sf.gui.g_Cursor.Instance().SetCustomCursor(cursor.GetImage().GetTexture())
	end
	return true
end 
-- обработка клика
function self.OnMouseDown(_owner, _pos, _button, _keyboard_state, _broadcast)
	if _broadcast or self.win or self.lose then return false end
	
	local i, n = 1, #self.bugs.bugs
	
	-- создаем клип с кликом
	local widget = sf.gui.CClipWidget("", 0, 0)
	widget.GetClip().Load(self.params.click_clip, true)	
	widget.SetOffset(_pos.X - widget.GetClip().GetViewSize().X/2, _pos.Y - widget.GetClip().GetViewSize().Y/2)
	_owner.AddWidget(widget)
	--widget.GetClip().Play()
	local obj = {clip = widget, start = self.timer.Get().GetTime()}
	table.insert(self.clicks, obj)
	
	local flag = true -- попали во что-нибудь?
	while i <= n do -- анализируем, не попали ли мы в один из объектов
		local bug_pos_X, bug_pos_Y  = self.bugs.bugs[i].clip.GetOffset().X, self.bugs.bugs[i].clip.GetOffset().Y
		local size = self.bugs.bugs[i].clip.GetClip().GetSize()
		
		-- вычисляем положение центра клипа
		bug_pos_X = bug_pos_X + size.X/2
		bug_pos_Y = bug_pos_Y + size.Y/2
		
		local obj = self.bugs.bugs[i].clip.GetClip().FindObject(self.params.bug_body_id)
		local cur_x = _pos.X - self.params.cursor_size.X/2
		local cur_y = _pos.Y - self.params.cursor_size.Y/2
		local rect = sf.misc.Poly4FromRect(sf.misc.FloatRect(cur_x, cur_y, self.params.cursor_size.X, self.params.cursor_size.Y))
		if obj then
			if (obj.GetLocation().GetPoly() + self.bugs.bugs[i].clip.GetOffset()).IsIntersects(rect) and self.bugs.bugs[i].state > 0 then
				-- попали
				flag = false
				self.bugs.bugs[i].state = -1	
				self.beated.amount = self.beated.amount + 1
				if self.params.sound_click then
					sound(self.params.sound_click)
				end
			end
		end

		i = i + 1
	end

	if flag then 
		-- уменьшаем оставшееся у игрока время при неверном клике
		self.progress = self.progress - self.params.progress_click*1000
		
		if self.params.sound_click_empty then
			sound(self.params.sound_click_empty)
		end
	end
	
	return true
end

-- создает жука и меняет ему состояние, если у него закончилось время клипа
-- _pos - середина нижней части позиции, в которой появился таракан
-- _time_start - время начала жизни
function self.SingleBug(_pos, _time_start, _num, _type)
	local widget = sf.gui.CClipWidget("", 0, 0)
	widget.GetClip().Load(self.params.objects[_type].look_out, true)
	local size = widget.GetClip().GetViewSize()
	widget.SetOffset(_pos.X - size.X/2, _pos.Y - size.Y)
	local time_of_life = qe.GetClipTime(widget.GetClip())

	return {
		clip = widget, -- сам объект
		state = 1,  -- сейчас виден = 1, убежал сам = 0,  стукнули = -1
		time_start = _time_start, -- начало жизни
		life_time = time_of_life, -- сколько вообще было время жизни
		pos = _pos, -- координаты середины нижней части позиции, в которой появилось пламя
		num = _num, -- номер позиции, которую занял объект
		bug_type = _type, -- тип таракана
		
		Update = function(_self, _time)
			if not ((_time - _self.time_start < _self.life_time)) then
				_self.state = 0 -- погас сам	
				return
			end 
		end
	}
end

-- структура, управляющая появлениями тараканов на поле
function self.AllBugs(_owner)
	return {
		bugs = {}, -- список языков пламени
		next_bug_time = 0, -- время появления следующего огня
		_owner = _owner, 
		
		Update = function(_self)
			local now = self.timer.Get().GetTime() -- нынешнее времечко
			
			local i, n = 1, #_self.bugs
			
			while i<=n do -- обновляем все объекты
				local bug = _self.bugs[i]
				bug:Update(now) -- обновляем i-ый объект
				
				if bug.state == 1 then -- не надо удалять клип
					i = i + 1
				else --  надо удалять клип
					
					if bug.state < 0 then -- если побили, а не закончилось его время
						if not bug.start_dying then
							-- создаем клип с таракашкой, держащимся за башку, плавно выводим его из альфы, ну а этот наоборот уводим в альфу
							bug.start_dying = self.timer.Get().GetTime()
							local clip = sf.gui.CClipWidget("", 0, 0)
							clip.GetClip().Load(self.params.objects[bug.bug_type].beated, true)
							local color = sf.graphics.Color(255, 255, 255, 255)
							local color1 = sf.graphics.Color(0, 255, 255, 255)
							clip.SetColor(color1)
							_self._owner.AddWidget(clip)
							bug.beated_clip = clip

							local off = bug.clip.GetOffset() 
							clip.SetOffset(off.X, off.Y)
						else
							if now - bug.start_dying >=  self.params.delay_show_beated then
								bug.state = 0
							else
								local cur_time = (now - bug.start_dying)/self.params.delay_show_beated
								local color = sf.graphics.Color(255*(1 - cur_time), 255, 255, 255)
								local color1 = sf.graphics.Color(225*cur_time, 255, 255, 255)
								bug.clip.SetColor(color)
								bug.beated_clip.SetColor(color1)
							end
						end	
						i = i + 1						
					else						
						if bug.beated_clip then
							local object = 
							{
								clip = bug.beated_clip,
								bug_type = bug.bug_type, -- тип таракана
								clip_to_change_state = nil, -- объект для плавного перехода между состояниями
								state = 1, -- 1 = хватается за голову, 2 - плавно переходит в лежащего на спине, 3 - летит на спине, 4 - лежит на панели, 5 - убегает со сцены
								pos = bug.num, -- какая позиция
								state_start = now -- когда попал в текущее состояние								
							}
							table.insert(self.beated.bugs, object) -- переместили из этого списка в другой
						else 
							self.pos_state[bug.num] = 0 -- позиция свободна
						end
						bug.clip.SetSize(0,0)
						bug.clip.GetClip().Stop() -- отключили звук в клипе
						_self._owner.RemoveWidget(bug.clip)
						table.remove(_self.bugs, i)
						n = n - 1
						
					end
				end
			end
		
			if (now >= _self.next_bug_time) then
				if #_self.bugs >= self.params.max_objects then
					local interval = random(self.params.min_interval, self.params.max_interval)
					_self.next_bug_time = now + interval -- следующее появление таракана 
				else
					-- надо генерить новый объект
					local free = 0 -- считаем количество свободных позиций
					for k = 1, #self.params.positions do
						if self.pos_state[k] == 0 then
							free = free + 1
						end
					end
					local free_num = random(1, free)
					local i = 0
					-- ищем свободную позицию free_num
					for k = 1, #self.params.positions do
						if self.pos_state[k] == 0 then
							i = i + 1
							if i == free_num then
								self.pos_state[k] = 1
								_self:_AddNewBug(now, k)
								break
							end
						end
					end
				end
			end
		end,
		
		-- добавление нового таракана в момент времени _time в позицию _num
		_AddNewBug = function(_self, _time, _num)
			local pos_x, pos_y = self.params.positions[_num].X, self.params.positions[_num].Y
			local pos = {X = pos_x, Y = pos_y}
			
			local type_bug = random(1, #self.params.objects)
			
			local object = self.SingleBug(pos, _time, _num, type_bug) -- создали новый объект
			local layer = self.params.objects_layer or 1
			object.clip.SetLayer(layer)
			_self._owner.AddWidget(object.clip)
						
			local interval = random(self.params.min_interval, self.params.max_interval)
			
			_self.next_bug_time = _time + interval -- следующее появление таракана 
			
			table.insert(_self.bugs, object)
		end		
	}
end

-- обновляет состояния побитых жуков
-- -- 1 = хватается за голову, 2 - плавно переходит в лежащего на спине, 3 - летит на спине, 4 - лежит на панели, 5 - убегает со сцены
function self.UpdateBeatedBugs(_owner)
	-- обновляем каждого таракашку
	local now = self.timer.Get().GetTime()

	for i, bug in ipairs(self.beated.bugs) do
		if bug.state == 1 then
			if now - bug.state_start >= self.params.delay_after_click then
				bug.state = 2
				bug.state_start = now
			end
		elseif bug.state == 2 then
			-- еще держится за башку, но уже плавно переворачивается
			if not bug.clip_to_change_state then
				-- создаем таракана на спине
				local clip = sf.gui.CClipWidget("", 0, 0)
				clip.GetClip().Load(self.params.objects[bug.bug_type].on_back, true)
				local color1 = sf.graphics.Color(0, 255, 255, 255)
				clip.SetColor(color1)
				_owner.AddWidget(clip)
				bug.clip_to_change_state = clip
				local off = bug.clip.GetOffset() 
				local size = bug.clip_to_change_state.GetClip().GetViewSize()
				local size1 = bug.clip.GetClip().GetViewSize()
				clip.SetOffset(off.X + size1.X/2 - size.X/2, off.Y + size1.Y - size.Y)
			else
				if now - bug.state_start >=  self.params.delay_for_alpha then
					-- инициализация движения, удаляем клип с качающим головой тараканом
					bug.state = 3
					bug.state_start = now
					_owner.RemoveWidget(bug.clip)
					bug.clip = bug.clip_to_change_state
					bug.clip_to_change_state = nil
					bug.start_pos = bug.clip.GetOffset()
					self.pos_state[bug.pos] = 0
				else
					local cur_time = (now - bug.state_start)/self.params.delay_for_alpha
					local color = sf.graphics.Color(255*(1 - cur_time), 255, 255, 255)
					local color1 = sf.graphics.Color(225*cur_time, 255, 255, 255)
					bug.clip.SetColor(color)
					bug.clip_to_change_state.SetColor(color1)
				end
			end
		elseif bug.state == 3 then
			if not bug.panel_pos then
				local panel = self.params.panel_success
				local space = panel.width / panel.objects -- какой промежуток между двумя соседними клипами, чтобы вместились на панель в ширину
				local x = panel.left_angle.X + i*space
				local y = panel.left_angle.Y
				bug.panel_pos = sf.misc.FloatVector(x, y)
			end
			
			-- рассчитываем, скока лететь
			local dist1 = (bug.start_pos.X - bug.panel_pos.X)*(bug.start_pos.X - bug.panel_pos.X)
			local dist2 = (bug.start_pos.Y - bug.panel_pos.Y)*(bug.start_pos.Y - bug.panel_pos.Y)
			local need_time = math.sqrt(dist1 + dist2) / self.params.speed*1000
			local cur_time = (now - bug.state_start)/need_time
			if cur_time > 1 then
				cur_time = 1
			end
			local off = bug.start_pos + (bug.panel_pos - bug.start_pos)*cur_time
			bug.clip.SetOffset(off.X, off.Y)
			if cur_time == 1 then
				bug.state = 4
			end
		elseif bug.state == 5 then
			-- разбегаются
			if not bug.clip_to_change_state then
				-- создаем убегающего таракана
				local clip = sf.gui.CClipWidget("", 0, 0)
				local layer = self.params.objects_layer or 1
				clip.SetLayer(layer)
				clip.GetClip().Load(self.params.objects[bug.bug_type].run_away, true)
				_owner.AddWidget(clip)
				bug.clip_to_change_state = clip
				local off = {X = bug.clip.GetOffset().X, Y = bug.clip.GetOffset().Y }
				local size = {X = bug.clip.GetClip().GetViewSize(), Y = bug.clip.GetClip().GetViewSize().Y}
				local size1 = clip.GetClip().GetViewSize()
				off.Y = off.Y + size.Y - size1.Y
				
				_owner.RemoveWidget(bug.clip)
				if not self.params.objects[bug.bug_type].run_left then
					off.X = off.X - size1.X
				end
				clip.SetOffset(off.X, off.Y)
			else
				local need_time = qe.GetClipTime(bug.clip_to_change_state.GetClip())
				if now - bug.state_start  >= need_time then
					_owner.RemoveWidget(bug.clip_to_change_state)
					bug.state = 6
					bug.state_start = now
					bug.clip = nil
					bug.clip_to_change_state = nil
				end
			end
		end
	end
end 