-- Items to use
-- 19_button 20_button

function OnSceneLoad(screen)
    LoadData(screen)
    DefaultOnSceneLoad('data/Scenes/Minigames/20_lock.lua', 'quest', 'data/Scenes/20_Mill_Inside_Main.lua')
    pf = play_field;
    g_timer = pf:GetTimer();    
    
    minigame_completed = false;
    button_normal_applied = false;
    button_wide_applied = false;
    skip_minigame = false
    
    SLOTS_SIDE = 4;
    CreateSlots();
    
    OBJECTS_AMOUNT = 9;
    CreateObjects();
    
    SetPositions();
    CorrectFreeSlots();
    
    RegisterToolApplyInfo('19_button', 'button_6', nil, nil, function(tool_name)
                                                                            button_normal_applied = true;
                                                                            
                                                                            play_field:GetObject('button_6'):ChangeOpacity(0, 1, 0.2);
                                                                            SetMiniGame();
                                                                            
                                                                            PlaySound('20_PlaceParts');
                                                                        end,
                                                                        
                                                                        function(tool_name)
                                                                            button_normal_applied = true;
                                                                            
                                                                            play_field:GetObject('button_6'):SetOpacity(1);
                                                                            SetMiniGame();
                                                                        end);
                                                                        
    RegisterToolApplyInfo('20_button', 'button_2', nil, nil,  function(tool_name)
                                                                            play_field:GetObject('button_2'):ChangeOpacity(0, 1, 0.2);
                                                                            
                                                                            button_wide_applied = true;
                                                                            SetMiniGame();
                                                                            
                                                                            PlaySound('20_PlaceParts');
                                                                        end,
                                                                        function(tool_name)
                                                                            play_field:GetObject('button_2'):SetOpacity(1);
                                                                            
                                                                            button_wide_applied = true;
                                                                            SetMiniGame();
                                                                        end);
                                                                        
    if not button_normal_applied then
        ShowMonolog('C_20_LOCK_NEED_N_BUTTON_HINT', 'C_20_LOCK_NEED_N_BUTTON_HINT');
    elseif not button_wide_applied then
        ShowMonolog('C_20_LOCK_NEED_W_BUTTON_HINT', 'C_20_LOCK_NEED_W_BUTTON_HINT');
    end
end

function SetMiniGame()
    if button_normal_applied and button_wide_applied then
        game_state.level_mode = 'mini_game';
        play_field:GetLevelScreen():GetPanel():SetMode(PM_MINIGAME)
        ShowMonolog('C_20_LOCK_START_HINT', 'C_20_LOCK_START_HINT')
    end
end

function CreateSlots()
    -- initial coords to calculate slots layout
    local first_obj = pf:GetObject('button_1');
    x1, y1 = first_obj:GetX(), first_obj:GetY();
    dx, dy = first_obj:GetAnimation():GetWidth(), first_obj:GetAnimation():GetHeight() + 5;
    
    Slots = {}
    for i = 1, SLOTS_SIDE do
        Slots[i] = {};
        for j = 1, SLOTS_SIDE do
            Slots[i][j] = {}
            -- some corrections because objects are not square
            local delta = 0;
            if i == 2 then
                delta = 6;
            elseif i == 4 then
                delta = 2;
            end
            Slots[i][j].Pos = Vector(x1 + dx * (j - 1) + 1, y1 + dy * (i - 1) + delta);
            
            Slots[i][j].Row = i;
            Slots[i][j].Col = j;
        end
    end
end

function SetPositions()
    for i = 1, OBJECTS_AMOUNT do
        if HasGameState(game_state.scene_path .. i) then
            local row = tonumber(GetGameState(game_state.scene_path .. i):Get(0))
            local col = tonumber(GetGameState(game_state.scene_path .. i):Get(1))
            RemoveGameState(game_state.scene_path .. i);
            Objects[i].Slot = Slots[row][col];
        end
        Objects[i].Entity:SetXY(Objects[i].Slot.Pos.x, Objects[i].Slot.Pos.y);
    end
end

function CorrectFreeSlots()
    for i = 1, SLOTS_SIDE do
        for j = 1, SLOTS_SIDE do
            Slots[i][j].Free = true;
        end    
    end
    for i = 1, OBJECTS_AMOUNT do
        ConsiderDim(Objects[i], false)
    end
end

function CreateObjects()
    Objects = {}
    for i = 1, OBJECTS_AMOUNT - 1 do
        Objects[i] = {};
        Objects[i].Entity = pf:GetObject('button_' .. i);
        Objects[i].Index = i;
        Objects[i].High = 0;
        if i == 2 or i == 7 then
            Objects[i].Wide = 1;
        else
            Objects[i].Wide = 0;
        end
    end
    Objects[OBJECTS_AMOUNT] = {}
    Objects[OBJECTS_AMOUNT].Entity = pf:GetObject('zasov');
    Objects[OBJECTS_AMOUNT].Index = OBJECTS_AMOUNT;
    Objects[OBJECTS_AMOUNT].High = 1;
    Objects[OBJECTS_AMOUNT].Wide = 1;
    
    Objects[2].Entity:SetOpacity(0.01);
    Objects[6].Entity:SetOpacity(0.01);
    -- Initial layout
    Objects[1].Slot = Slots[1][1]
    Objects[2].Slot = Slots[1][2]
    Objects[3].Slot = Slots[1][4]
    Objects[4].Slot = Slots[2][3]
    Objects[5].Slot = Slots[3][3]
    Objects[6].Slot = Slots[4][1]
    Objects[7].Slot = Slots[4][2]
    Objects[8].Slot = Slots[4][4]
    Objects[9].Slot = Slots[2][1]
end

function GetObjectByName(obj_name)
    for i = 1, OBJECTS_AMOUNT do
        if Objects[i].Entity:GetName() == obj_name then
            return Objects[i];
        end
    end
    return nil;
end

function IsComplete()
    if Objects[OBJECTS_AMOUNT].Slot.Row ~= 2 then
        return false;
    end
    for i = 2, 3 do
        for j = Objects[OBJECTS_AMOUNT].Slot.Col + Objects[OBJECTS_AMOUNT].Wide + 1, SLOTS_SIDE do
            if not Slots[i][j].Free then
                return false;
            end
        end
    end
    minigame_completed = true;
    FinishMiniGame();
end

function SkipMiniGame()
    if skip_minigame == true or minigame_completed == true then
        return
    end
    
    -- all objects on bolt road should be deleted
    for i = 2, 3 do
        for j = Objects[OBJECTS_AMOUNT].Slot.Col + 2, SLOTS_SIDE do
            for k = 1, OBJECTS_AMOUNT - 1 do
                if Slots[i][j] and Objects[k].Slot == Slots[i][j] then
                    Objects[k].Entity:ChangeOpacity(1, 0, 0.2);
                end
            end
        end
    end
    -- how long to move depends on current bolt position
    local delta = Objects[OBJECTS_AMOUNT].Slot.Col == 1 and 87 or 0;
    TimerDoString('FinishMiniGame(' .. delta .. ')', 0.2, g_timer);
        
    minigame_completed = true
    skip_minigame = true
    
    --[[if not HasGameState('MiniGameCompleted', '20_lock') then
        AddGameState('MiniGameCompleted', '20_lock');
    end
    if not HasGameState('ClosedScene', game_state.scene_path) then
        AddGameState('ClosedScene', game_state.scene_path);
    end]]
end

function FinishMiniGame(extra)
    extra = extra or 0;
    bolt = Objects[OBJECTS_AMOUNT].Entity;
    local ef = CreateAnimationEffector()
    ef:SetMode(_ANIM_FWD + _ANIM_NOLOOP);
    ef:AttachTo(bolt)
    ef:SetKey(_TRACK_X, 0, bolt:GetX());
    ef:SetKey(_TRACK_X, 1.5, bolt:GetX() + 110 + extra);
    ef:Play();
    
    PlaySound('20_MoveBar');
    
    if not HasGameState('MiniGameCompleted', '20_lock') then
        AddGameState('MiniGameCompleted', '20_lock');
    end
    if not HasGameState('ClosedScene', game_state.scene_path) then
        AddGameState('ClosedScene', game_state.scene_path);
    end
    
    TimerDoString('StartLevel(game_state.parent_scene);', 1.5, g_timer);
end

--[[function RegisterCompleteMiniGame()
    if not HasGameState('MiniGameCompleted', '20_lock') then
        AddGameState('MiniGameCompleted', '20_lock');
    end
    if not HasGameState('ClosedScene', game_state.scene_path) then
        AddGameState('ClosedScene', game_state.scene_path);
    end
    StartLevel(game_state.parent_scene);
end]]

-- Sets free or not to Slots that object being. Consider its dimension
function ConsiderDim(obj, free)
    obj.Slot.Free = free;
    if obj.Wide == 1 then
        Slots[obj.Slot.Row][obj.Slot.Col + 1].Free = free;
    end
    if obj.High == 1 then
        Slots[obj.Slot.Row + 1][obj.Slot.Col].Free = free;
        Slots[obj.Slot.Row + 1][obj.Slot.Col + 1].Free = free;
    end
end

-- Checks is slot is free to current DragObject. Consider its dimension
function IsSlotFree(DragObject, row, col)
    if not Slots[row][col].Free then
        return false;
    end    
    if DragObject.Wide == 1  and (not Slots[row][col + 1] or not Slots[row][col + 1].Free) then
        return false;
    end
    if DragObject.High == 1  and
        ((not Slots[row + 1] or not Slots[row + 1][col] or not Slots[row + 1][col].Free) or
        (not Slots[row + 1] or not Slots[row + 1][col] or not Slots[row + 1][col + 1].Free)) then
        return false;
    end
    return true;
end

function CalculateBoundaries(DragObject)
    local row = DragObject.Slot.Row;
    local col = DragObject.Slot.Col;
    
    top = Slots[row][col].Pos.y;
    bottom = top;
    left = Slots[row][col].Pos.x;
    right = left;
    
    if DragObject.Entity:GetName() ~= 'zasov' then
        for i = row - 1, row - 2, -1 do
            if Slots[i] and Slots[i][col] and Slots[i][col].Free 
                        and Slots[i][col + DragObject.Wide] and Slots[i][col + DragObject.Wide].Free then
                top = Slots[i][col].Pos.y;
            else
                break;
            end
        end
        for i = row + 1, row + 2 do
            if Slots[i] and Slots[i][col] and Slots[i][col].Free 
                        and Slots[i][col + DragObject.Wide] and Slots[i][col + DragObject.Wide].Free then
                bottom = Slots[i][col].Pos.y;
            else
                break;
            end
        end
    end
    
    for j = col - 1, col - 2, -1 do
        if DragObject.High == 1 then
            if Slots[row][j] and Slots[row][j].Free and
                Slots[row + 1] and Slots[row + 1][j] and Slots[row + 1][j].Free then
                left = Slots[row][j].Pos.x;
            else
                break;
            end
        elseif Slots[row][j] and Slots[row][j].Free then
            left = Slots[row][j].Pos.x;
        else
            break;
        end
    end
    for j = col + 1 + DragObject.Wide, col + 2 + DragObject.Wide do
        if DragObject.High == 1 then
            if Slots[row][j] and Slots[row][j].Free and
                Slots[row + 1] and Slots[row + 1][j] and Slots[row + 1][j].Free then
                right = Slots[row][j - DragObject.Wide].Pos.x;
            else
                break;
            end
        elseif Slots[row][j] and Slots[row][j].Free then
            right = Slots[row][j - DragObject.Wide].Pos.x;
        else
            break;
        end
    end
    
    -- if can move in two directions - set flag
    if (top ~= Slots[row][col].Pos.y or bottom ~= Slots[row][col].Pos.y) and
        (left ~= Slots[row][col].Pos.x or right ~= Slots[row][col].Pos.x) then
        dual_direction = true;
    else
        dual_direction = false;
    end
end

----------------------------------------------------------------
-- Drag Functions
function StartDrag(obj)
    DragObject = obj;
    DragObject.Entity:SetZ(DragObject.Entity:GetZ() + 1)
    
    DragDeltaX = GetMousePos().x - DragObject.Entity:GetX()
    DragDeltaY = GetMousePos().y - DragObject.Entity:GetY()
        
    ConsiderDim(DragObject, true);
    -- boundaries calculation   
    CalculateBoundaries(DragObject);
    game_state.custom_cursors[DragObject.Entity:GetName()] = 'CursorHand';
    game_state.ignore_on_highlight[DragObject.Entity:GetName()] = true;
end

function UpdateDrag()
    if DragObject then
        local mouse_x = GetMousePos().x - DragDeltaX
        local mouse_y = GetMousePos().y - DragDeltaY
        
        -- if can move in two directions
        if dual_direction then
            dual_direction = false;
            local delta_x = math.abs(mouse_x - DragObject.Slot.Pos.x);
            local delta_y = math.abs(mouse_y - DragObject.Slot.Pos.y);
            -- what direction is in priority
            if delta_x > delta_y then
                top = DragObject.Slot.Pos.y;
                bottom = top;
            elseif delta_x < delta_y then
                left = DragObject.Slot.Pos.x;
                right = left;
            else    -- not or diagonal moving
                dual_direction = true; -- one more try
                DragObject.Entity:SetXY(DragObject.Slot.Pos.x, DragObject.Slot.Pos.x)
            end
        end
        
        if mouse_x < left then
            mouse_x = left;
        elseif mouse_x > right then
            mouse_x = right;
        end
        
        if mouse_y < top then
            mouse_y = top;
        elseif mouse_y > bottom then
            mouse_y = bottom;
        end
        
        DragObject.Entity:SetXY(mouse_x, mouse_y)
    end
end

function StopDrag()
    if DragObject then
        local delta = 52;
        DragObject.Entity:SetZ(DragObject.Entity:GetZ() - 1)
        for i = 1, SLOTS_SIDE do
            local is_break = false;
            for j = 1, SLOTS_SIDE do
                if IsSlotFree(DragObject, i, j) and 
                   math.abs(DragObject.Entity:GetX() - Slots[i][j].Pos.x) < delta and
                   math.abs(DragObject.Entity:GetY() - Slots[i][j].Pos.y) < delta then
                    DragObject.Entity:Move(Slots[i][j].Pos.x - DragObject.Entity:GetX(), Slots[i][j].Pos.y - DragObject.Entity:GetY(), 0.2)
                    DragObject.Slot = Slots[i][j];  -- now we are at this slot
                    ConsiderDim(DragObject, false);
                    is_break = true;
                    break;
                end
            end
            if is_break then
                break;
            end
        end
        game_state.custom_cursors[DragObject.Entity:GetName()] = nil;
        game_state.ignore_on_highlight[DragObject.Entity:GetName()] = nil
        DragObject = nil;
        DragDeltaX = nil
        DragDeltaY = nil
        IsComplete();
    end
end
----------------------------------------------------------------

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

function OnUpdate(dt)
    DefaultOnUpdate(dt)
    if DragObject and not minigame_completed then
        UpdateDrag();
    end
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)
    DefaultOnObjectMouseLeave(object)
end

function OnObjectMouseDown(object)
    local is_wrong_click = not DefaultOnObjectMouseDown(object)
    local obj_name = object:GetName()
    local pf = play_field
    
    if not button_normal_applied then
        if obj_name ~= 'Zoom_Zamok_Background' then
            ShowMonolog('C_20_LOCK_NEED_N_BUTTON_HINT', 'C_20_LOCK_NEED_N_BUTTON_HINT');
        end
    elseif not button_wide_applied then
        if obj_name ~= 'Zoom_Zamok_Background' then
            ShowMonolog('C_20_LOCK_NEED_W_BUTTON_HINT', 'C_20_LOCK_NEED_W_BUTTON_HINT');
        end
    else
        local obj = GetObjectByName(obj_name);
        if obj and not minigame_completed then
            PlaySound('20_MoveParts');
            
            StartDrag(obj);
        end
    end 

    DefaultOnObjectMouseDownProcessed(is_wrong_click);
end

function OnObjectMouseUp(object)
    DefaultOnObjectMouseUp(object)
    if DragObject and not minigame_completed then
        StopDrag();
    end
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)
    
    if not minigame_completed then
        for i = 1, OBJECTS_AMOUNT do
            AddGameState(game_state.scene_path .. i, tostring(Objects[i].Slot.Row))
            AddGameState(game_state.scene_path .. i, tostring(Objects[i].Slot.Col))
        end
    end
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/20_lock/Zoom_Zamok_Background.jpg',0,0,1024,768,1,0)
	anim:SetHotSpot(512,384)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('Zoom_Zamok_Background',anim,512,384,100,0,1,1)
	local anim = CreateAnimation('data/Texture/Minigames/20_lock/lyamka.png',0,0,60,303,1,0)
	anim:SetHotSpot(30,151)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('lyamka',anim,759,295,110,0,1,1)
	local anim = CreateAnimation('data/Texture/Minigames/20_lock/lyamki.png',0,0,162,321,1,0)
	anim:SetHotSpot(81,160)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('lyamki',anim,259,293,110,0,1,1)
	local anim = CreateAnimation('data/Texture/Minigames/20_lock/zasov.png',0,0,847,231,1,0)
	anim:SetHotSpot(372,74)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('zasov',anim,428,237,101,0,1,1)
	local anim = CreateAnimation('data/Texture/Minigames/20_lock/button_1.png',0,0,87,82,1,0)
	anim:SetHotSpot(43,41)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('button_1',anim,427,151,103,0,1,1)
	local anim = CreateAnimation('data/Texture/Minigames/20_lock/button_2.png',0,0,178,82,1,0)
	anim:SetHotSpot(43,41)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('button_2',anim,512,151,103,0,1,1)
	local anim = CreateAnimation('data/Texture/Minigames/20_lock/button_1.png',0,0,87,82,1,0)
	anim:SetHotSpot(43,41)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('button_3',anim,689,151,103,0,1,1)
	local anim = CreateAnimation('data/Texture/Minigames/20_lock/button_1.png',0,0,87,82,1,0)
	anim:SetHotSpot(43,41)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('button_4',anim,605,243,103,0,1,1)
	local anim = CreateAnimation('data/Texture/Minigames/20_lock/button_1.png',0,0,87,82,1,0)
	anim:SetHotSpot(43,41)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('button_5',anim,604,325,103,0,1,1)
	local anim = CreateAnimation('data/Texture/Minigames/20_lock/button_1.png',0,0,87,82,1,0)
	anim:SetHotSpot(43,41)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('button_6',anim,427,414,103,0,1,1)
	local anim = CreateAnimation('data/Texture/Minigames/20_lock/button_2.png',0,0,178,82,1,0)
	anim:SetHotSpot(43,41)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('button_7',anim,512,414,103,0,1,1)
	local anim = CreateAnimation('data/Texture/Minigames/20_lock/button_1.png',0,0,87,82,1,0)
	anim:SetHotSpot(43,41)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('button_8',anim,689,414,103,0,1,1)
end

function FreeData(screen)
end
-- CLEAR_ON_SAVE_END
