lock_with_turns = {}

lock_with_turns.params = 
{
	-- минимальное и максимальное значения на циферблате
	interval = {0,9},
	-- погрешность в градусах
	inaccuracy = 10.0,
	-- начальные значения на "крутилках"
	init_values = {7,8,9},
	-- целевые значения на "крутилках"
	target_value = {1,2,3}
}

function lock_with_turns.IsAnglesEquals(_a1, _a2)
	local inacc = 2.0 * math.pi * lock_with_turns.params.inaccuracy / 360.0
	return math.abs(_a1 - _a2) < inacc
end
	
function lock_with_turns.EndGame(_owner)
	local resolved = true
	for i, turn in pairs(lock_with_turns.turns) do
		local target_angle = lock_with_turns.ValueToAngle(lock_with_turns.params.target_value[i])
		if not lock_with_turns.IsAnglesEquals(target_angle, turn.angle) then
			resolved = false
			break
		end
	end
	if resolved then
		if lock_with_turns.widgets then
			for _, w in pairs(lock_with_turns.widgets) do
				sminigames.ImageDropBuffer(w.GetImage())
			end
			lock_with_turns.widgets = nil
		end
		_owner.SetGameResult(1)
		_owner.OnEndGame()
	end
end
				
function lock_with_turns.OnMouseDown(_owner, _pos, _button, _state, _broadcast)
	for i, turn in pairs(lock_with_turns.turns) do
		if turn.widget.GetPoly().IsContains(_pos.X, _pos.Y) and lock_with_turns.IsTurnContains(turn, _pos) then
			lock_with_turns.drag_info = {}
			lock_with_turns.drag_info.turn_num = i
			lock_with_turns.drag_info.last_turn_angle = turn.angle
			lock_with_turns.drag_info.last_mouse_pos = sf.misc.FloatVector(_pos.X, _pos.Y)
			return true
		end
	end
	return false
end

function lock_with_turns.OnMouseMove(_owner, _pos, _state, _broadcast)
	if lock_with_turns.drag_info then
		
		local turn = lock_with_turns.turns[lock_with_turns.drag_info.turn_num]
		local offset = turn.widget.GetOffset()
		local size = turn.widget.GetSize()
		local center = offset + size / 2.0
	
		local vec1 = sf.misc.FloatVector(lock_with_turns.drag_info.last_mouse_pos - center)
		local vec2 = sf.misc.FloatVector(_pos - center)		

		if sf.misc.CalculateFloatVectorModule(vec1) < 0.001 or sf.misc.CalculateFloatVectorModule(vec2) < 0.001 then
			return false
		end

		vec2 = vec2 * (sf.misc.CalculateFloatVectorModule(vec1) / sf.misc.CalculateFloatVectorModule(vec2))

		local a = sf.misc.CalculateFloatVectorModule(vec1)
		local b = sf.misc.CalculateFloatVectorModule(vec2)
		local c = sf.misc.CalculateFloatVectorModule(vec2-vec1)

		local angle = math.acos(misc.Clamp((a*a+b*b-c*c)/(2*a*b), -1.0, 1.0))

		local p1 = vec2 * sf.misc.MatrixRotation(angle, 0.0, 0.0)
		local p2 = vec2 * sf.misc.MatrixRotation(-angle, 0.0, 0.0)

		if 	sf.misc.CalculateFloatVectorModule(vec1-p1) < sf.misc.CalculateFloatVectorModule(vec1-p2) then
			angle = angle * -1.0
		end
		
		lock_with_turns.RotateTurn(turn, lock_with_turns.drag_info.last_turn_angle + angle)
		
		return true
	end
	return false
end

function lock_with_turns.OnMouseUp(_owner, _pos, _button, _state, _broadcast)	
	if lock_with_turns.drag_info then
		lock_with_turns.drag_info = nil
		lock_with_turns.EndGame(_owner)
		return true
	end
	return false
end

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

function lock_with_turns.Init(_owner)
	sminigames.SetStandartScriptName(_owner, lock_with_turns)
	
	lock_with_turns.turns = {}
	lock_with_turns.widgets = {}
	
	local w = _owner.GetWidgets()
	while not w.IsEnd() do
		local widget = __cast(w.Get(), sf.gui.CImageWidget)
		if widget then
			local turn_num = tonumber(widget.GetId().c_str())
			if turn_num then
				table.insert(lock_with_turns.widgets, widget)
				assert(turn_num <= #lock_with_turns.params.init_values and not lock_with_turns.turns[turn_num])
				local init_angle = lock_with_turns.ValueToAngle(lock_with_turns.params.init_values[turn_num])
				local turn = {widget = widget, angle = init_angle}
				lock_with_turns.turns[turn_num] = turn
				lock_with_turns.RotateTurn(turn, init_angle)
			end
		end
		w.Next()		
	end
end

function lock_with_turns.RotateTurn(_turn, _angle)
	local angle = lock_with_turns.NormalAngle(_angle)
	local size = _turn.widget.GetSize()
	local matrix = sf.misc.MatrixTranslation(-size.X / 2.0, -size.Y / 2.0)
	matrix = matrix * sf.misc.MatrixRotation(angle, 0.0, 0.0)
	matrix = matrix * sf.misc.MatrixTranslation(size.X / 2.0, size.Y / 2.0)
	_turn.widget.SetRotation(matrix)
	_turn.angle = angle
end

function lock_with_turns.NormalAngle(_a)
	local a = math.mod(math.abs(_a) + 2.0 * math.pi, 2.0 * math.pi)
	if _a < 0 then a = 2.0 * math.pi - a end
	return a
end

function lock_with_turns.ValueToAngle(_value)
	local interval = lock_with_turns.params.interval
	local length = interval[2]-interval[1]+1
	local angle = 2.0 * math.pi * (length - _value-interval[1]) / length
	return angle
end

function lock_with_turns.IsTurnContains(_turn, _pos)
	local offset = _turn.widget.GetOffset()
	local size = _turn.widget.GetSize()
	local center = offset + size / 2.0
	local pos = sf.misc.FloatVector(_pos.X, _pos.Y)
	pos = pos * sf.misc.MatrixRotation(-_turn.angle, center.X, center.Y)
	pos = pos - offset
	return sminigames.IsVisiblePoint(_turn.widget.GetImage(), pos.X, pos.Y)
end
