-- миниигра птички-рыбки
s12_birds_and_fishes_minigame = {} -- название таблицы миниигры, должно совпадать с имненем файла
local self = s12_birds_and_fishes_minigame -- для удобства, чтобы вместо s12_example_minigame.some_var писать self.some_var

self.params = 
{
	speed = 150, -- скорость движения, пиксели в секунду
	pos_amount = 15, -- количество позиций для фишек на поле
	dibs_amount = 10, -- количество фишек в игре
	pos_width = 45,  -- ширина позиции фишки
	pos_height = 45, -- высота позиции фишки
	sound_id = "open_stone_minigame", -- звук движения фишки
	positions = { -- описание точек, в которых может находиться фишка
		{ -- номер позиции: 1
			state =  "bird", -- состояние позиции: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			end_state = "fish", -- в каком состоянии должна находиться позиция на момент окончания игры: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			pos = sf.misc.FloatVector(442.5, 237.5), -- положение фишки на экране
			neighbours = { -- список позиций, в которые можно перейти из данной
				2
			}
		},
		{ -- номер позиции: 2
			state =  "bird", -- состояние позиции: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			end_state = "fish", -- в каком состоянии должна находиться позиция на момент окончания игры: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			pos = sf.misc.FloatVector(488.5, 237.5), -- положение фишки на экране
			neighbours = { -- список позиций, в которые можно перейти из данной
				1, 3, 6
			}
		},
		{ -- номер позиции: 3
			state =  "bird", -- состояние позиции: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			end_state = "fish", -- в каком состоянии должна находиться позиция на момент окончания игры: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			pos = sf.misc.FloatVector(533.5, 237.5), -- положение фишки на экране
			neighbours = { -- список позиций, в которые можно перейти из данной
				2, 4
			}
		},
		{ -- номер позиции: 4
			state =  "bird", -- состояние позиции: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			end_state = "fish", -- в каком состоянии должна находиться позиция на момент окончания игры: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			pos = sf.misc.FloatVector(578.5, 237.5), -- положение фишки на экране
			neighbours = { -- список позиций, в которые можно перейти из данной
				3, 5
			}
		},
		{ -- номер позиции: 5
			state =  "bird", -- состояние позиции: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			end_state = "fish", -- в каком состоянии должна находиться позиция на момент окончания игры: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			pos = sf.misc.FloatVector(623.5, 237.5), -- положение фишки на экране
			neighbours = { -- список позиций, в которые можно перейти из данной
				4
			}
		},
		{ -- номер позиции: 6
			state =  "free", -- состояние позиции: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			end_state = "free", -- в каком состоянии должна находиться позиция на момент окончания игры: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			pos = sf.misc.FloatVector(488.5, 283.5), -- положение фишки на экране
			neighbours = { -- список позиций, в которые можно перейти из данной
				2, 7
			}
		},
		{ -- номер позиции: 7
			state =  "free", -- состояние позиции: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			end_state = "free", -- в каком состоянии должна находиться позиция на момент окончания игры: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			pos = sf.misc.FloatVector(488.5, 329.5), -- положение фишки на экране
			neighbours = { -- список позиций, в которые можно перейти из данной
				6, 8, 10
			}
		},
		{ -- номер позиции: 8
			state =  "free", -- состояние позиции: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			end_state = "free", -- в каком состоянии должна находиться позиция на момент окончания игры: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			pos = sf.misc.FloatVector(533.5, 329.5), -- положение фишки на экране
			neighbours = { -- список позиций, в которые можно перейти из данной
				7, 9
			}
		},
		{ -- номер позиции: 9
			state =  "free", -- состояние позиции: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			end_state = "free", -- в каком состоянии должна находиться позиция на момент окончания игры: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			pos = sf.misc.FloatVector(578.5, 329.5), -- положение фишки на экране
			neighbours = { -- список позиций, в которые можно перейти из данной
				8
			}
		},
		{ -- номер позиции: 10
			state =  "free", -- состояние позиции: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			end_state = "free", -- в каком состоянии должна находиться позиция на момент окончания игры: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			pos = sf.misc.FloatVector(488.5, 375.5), -- положение фишки на экране
			neighbours = { -- список позиций, в которые можно перейти из данной
				7, 14
			}
		},
		{ -- номер позиции: 11
			state =  "fish", -- состояние позиции: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			end_state = "bird", -- в каком состоянии должна находиться позиция на момент окончания игры: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			pos = sf.misc.FloatVector(397.5, 465.5), -- положение фишки на экране
			neighbours = { -- список позиций, в которые можно перейти из данной
				12
			}
		},
		{ -- номер позиции: 12
			state =  "fish", -- состояние позиции: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			end_state = "bird", -- в каком состоянии должна находиться позиция на момент окончания игры: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			pos = sf.misc.FloatVector(397.5, 420.5), -- положение фишки на экране
			neighbours = { -- список позиций, в которые можно перейти из данной
				11, 13
			}
		},
		{ -- номер позиции: 13
			state =  "fish", -- состояние позиции: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			end_state = "bird", -- в каком состоянии должна находиться позиция на момент окончания игры: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			pos = sf.misc.FloatVector(443.5, 420.5), -- положение фишки на экране
			neighbours = { -- список позиций, в которые можно перейти из данной
				12, 14
			}
		},
		{ -- номер позиции: 14
			state =  "fish", -- состояние позиции: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			end_state = "bird", -- в каком состоянии должна находиться позиция на момент окончания игры: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			pos = sf.misc.FloatVector(488.5, 420.5), -- положение фишки на экране
			neighbours = { -- список позиций, в которые можно перейти из данной
				10, 13, 15
			}
		},
		{ -- номер позиции: 15
			state =  "fish", -- состояние позиции: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			end_state = "bird", -- в каком состоянии должна находиться позиция на момент окончания игры: "bird" - на ней стоит птичка, "fish" - на ней стоит рыбка, "free" - свободна
			pos = sf.misc.FloatVector(533.5, 420.5), -- положение фишки на экране
			neighbours = { -- список позиций, в которые можно перейти из данной
				14
			}
		}
	}	
}
-- проверка, закончилась ли у нас игра
function s12_birds_and_fishes_minigame.CheckEnd(_owner)
	local alright = true;
	-- надо проверить, находятся ли все позиции в своем финальном положении
	for i = 1, self.params.pos_amount do
		if (self.position_state[i] ~= self.params.positions[i].end_state) then
			-- положение i-ой фишки не совпало с ее конечным положением, игра не завершена
			alright = false;
			break
		end
	end
	
	if (alright == true) then
	-- закончить миниигру
		self.win = true
	end
end

function self.Init(_owner) -- функция, инициализирующая миниигру; в ней стоит инициализировать все переменные, нужные миниигре
	sminigames.SetStandartScriptName(_owner, s12_birds_and_fishes_minigame) 
	
	-- инициализация переменных
	-- создаем таблицу состояний позиций
	self.position_state = {}
	for i = 1, self.params.pos_amount do
		table.insert(self.position_state, self.params.positions[i].state)
	end
--------------изображения	
	self.objects = {}
	-- создаем птичек
	local i = 1
	local initialized = {}
	while true do
		local image_widget =  __cast(_owner.GetWidget("bird_"..i, true), sf.gui.CImageWidget)
		if not image_widget then 
			break 
		end
		
		-- создаем объект для птички
		local o = {image = image_widget, object_type = "bird"}
		table.insert(self.objects, o)
		table.insert(initialized, false)
		
		i = i + 1		
	end
	
	-- создаем рыбок
	local i = 1
	while true do
		local image_widget = __cast(_owner.GetWidget("fish_"..i, true), sf.gui.CImageWidget)
		if not image_widget then 
			break 
		end
		
		-- создаем объект для рыбки
		local o = {image = image_widget, object_type = "fish"}
		table.insert(self.objects, o)
		table.insert(initialized, false)
		
		i = i + 1		
	end
	
	--заполняем позиции виджетов
	for i = 1, self.params.pos_amount do
		local ob_type = self.params.positions[i].state
		if (ob_type ~= "free") then
			for j = 1, self.params.dibs_amount do
				if (initialized[j] == false and ob_type == self.objects[j].object_type) then
					initialized[j] = true
					local width, height = self.objects[j].image.GetSize().X, self.objects[j].image.GetSize().Y
					self.objects[j].image.SetOffset(self.params.positions[i].pos.X - width/2, self.params.positions[i].pos.Y - height/2)
					break
				end
			end			
		end
	end
	
	__assert(#self.objects == self.params.dibs_amount, 
		"Кол-во изображений не совпадает с кол-вом фишек, сейчас "..#self.objects.." должно быть "..self.params.dibs_amount)
------------------------клипы 	
	-- подсветка ячейки, в которую идем
	self.clip_on_move = sf.gui.CClipWidget("", 1, 0)
	self.clip_on_move.GetClip().Load("minigame_shine02", true)
	self.on_move_size = self.clip_on_move.GetClip().GetViewSize()
	self.clip_on_move.SetOffset(0, 0)
	self.clip_on_move.SetSize(0, 0)
	_owner.AddWidget(self.clip_on_move) 
    -- подсветка фишки
	self.clip_dib = sf.gui.CClipWidget("", 0, 0)
	self.clip_dib.GetClip().Load("minigame_shine01", true)
	self.clip_dib_size = self.clip_dib.GetClip().GetViewSize()
	self.clip_dib.SetOffset(0, 0)
	self.clip_dib.SetSize(0, 0)
	_owner.AddWidget(self.clip_dib) 
-- проигрывается, когда нельзя идти
	self.clip_error = sf.gui.CClipWidget("", 0, 0) 
	self.clip_error.GetClip().Load("minigame_shine03", true)
	self.error_size = self.clip_error.GetClip().GetViewSize()
	self.clip_error.SetOffset(0, 0)
	self.clip_error.SetSize(0, 0)
	_owner.AddWidget(self.clip_error)

-------------------------------вспомогательные 
	self.selected = -1 -- сейчас не выделена фишка (номер выделенного объекта)
	self.sel_pos = -1 -- номер выделенной позиции
	
	self.cur_way = {} -- по какому пути движется объект
	self.cur_line = -1 -- по какому участку пути сейчас двигаемся
	self.moving = -1 -- какой объект движется
	self.obj_pos = {x = 0, y = 0} -- позиция движущегося объекта
	self.last_start = 0 -- начало движения от последней точки
	self.last_length = 0 -- сколько должны двигаться от этой точки
	
	self.timer = __create_timer("", "qe_level") -- создаем таймер, который будет использоваться для движения объекта
		
	self.win = false -- победили?
	
	self.sound = 0
end

-- ищет фишку, в ктороую попадают заданные координаты; если фишка не найдена, возвращает nil
function self.FindDibByPos(_x, _y)
	local i
	for i = 1, self.params.dibs_amount do
		local pos = self.objects[i].image.GetOffset()
		local width, height = self.objects[i].image.GetSize().X, self.objects[i].image.GetSize().Y

		if (math.abs(pos.X + width/2 - _x) < self.params.pos_width/2) and (math.abs(pos.Y + height/2 - _y) < self.params.pos_height/2) then
			return i
		end
		
	end	
end

-- обработка нажатия кнопки мыши
function self.OnMouseDown(_owner, _pos, _button, _keyboard_state, _broadcast)
	-- если фишка двигается, то не обрабатываем клик
	if _broadcast or self.moving >= 0 then return false end
	
	-- анализируем, во что мы попали
	local dib_in_pos = self.FindDibByPos(_pos.X, _pos.Y) -- попали в какую-нибудь фишку?
	if dib_in_pos then
		-- попали в фишку - надо определить, на какой она позиции стоит
		local pos_x, pos_y  = self.objects[dib_in_pos].image.GetOffset().X, self.objects[dib_in_pos].image.GetOffset().Y
		local width, height = self.objects[dib_in_pos].image.GetSize().X, self.objects[dib_in_pos].image.GetSize().Y
		local center = { -- центр позиции совпадает с серединой нижнего края фишки
			X = pos_x + width/2,
			Y = pos_y + height/2
		} 
		-- теперь ищем позицию, которая приблизительно такая же, как найденная
		for i = 1, self.params.pos_amount do
			local posi = self.params.positions[i].pos
			if (math.abs(posi.X - center.X) < self.params.pos_width/2 ) and (math.abs(posi.Y - center.Y) < self.params.pos_height/2) then
				-- мы попали в эту позицию
				self.selected = dib_in_pos
				self.sel_pos = i
				
				self.clip_dib.SetOffset(self.params.positions[i].pos.X - self.clip_dib_size.X/2, 
					self.params.positions[i].pos.Y - self.clip_dib_size.Y/2) -- запускаем эффект для выделения фишки
				self.clip_dib.SetSize(self.clip_dib_size.X, self.clip_dib_size.Y)
				
				break
			end
		end
	else
		-- попали не в фишку... значит могли попасть в позицию
		for i = 1, self.params.pos_amount do
			local posi = self.params.positions[i].pos
			if (math.abs(posi.X - _pos.X) < self.params.pos_width/2 ) and (math.abs(posi.Y - _pos.Y) < self.params.pos_height/2) then
				-- мы попали в эту позицию
				if self.selected ~= -1 then
					--  надо генерить путь от выделенной фишки к этой позиции
					local matr = self.MakeTransitionMatrix()

					local way = self.FindWay(matr, self.sel_pos, i)
					if (way == nil) then
						-- нет пути - ошибка. потом надо будет заменить на клип 
						local pos_clip_x, pos_clip_y = self.params.positions[i].pos.X, self.params.positions[i].pos.Y
						self.clip_error.SetOffset(pos_clip_x - self.error_size.X/2, pos_clip_y - self.error_size.Y/2)
						self.clip_error.SetSize(self.error_size.X, self.error_size.Y)
						self.clip_error.GetClip().SetTime(0)
						self.clip_error.GetClip().Play()
					else
						-- инициализировать движение и изменить состояние selected на free, а i на object_type
						if #way > 1 then
							local pos_clip_x, pos_clip_y = self.params.positions[way[1]].pos.X, self.params.positions[way[1]].pos.Y
							self.clip_on_move.SetOffset(pos_clip_x - self.on_move_size.X/2, pos_clip_y - self.on_move_size.Y/2)
							self.clip_on_move.SetSize(self.on_move_size.X, self.on_move_size.Y)
						
							local w = self.ReverseArray(way)
							self.position_state[self.sel_pos] = "free"
							self.position_state[i] = self.objects[self.selected].object_type
							self.sel_pos = w[#w]
					
							self.cur_way = w -- указываем, по какому пути мы идем
							self.moving = self.selected -- движется выбранный объект
							self.cur_line = 1
							self.obj_pos.x = self.objects[self.selected].image.GetOffset().X + self.objects[self.selected].image.GetSize().X/2
							self.obj_pos.y = self.objects[self.selected].image.GetOffset().Y + self.objects[self.selected].image.GetSize().Y/2
					
							self.UpdateMovingTimer()
						end
					end
				end
				
				break
			end
		end
	end
	
	s12_birds_and_fishes_minigame.CheckEnd(_owner)
	return true
end


-- поиск пути между позициями _start_pos и _end_pos
-- _adj_matr - матрица смежности
-- ! путь выводится, начиная с _end_pos
function self.FindWay(_adj_matr, _start_pos, _end_pos)
	local isSeen = {} -- просматривали ли вершину
	local last = {} -- номер последней помеченой вершины
	local dst = {} -- текущие кратчайшие расстояния от start_pos до end_pos
	local pos_amount = self.params.pos_amount
	for j = 1, pos_amount do
		table.insert(isSeen, 0)
		table.insert(dst, -100) -- бесконечность
	end

	dst[_start_pos] = 0
	isSeen[_start_pos] = 1
	last = _start_pos
	-- поиск кратчайших расстояний от start_pos до остальных вершин графа (алгоритм Дейкстры)
	for j = 1, (pos_amount-1) do
		for i = 1, pos_amount do
			if (isSeen[i] == 0 and _adj_matr[last][i] == 1) then -- не помечена и связана с последней вершиной
				if (dst[i] < 0) then
					dst[i] = dst[last] + 1
				else
					dst[i] = math.min((dst[last] + 1), dst[i])
				end
			end
		end
		local min_dist = -100
		for x = 1, pos_amount do
			if (isSeen[x] == 0) then
				if (min_dist < 0) then
					min_dist = dst[x]
					last = x
				else
					if (dst[x] >= 0) then
						if (min_dist > dst[x]) then
							min_dist = dst[x]
							last = x
						end
					end
				end
			end
		end
		isSeen[last] = 1
	end
	
	-- построим сам путь
	if (dst[_end_pos] >= 0) then --если путь существует
		local way = {_end_pos} -- первым элементом будет последний элемент
		local verse = _end_pos
		while (verse ~= _start_pos) do
			for i = 1, pos_amount do
				if (dst[i] + 1 == dst[verse] and _adj_matr[i][way[#way]] == 1) then -- мы пришли сюда из этой вершины
					table.insert(way, i)
					verse = i
					break
				end
			end
		end
		
		return way -- построили список вершин в пути
	else
		return nil
	end
end

--создает матрицу смежности для текущего состояния поля
function self.MakeTransitionMatrix()
	local matr = {}
	local pos_amount = self.params.pos_amount
	-- заполняем нулями
	for i = 1, pos_amount do
		table.insert(matr, {})
		for j = 1, pos_amount do
			table.insert(matr[i], 0)
		end
	end
	-- выставляем единички в нужных местах
	for i = 1, pos_amount do
		if (self.position_state[i] == "free" or i == self.sel_pos) then
			local j = 1
			while (self.params.positions[i].neighbours[j]) do
				if (self.position_state[self.params.positions[i].neighbours[j]] == "free" or self.params.positions[i].neighbours[j] == self.sel_pos) then
					matr[i][self.params.positions[i].neighbours[j]] = 1
					matr[self.params.positions[i].neighbours[j]][i] = 1
				end
				j = j +1
			end
		end
	end
	
	return matr
end

-- выдает массив  _arr в обратном порядке
function self.ReverseArray(_arr)
	local size = #_arr
	local res = {}
	while (size > 0) do
		table.insert(res, _arr[size])
		size = size - 1
	end
	return res
end

-- обновление миниигры 
function self.OnUpdate(_owner)
	if  sf.core.g_Application.GetTimeManager().GetTimer(_T("qe_level")).GetK() < 0.01 then
		return
	end

	if (self.moving <= 0) then return end
	if self.params.sound_id   then
		sound(self.params.sound_id)
	end
	local ind_end = self.cur_way[self.cur_line + 1]
	local ind_start = self.cur_way[self.cur_line]
	
	local width, height = self.objects[self.moving].image.GetSize().X, self.objects[self.moving].image.GetSize().Y
	local current_time = (self.timer.Get().GetTime() - self.last_start)/self.last_length
    if current_time > 1 then 
		current_time = 1  -- мы не можем зайти дальше точки 
	end
    -- вычисляем новую координату  объекта по величине сдвига
	local start_pos = self.params.positions[ind_start].pos
	local end_pos = self.params.positions[ind_end].pos
	self.obj_pos.x = start_pos.X + (end_pos.X - start_pos.X )*current_time 
	self.obj_pos.y = start_pos.Y + (end_pos.Y - start_pos.Y)*current_time 
    self.objects[self.moving].image.SetOffset(self.obj_pos.x - width/2, self.obj_pos.y - height/2)
	self.clip_dib.SetOffset(self.obj_pos.x -  self.clip_dib_size.X/2, self.obj_pos.y - self.clip_dib_size.Y/2) 
	
	local now = self.timer.Get().GetTime()
	if (now  >= self.last_length +  self.last_start) then -- идем дольше, чем рассчитанное время пути в этом отрезке?
		self.cur_line = self.cur_line + 1 -- перемещаемся на следующий отрезок пути
		if (self.cur_line == #self.cur_way) then
			-- дошли до конца пути, надо убрать информацию о движении
			local new_x = self.params.positions[self.cur_way[self.cur_line]].pos.X - width/2
			local new_y = self.params.positions[self.cur_way[self.cur_line]].pos.Y - height/2
			self.objects[self.moving].image.SetOffset(new_x, new_y)
			self.clip_dib.SetSize(0, 0) -- прячем эффект выделения фишки
			self.clip_on_move.SetSize(0, 0) -- спрятали клип с выделением точки назначения
			
			self.cur_way = {} -- по какому пути движется объект
			self.cur_line = -1 -- по какому участку пути сейчас двигаемся
			self.moving = -1 -- какой объект движется
			self.obj_pos = {x = 0, y = 0} -- позиция движущегося объекта
			self.selected = -1
			self.sel_pos = -1
			self.sound  = 0
			if (self.win) then -- пометка о победе меняется в начале движения, а игра должна закончиться только после окончания последнего перемещения
				_owner.SetGameResult(1)
				_owner.OnEndGame()
			end
		else -- пересчитать следующую длину
			self.UpdateMovingTimer()
		end
	end
end

-- запоминает момент начала движения от точки и вычисляет время движения до следующей точки отрезка
function self.UpdateMovingTimer()
	self.last_start = self.timer.Get().GetTime() -- начало движения		
	local 	end_pos = self.params.positions[self.cur_way[self.cur_line + 1]].pos
	local distance_sqr1 = (self.obj_pos.x - end_pos.X)*(self.obj_pos.x - end_pos.X)
	local distance_sqr2 = (self.obj_pos.y - end_pos.Y)*(self.obj_pos.y - end_pos.Y)
	
	self.last_length = 1000*math.sqrt(distance_sqr1 + distance_sqr2) / self.params.speed
end
