function GetPartByName(name)
    for i, v in ipairs(game_state.parts) do
        if v:GetName() == name then
            return v;
        end
    end
    return nil;
end
--------------------------------------------------------------------------

function NormalizeAngle(angle)
    return angle - M_TWO_PI * math.floor(angle / M_TWO_PI)
end

function GetAngleTo(obj, x, y)
    local dx = x - obj:GetX();
    local dy = y - obj:GetY();
    
    return math.atan2(dy, dx);
end
--------------------------------------------------------------------------

function SavePartPositions()
    for i, v in ipairs(game_state.parts) do
        local nodeName = '05_lock_part(part_'..i..'_angle)';
        
        if HasGameState(nodeName) then
            RemoveGameState(nodeName);
        end
        
        AddGameState(nodeName, '0'..v:GetAngle());
    end
end
--------------------------------------------------------------------------

function LoadPartPositions()
    for i, v in pairs(game_state.parts) do
        local nodeName = '05_lock_part(part_'..i..'_angle)'
        
        if HasGameState(nodeName) then
            local value = GetGameState(nodeName):Get(0)
            
            if string.find(value, '-') then
                value = string.sub(value, 2)
            end
        
            value = tonumber(value)
            v:SetAngle(value);
        end
    end
end
--------------------------------------------------------------------------

function IsQuad()
    for i, part in ipairs(game_state.parts) do
        local delta_angle = nil;
        
        if game_state.trg_angles[i] == 0 and part:GetAngle() > M_PI then
            delta_angle = part:GetAngle() - M_TWO_PI;
        else
            delta_angle = part:GetAngle() - game_state.trg_angles[i];
        end
        
        if math.abs(delta_angle) > game_state.angle_range then
            return false;
        end
    end
    return true;
end
--------------------------------------------------------------------------

function OpenLock()
    game_state.completed_minigame = true;
    
    local time      = 0.5;
    local quad_time = 1;
    local delay     = 0.5;
    
    for i, part in ipairs(game_state.parts) do
        local eff = CreateAnimationEffector();
        
        eff:AttachTo( part );
        eff:SetMode(_ANIM_FWD + _ANIM_NOLOOP);
        
        local trgt_a = game_state.trg_angles[i]
        if trgt_a == 0 and part:GetAngle() > M_PI then
            trgt_a = M_TWO_PI;
        end
        
        eff:SetKey(_TRACK_A, 0,    part:GetAngle());
        eff:SetKey(_TRACK_A, time, trgt_a);
        eff:Play();
        
        game_state.custom_cursors[part:GetName()] = nil;
    end
    
    local quad = play_field:GetObject('quad');
    local eff  = CreateAnimationEffector();
    eff:AttachTo( quad );
    eff:SetMode(_ANIM_FWD + _ANIM_NOLOOP);
    
    eff:SetKey(_TRACK_O, 0,                      quad:GetOpacity());
    eff:SetKey(_TRACK_O, time,                   quad:GetOpacity());
    eff:SetKey(_TRACK_O, time + quad_time * 1/3, 1. );
    eff:SetKey(_TRACK_O, time + quad_time * 2/3, 0.5);
    eff:SetKey(_TRACK_O, time + quad_time,       1.);
    eff:Play();
    
    TimerDoString('OnCompleted()', time + quad_time + delay, play_field:GetTimer());
end
--------------------------------------------------------------------------

function OnCompleted()
    if not HasGameState('MiniGameCompleted', '05_lock') then
        AddGameState('MiniGameCompleted', '05_lock');
        AddGameState('ClosedScene', game_state.scene_path)
    end
    
    StartLevel(game_state.parent_scene);
end
--------------------------------------------------------------------------

function SkipMiniGame()
	OpenLock();
end
--------------------------------------------------------------------------

function StartMG()
    game_state.part_added = true;
    AddNoteToNotebook('JAIL_CELLS');

    game_state.level_mode = 'mini_game';
    play_field:GetLevelScreen():GetPanel():SetMode(PM_MINIGAME)
    
    for i=1, OBJECTS_AMOUNT do
        game_state.custom_cursors['elem_'..i] = 'CursorActive';
    end
    
    if IsQuad() then
        OpenLock();
    end
end

function OnSceneLoad(screen)
	LoadData(screen)
    DefaultOnSceneLoad('data/Scenes/Minigames/05_lock.lua', 'quest', 'data/Scenes/05_Gandarmery_Wart.lua')
    
    play_field:GetObject('elem_1'):SetOpacity(0.);
    play_field:GetObject('elem_3'):SetOpacity(0.);
    play_field:GetObject('quad'  ):SetOpacity(0.);
    
    OBJECTS_AMOUNT = 4;
    
    game_state.skip_minigame      = false;
    game_state.completed_minigame = false;
	game_state.screen             = screen;
	game_state.locsApplied        = 0;
    
    game_state.part_added  = false
    game_state.active_part = nil;
    game_state.delta_angle = 0;
    game_state.angle_range = 10 / 180 * M_PI;
    game_state.parts       = {}
    game_state.trg_angles  = {0, M_PI_OVER_2, M_PI, M_PI + M_PI_OVER_2};
    
    for i=1, OBJECTS_AMOUNT do
        game_state.parts[i] = play_field:GetObject('elem_'..i);
        game_state.parts[i]:SetAngle(math.random() * M_TWO_PI);
    end
    
    LoadPartPositions();
    SavePartPositions();
    
    RegisterToolApplyInfo('05_lock_part', 'area_part',  nil, nil,   function(tool_name)
                                                                        play_field:GetObject('elem_1'):ChangeOpacity(0., 1., 0.5);
                                                                        game_state.locsApplied = game_state.locsApplied + 1
                                                                        
                                                                        if (game_state.locsApplied == 2) then
                                                                            play_field:GetObject('area_part'):SetVisible(false);
                                                                            StartMG();
                                                                            ShowMonolog('C_05_MG_HINT_2', 'C_05_MG_HINT_2')
                                                                        else
                                                                            game_state.custom_cursors['area_part'] = 'CursorActive'
                                                                        end
                                                                        
                                                                        PlaySound('05_PlaceLockPart');
                                                                    end,
                                                                    function(tool_name)
                                                                        play_field:GetObject('elem_1'):SetOpacity(1.);
                                                                        game_state.locsApplied = game_state.locsApplied + 1
                                                                        
                                                                        if (game_state.locsApplied == 2) then
                                                                            play_field:GetObject('area_part'):SetVisible(false);
                                                                            StartMG();
                                                                        end
                                                                    end,
                                                                    function()
                                                                        ShowMonolog('C_05_HINT_NO_LOCK_PARTS', 'C_05_HINT_NO_LOCK_PARTS');
                                                                    end);
    
    RegisterToolApplyInfo('04_lock_part', 'area_part',  nil, nil,   function(tool_name)
                                                                        play_field:GetObject('elem_3'):ChangeOpacity(0., 1., 0.5);
                                                                        game_state.locsApplied = game_state.locsApplied + 1
                                                                        
                                                                        if (game_state.locsApplied == 2) then
                                                                            play_field:GetObject('area_part'):SetVisible(false);
                                                                            StartMG();
                                                                            ShowMonolog('C_05_MG_HINT_2', 'C_05_MG_HINT_2')
                                                                        else
                                                                            game_state.custom_cursors['area_part'] = 'CursorActive'
                                                                        end
                                                                        
                                                                        PlaySound('05_PlaceLockPart');
                                                                    end,
                                                                    function(tool_name)
                                                                        play_field:GetObject('elem_3'):SetOpacity(1.);
                                                                        game_state.locsApplied = game_state.locsApplied + 1
                                                                        
                                                                        if (game_state.locsApplied == 2) then
                                                                            play_field:GetObject('area_part'):SetVisible(false);
                                                                            StartMG();
                                                                        end
                                                                    end,
                                                                    function()
                                                                        ShowMonolog('C_05_HINT_NO_LOCK_PARTS', 'C_05_HINT_NO_LOCK_PARTS');
                                                                    end);
                                                                    
    if not game_state.part_added then
        ShowMonolog('C_05_MG_HINT_1', 'C_05_MG_HINT_1')
    else
        ShowMonolog('C_05_MG_HINT_2', 'C_05_MG_HINT_2')
    end
end

function OnSceneFree(screen)
    DefaultOnSceneFree()
    FreeData(screen)
end

function OnUpdate(dt)
    local can_move = game_state.part_added and not game_state.completed_minigame;
    
    if can_move and game_state.active_part then
        local mouse             = GetMousePos();
        local angle_to_mouse    = GetAngleTo(game_state.active_part, mouse.x, mouse.y);
        
        game_state.active_part:SetAngle(angle_to_mouse + game_state.delta_angle);
    end
    
    DefaultOnUpdate(dt)
end

function OnAllItemsFound()
    DefaultOnAllItemsFound()
end

function OnLose()
    DefaultOnLose()
end

function OnExit()
    DefaultOnExit()
end

function OnClose()
    DefaultOnClose()
end

function OnObjectMouseEnter(object)
    DefaultOnObjectMouseEnter(object)
end

function OnObjectMouseLeave(object)
    if game_state.active_part and object:GetName() == game_state.active_part:GetName() then
        game_state.active_part = nil
        
        SavePartPositions();
        
        if IsQuad() then
            OpenLock();
        end
    end
    
    DefaultOnObjectMouseLeave(object)
end

function OnObjectMouseDown(object)
    local is_wrong_click = not DefaultOnObjectMouseDown(object)
    local obj_name = object:GetName()
    local pf = play_field
    
    local can_move = game_state.part_added and not game_state.completed_minigame;
    
    if can_move then
        game_state.active_part = GetPartByName(obj_name);
        
        if game_state.active_part then
            local mouse             = GetMousePos();
            local angle_to_mouse    = GetAngleTo(game_state.active_part, mouse.x, mouse.y);
            game_state.delta_angle  = game_state.active_part:GetAngle() - angle_to_mouse;
            
            game_state.custom_cursors[game_state.active_part:GetName()] = 'CursorHand';
            
            PlaySound('05_UseLockParts');
        end
    end
	
    DefaultOnObjectMouseDownProcessed(is_wrong_click);
end

function OnObjectMouseUp(object)
    if game_state.part_added and game_state.active_part then
        game_state.custom_cursors[game_state.active_part:GetName()] = 'CursorActive';
        
        game_state.active_part:SetAngle(NormalizeAngle(game_state.active_part:GetAngle()))
        local a = game_state.active_part:GetAngle();
        local target = nil;
        for i, v in ipairs(game_state.parts) do
            if v:GetName() == game_state.active_part:GetName()  then
                target = game_state.trg_angles[i];
                break;
            end
        end
        
        game_state.active_part = nil;
        SavePartPositions();
        
        if IsQuad() then
            OpenLock();
        end
    end
    
    DefaultOnObjectMouseUp(object)
end

function OnObjectFound(object)
    DefaultOnObjectFound(object)
end

function OnToolFound(tool)
    DefaultOnToolFound(tool)
end

function OnToolApply(tool, object)
    DefaultOnToolApply(tool, object)
end

function OnObjectReachPanel(object)
    DefaultOnObjectReachPanel(object)
end

function OnButtonPress(title)
    DefaultOnButtonPress(title)
end

function OnMouseMove(previous_pos, current_pos)
    DefaultOnMouseMove(previous_pos, current_pos)
end

function OnSetTool(tool)
    DefaultOnSetTool(tool)
end

function LevelLoaded(level)
end

function OnSceneDestroy(playfield)
    DefaultOnSceneDestroy(playfield)
end

--------------------------------------------------------------------------------
-- CLEAR_ON_SAVE_BEGIN
-- Do not edit code listed below (generated by the editor on each save)
function LoadData(screen)
	local anim = CreateAnimation('data/Texture/Minigames/05_lock/back.jpg',0,0,1024,768,1,0)
	anim:SetHotSpot(512,384)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('back',anim,512,384,98,0,1,1)
	local anim = CreateAnimation('data/Texture/Minigames/05_lock/mini_game_zamok_detal.png',0,0,112,112,1,0)
	anim:SetHotSpot(71,41)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('elem_1',anim,591,366,101,0,1,1)
	local anim = CreateAnimation('data/Texture/Minigames/05_lock/mini_game_zamok_kvadrat.png',0,0,170,170,1,0)
	anim:SetHotSpot(85,85)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('quad',anim,512,447,102,0,1,1)
	local anim = CreateAnimation('data/Texture/Minigames/05_lock/mini_game_zamok_detal.png',0,0,112,112,1,0)
	anim:SetHotSpot(71,41)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('elem_2',anim,587,528,101,90,1,1)
	local anim = CreateAnimation('data/Texture/Minigames/05_lock/mini_game_zamok_detal.png',0,0,112,112,1,0)
	anim:SetHotSpot(71,41)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('elem_3',anim,425,524,101,180,1,1)
	local anim = CreateAnimation('data/Texture/Minigames/05_lock/mini_game_zamok_detal.png',0,0,112,112,1,0)
	anim:SetHotSpot(71,41)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('elem_4',anim,428,362,101,270,1,1)
	local anim = CreateAnimation('data/Texture/Scenes/Common/area.png',0,0,8,8,1,0)
	anim:SetHotSpot(4,4)
	anim:SetBlendMode(2)
	anim:SetColor('0x01FFFFFF')
	screen:CreateObject('area_part',anim,513,448,103,0,40,30)
end

function FreeData(screen)
end
-- CLEAR_ON_SAVE_END
