select_keys = {}

select_keys.params = 
{
	sequence = {1,4,6},
	correctly_angle = 360, -- угол поворота правильно вставленного угла
	incorrectly_angle = 45, -- угол поворота неправильно вставленного угла
	speed_rotation = 200.0, -- скорость поворота ключа (градусов в секунду)
	pause_before_push = 300.0, -- пауза перед вставкой следующего ключа
	pause_before_rotate = 150.0, -- пауза перед поворотом вставленного ключа
	offset_selected_key = sf.misc.FloatVector(0.0, -20.0), -- смещение выбранного ключа,
	
	sound_insert = "insert_key",
	sound_rotate_right = "rotate_right_key",
	sound_rotate_wrong = "rotate_wrong_key",
	sound_indicator = "indicator"
}

function select_keys.CheckEnd(_owner)
	local target_sequence_length = #select_keys.params.sequence
	local current_sequence_length = #select_keys.current_sequence
	if target_sequence_length == current_sequence_length then
		_owner.SetGameResult(1)
		_owner.OnEndGame()
	end
end
				
function select_keys.EndGame(_owner)
	_owner.SetGameResult(1)
	_owner.OnEndGame()
end
				
function select_keys.OnMouseDown(_owner, _pos, _button, _state, _broadcast)
	
	if #select_keys.effects > 0 then
		return false
	end

	local ret = false
	for i, key in pairs(select_keys.keys) do
		if sf.misc.BitwiseAnd(key.widget.GetFlags(), sf.gui.CBaseWidget.FlagHidden) == 0 and
			key.widget.GetPoly().IsContains(_pos.X, _pos.Y) == true then
				select_keys.PushKey(key)
				ret = true
				break
		end
	end
	
	--[[if ret == false then
		if sf.misc.BitwiseAnd(select_keys.image_key_pushed.GetFlags(), sf.gui.CBaseWidget.FlagHidden) == 0 and
			select_keys.image_key_pushed.GetPoly().IsContains(_pos.X, _pos.Y) == true then
				local target_sequence_length = #select_keys.params.sequence
				local current_sequence_length = #select_keys.current_sequence
				
				if target_sequence_length > current_sequence_length then				
					local c = select_keys.params.sequence[current_sequence_length + 1]
					select_keys.RotateKey(c == select_keys.pushed_key.id)
				end
		end
	end]]
	
	return ret
end

function select_keys.OnMouseMove(_owner, _pos, _state, _broadcast)
	if sf.misc.BitwiseAnd(select_keys.image_key_pushed.GetFlags(), sf.gui.CBaseWidget.FlagHidden) == 0 and
		select_keys.image_key_pushed.GetPoly().IsContains(_pos.X, _pos.Y) == true then
		sf.gui.g_Cursor.Instance().SetCursor(sf.gui.CCursor.CursorHand)
	end
	return false
end

function select_keys.OnMouseUp(_owner, _pos, _button, _state, _broadcast)	
	return false
end

function select_keys.OnUpdate(_owner)
	local i = 1
	while i <= #select_keys.effects do
		local effect = select_keys.effects[i]
		assert(effect)
		if not effect.is_dead then
			effect:Update(select_keys.timer.Get().GetTime())
		end
		if effect.is_dead then
			table.remove(select_keys.effects, i)			
		else
			i = i+1
		end
	end
	if select_keys.rotation then
		local a = select_keys.rotation.calc_angle(select_keys.timer.Get().GetTime())
		if a then
			local size = select_keys.image_key_pushed.GetSize()
			local matrix = sf.misc.MatrixTranslation(-size.X / 2.0 , -size.Y / 2.0)
			matrix = matrix * sf.misc.MatrixRotation(a, 0.0, 0.0)
			matrix = matrix * sf.misc.MatrixTranslation(size.X / 2.0 , size.Y / 2.0)
			select_keys.image_key_pushed.SetRotation(matrix)
		else
			select_keys.rotation.post_function(_owner)
			select_keys.rotation = nil
		end
	end
end

function select_keys.OnPreferredSize(_owner)
	return sminigames.OnPreferredSize(_owner)
end

function select_keys.Init(_owner)
	sminigames.SetStandartScriptName(_owner, select_keys)
	
	select_keys.image_symbols = __cast(_owner.GetWidget("symbols", false).get(), sf.gui.CImageWidget)
	assert(select_keys.image_symbols)
	
	select_keys.image_key_pushed = __cast(_owner.GetWidget("key_pushed", false).get(), sf.gui.CImageWidget)
	assert(select_keys.image_key_pushed)
	
	select_keys.timer = __create_timer("", "qe_level")
	
	select_keys.pushed_key = nil
	select_keys.current_sequence = {}
	
	select_keys.keys = {}	
	
	select_keys.timer = __create_timer("", "qe_level")

	select_keys.effects = {}
	
	local w = _owner.GetWidgets()
	while not w.IsEnd() do
		local widget = w.Get()
		local key = string.match(widget.GetId().c_str(), "key_(%d+)")
		if key then	
			local image_key = __cast(widget, sf.gui.CImageWidget)
			assert(image_key)
			table.insert(select_keys.keys, {widget = image_key, id = tonumber(key), pos = sf.misc.FloatVector(image_key.GetOffset())})
		end
		w.Next()
	end	
	
	select_keys.UpdateSymbols()
	select_keys.PopKey()
end

function select_keys.UpdateSymbols()
	local target_sequence_length = #select_keys.params.sequence
	local current_sequence_length = #select_keys.current_sequence
	if target_sequence_length > 0 and current_sequence_length > 0 then
		select_keys.image_symbols.RemFlags(sf.gui.CBaseWidget.FlagHidden)
		local k = current_sequence_length / target_sequence_length
		local texture_size = select_keys.image_symbols.GetImage().GetTextureSize()		
		local source_rect = sf.misc.IntRect(0, 0, texture_size.X * k, texture_size.Y)
		local dest_rect = sf.misc.FloatRect(0.0, 0.0, texture_size.X * k, texture_size.Y)
		select_keys.image_symbols.GetImage().SetSourceRect(source_rect)
		select_keys.image_symbols.GetImage().SetRect(dest_rect)
	else
		select_keys.image_symbols.AddFlags(sf.gui.CBaseWidget.FlagHidden)
	end
end

function select_keys._StartRotate()
	local target_sequence_length = #select_keys.params.sequence
	local current_sequence_length = #select_keys.current_sequence
	
	if target_sequence_length > current_sequence_length then				
		local c = select_keys.params.sequence[current_sequence_length + 1]
		select_keys.RotateKey(c == select_keys.pushed_key.id)
	end
end

function select_keys._PushKey()
	select_keys.image_key_pushed.RemFlags(sf.gui.CBaseWidget.FlagHidden)		
	select_keys.pushed_key.widget.AddFlags(sf.gui.CBaseWidget.FlagHidden)
	local size = select_keys.image_key_pushed.GetSize()
	local matrix = sf.misc.MatrixTranslation(-size.X / 2.0, -size.Y / 2.0)
	matrix = matrix * sf.misc.MatrixRotation(0.0, 0.0, 0.0)
	matrix = matrix * sf.misc.MatrixTranslation(size.X / 2.0, size.Y / 2.0)
	select_keys.image_key_pushed.SetRotation(matrix)
	select_keys.pushed_key.widget.SetOffset(select_keys.pushed_key.pos.X, select_keys.pushed_key.pos.Y)
	sf.core.g_Application.GetAudioManager().Play(_T(select_keys.params.sound_insert), -2, -2, -2, -2, -2)
	local t = select_keys.timer.Get().GetTime()	
	local effect = select_keys.PauseBeforeActionEffect(t, t + select_keys.params.pause_before_rotate, select_keys._StartRotate)
	table.insert(select_keys.effects, effect)
end

function select_keys.PushKey(_key)
	if select_keys.pushed_key then
		select_keys.PopKey()
		select_keys.pushed_key = _key		
		select_keys.pushed_key.widget.SetOffset(
			select_keys.pushed_key.pos.X + select_keys.params.offset_selected_key.X, 
			select_keys.pushed_key.pos.Y + select_keys.params.offset_selected_key.Y)
		local t = select_keys.timer.Get().GetTime()	
		local effect = select_keys.PauseBeforeActionEffect(t, t + select_keys.params.pause_before_push, select_keys._PushKey)
		table.insert(select_keys.effects, effect)
	else
		select_keys.pushed_key = _key	
		select_keys._PushKey()
	end
end

function select_keys.PopKey()
	select_keys.image_key_pushed.AddFlags(sf.gui.CBaseWidget.FlagHidden)
	if select_keys.pushed_key then		
		select_keys.pushed_key.widget.RemFlags(sf.gui.CBaseWidget.FlagHidden)
		select_keys.pushed_key = nil
		select_keys.rotation = nil
	end
end

select_keys.calc_angle = 
{
	function(_curr_time)
		local max_angle = select_keys.params.correctly_angle
		local t = _curr_time - select_keys.rotation.start_time
		local a = select_keys.params.speed_rotation * (t / 1000.0)
		if a <= max_angle then 
			return (math.pi * a / 180.0)
		end
		return nil
	end,
	function(_curr_time)
		local max_angle = select_keys.params.incorrectly_angle
		local t = _curr_time - select_keys.rotation.start_time
		local a = select_keys.params.speed_rotation * (t / 1000.0)
		if a <= (max_angle * 2.0) then 
			if a > max_angle then
				a = (max_angle * 2.0) - a
			end
			return (math.pi * a / 180.0)
		end
		return nil
	end,
}

function select_keys.RotateKey(_correctly)
	select_keys.rotation = {}
	select_keys.rotation.start_time = select_keys.timer.Get().GetTime()

	if _correctly then
		sf.core.g_Application.GetAudioManager().Play(_T(select_keys.params.sound_rotate_right), -2, -2, -2, -2, -2)
		select_keys.rotation.post_function = select_keys.IncreaseSequence
		select_keys.rotation.calc_angle = select_keys.calc_angle[1]
	else
		sf.core.g_Application.GetAudioManager().Play(_T(select_keys.params.sound_rotate_wrong), -2, -2, -2, -2, -2)
		select_keys.rotation.post_function = select_keys.Reset
		select_keys.rotation.calc_angle = select_keys.calc_angle[2]
	end
end

function select_keys.IncreaseSequence(_owner)
	sf.core.g_Application.GetAudioManager().Play(_T(select_keys.params.sound_indicator), -2, -2, -2, -2, -2)
	table.insert(select_keys.current_sequence, 0)	
	select_keys.PopKey()
	select_keys.UpdateSymbols()
	select_keys.CheckEnd(_owner)
end

function select_keys.Reset(_owner)
	select_keys.current_sequence = {}
	select_keys.PopKey()
	select_keys.UpdateSymbols()
end

function select_keys.PauseBeforeActionEffect(_start_time, _target_time, _action)
	return
	{
		_start_time = _start_time,
		_target_time = _target_time,
		_action = _action,
		
		is_dead = false,
		
		Update = function(_self, _time)			
			if _time >= _self._target_time then	
				_self._action()
				_self.is_dead = true				
			end
		end
	}
end
