local scr;
-------------------------------------------
-- my functions
-------------------------------------------
function CreateNode(id, linkType, xi, yi)
    local node = {};
    
    node.id = id;
    node.kx = xi;
    node.ky = yi;
    node.active  = nil;
	node.passive = nil;
	node.ls      = nil;
	node.state   = 0;
	node.bActive = false;
    
    CreateNodeBtn(node, game_state.startX + (xi-1) * game_state.cellWidth, game_state.startY + (yi-1) * game_state.cellHeight);
    CreateLinkSys(node, linkType);
    
    node.GetNearNode = function(side)
        local xi = node.kx;
        local yi = node.ky;
        
        if     side == 1 then   xi = xi + 1;
        elseif side == 2 then   yi = yi + 1;
        elseif side == 3 then   xi = xi - 1;
        elseif side == 4 then   yi = yi - 1;
        end
        
        if xi < 1 or xi > game_state.cols or yi < 1 or yi > game_state.rows then
            return nil;
        end
        
        return game_state.field[yi][xi];
    end
    
    node.SetActive = function(active)
        node.active:SetVisible(active);
        node.passive:SetVisible(not active);
        
        node.bActive = active;
    end
    
    node.IsActive = function()
        return  node.bActive;
    end
    
    node.IsAnimating = function()
        return node.ls.IsAnimating();
    end
    
    node.HasObject = function(objectName)
        return ((node.active:GetName() == objectName) or (node.passive:GetName() == objectName));
    end
    
    node.OnClick = function()
        node.ls.Rotate();
        MakeSoundEntity('19_Switching_nodes'):Play(false);
    end
    
    game_state.field[yi][xi] = node;
end
-------------------------------------------
function GetNode(xi, yi)
    return game_state.field[yi][xi];
end
-------------------------------------------
function CreateNodeBtn(node, x, y)
    local passive_depth = 110;
    local active_depth  = 111;

    local anim1 = CreateAnimation('data/Texture/Minigames/Corridor/Over_Active.png',0,0,71,70,1,0)
	anim1:SetHotSpot(35,35)
	anim1:SetBlendMode(2)
	anim1:SetColor('0xFFFFFFFF')
	scr:CreateObject('Over_Active'..node.id,anim1,x,y,active_depth,0,1,1);

	local anim2 = CreateAnimation('data/Texture/Minigames/Corridor/Over_Passive.png',0,0,50,50,1,0)
	anim2:SetHotSpot(25,25)
	anim2:SetBlendMode(2)
	anim2:SetColor('0xFFFFFFFF')
	scr:CreateObject('Over_Passive'..node.id,anim2,x,y,passive_depth,0,1,1)

	node.active = play_field:GetObject('Over_Active'..node.id);
	node.passive = play_field:GetObject('Over_Passive'..node.id);

	game_state.ignore_on_highlight['Over_Active'..node.id] = true
	game_state.ignore_on_highlight['Over_Passive'..node.id] = true

	game_state.custom_cursors['Over_Active'..node.id] = 'CursorActive'
	game_state.custom_cursors['Over_Passive'..node.id] = 'CursorActive'
end

-------------------------------------------
--[[
---------------------
  image     |  type |
---------------------           4
    |       |       |           |
    O       |   1   |       3 - 0 - 1
    |       |       |           |
---------------------           2 
    |       |       |
    o -     |   2   |
            |       |
---------------------
    |       |       |
    o -     |   3   |
    |       |       |
---------------------
]]
-------------------------------------------
function CreateLinkSys(node, type)
    local ls = {};
    node.ls = ls;
    
    ls.real = {0,0,0,0};
    ls.objs = {};
    
    if type == 1 then
        CreateLink(node, 4);
        CreateLink(node, 2);
    elseif type == 2 then
        CreateLink(node, 4);
        CreateLink(node, 1);
    elseif type == 3 then
        CreateLink(node, 4);
        CreateLink(node, 1);
        CreateLink(node, 2);
    end
    
    ls.GetLink = function(side)
        local linkId = ls.real[side];
        return node.ls.objs[linkId];
    end
    
    ls.Shift = function()
        local rotateCount = math.random(1,4);
        
        for i=1, rotateCount do
            local buf = ls.real[4];
            
            for j=1, 4 do
                ls.real[j], buf = buf, ls.real[j];
            end
        end
        
        for i=1, 4 do
            if ls.GetLink(i) ~= nil then
                ls.GetLink(i).setSide(i);
            end
        end
    end
    
    ls.Rotate = function()
        local mask = {};
        for i=1, 4 do
            if i > 1 then   mask[i] = ls.real[i - 1];
            else            mask[i] = ls.real[4];
            end
        end
        
        local hideArr = {};
        local showArr = {};
        
        for i=1, 4 do
            if (mask[i] == 0 and ls.real[i] ~= 0) or
               (mask[i] ~= 0 and ls.real[i] == 0)
            then
                if mask[i] == 0 then
                    hideArr[table.getn(hideArr) + 1] = i;
                else
                    showArr[table.getn(showArr) + 1] = i;
                end
            end
        end
        
        for i, side in ipairs(hideArr) do
            local link = ls.GetLink(side);
            link.RotateTo(showArr[i]);
            
            ls.real[ side ] = 0;
            ls.real[ showArr[i] ] = link.id;
        end
    end
    
    ls.IsAnimating = function()
        for i=1, 4 do
            if (ls.GetLink(i) ~= nil) and (ls.GetLink(i).IsAnimating() == true) then
                return true;
            end
        end
        return false;
    end
end
-------------------------------------------
function CreateLink(node, side)
	local link  = {};
	link.obj = CreateContactObject();
	link.obj:AttachTo(node.passive);
	
	link.effect = nil;
	link.id     = game_state.freeID;
	
	link.setSide = function(side)
	    local a = side2angle(side);
	    local x = game_state.connectR * math.cos(a);
	    local y = game_state.connectR * math.sin(a);
	    
	    link.obj:SetX(x);
	    link.obj:SetY(y);
	    link.obj:SetAngle(a + M_PI_OVER_2)
	    
	    link.side = side;
	end
	
	link.RotateTo = function(newSide)
	    local a    = side2angle(newSide);
	    local newX = game_state.connectR * math.cos(a);
	    local newY = game_state.connectR * math.sin(a);
	    
	    if link.effect ~= nil then
	        link.effect:Destroy();
	    end
	    
	    link.effect = CreateAnimationEffector();
	    link.effect:AttachTo(link.obj);
	    
	    link.effect:SetKey(_TRACK_X, 0,                                 link.obj:GetX());
	    link.effect:SetKey(_TRACK_X, game_state.rotateLinksTime * 0.5,  0);
	    link.effect:SetKey(_TRACK_X, game_state.rotateLinksTime,        newX);
	    
	    link.effect:SetKey(_TRACK_Y, 0,                                 link.obj:GetY());
	    link.effect:SetKey(_TRACK_Y, game_state.rotateLinksTime * 0.5,  0);
	    link.effect:SetKey(_TRACK_Y, game_state.rotateLinksTime,        newY);
	    
	    link.effect:SetKey(_TRACK_A, 0,                                     link.obj:GetAngle());
	    link.effect:SetKey(_TRACK_A, game_state.rotateLinksTime * 0.5,      link.obj:GetAngle());
	    link.effect:SetKey(_TRACK_A, game_state.rotateLinksTime * 0.501,    a + M_PI_OVER_2);
	    link.effect:SetKey(_TRACK_A, game_state.rotateLinksTime,            a + M_PI_OVER_2);
	    
	    link.effect:Play();
	    
	    link.side = newSide;
	end
	
	link.IsAnimating = function()
	    return (link.effect ~= nil and link.effect:IsPaused() == false);
	end
	
	link.setSide(side);
	
	node.ls.real[side] = link.id;
	node.ls.objs[link.id] = link;
end
-------------------------------------------
function CreateContactObject()
    game_state.freeID = game_state.freeID + 1;
    
    local anim = CreateAnimation('data/Texture/Minigames/Corridor/contact.png',0,0,22,45,1,0)
	anim:SetHotSpot(11,22)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	scr:CreateObject('contact'..game_state.freeID,anim,0,0,-1,0,1,1)
	
	return play_field:GetObject('contact'..game_state.freeID);
end
-------------------------------------------
function side2angle(side)
    if side == 1 then
        return 0;
    elseif side == 2 then
        return M_PI_OVER_2;
    elseif side == 3 then
        return M_PI;
    elseif side == 4 then
        return -M_PI_OVER_2;
    end
end
-------------------------------------------
function getInverseSide(side)
    if     side == 1 then   return 3;
    elseif side == 2 then   return 4;
    elseif side == 3 then   return 1;
    elseif side == 4 then   return 2;
    end
end
-------------------------------------------
function BuildField(mask, cols, rows)
    local linkType = nil;
    local id = 1;
    
    for yi=1, rows do
        game_state.field[yi] = {};
        
        for xi=1, cols do
            linkType = mask[yi][xi];
            
            if linkType ~= 0 then
                CreateNode(id, linkType, xi, yi);
                GetNode(xi,yi).ls.Shift();
                GetNode(xi,yi).SetActive(false);
                id = id + 1;
            end
        end
    end
end
-------------------------------------------
function BuildWay(startXi, startYi, enterSide)
    local node = nil;
    
    for yi=1, game_state.rows do
        for xi=1, game_state.cols do
            node = GetNode(xi, yi);
            
            if node ~= nil then
                node.state = 0;
            end
        end
    end
    
    local node = GetNode(startXi, startYi);
    
    if node ~= nil and node.ls.GetLink(enterSide) ~= nil and node.ls.GetLink(enterSide).IsAnimating() == false then
        AddNodeInWay(node);
    end
    
    for yi=1, game_state.rows do
        for xi=1, game_state.cols do
            node = GetNode(xi, yi);
            
            if node ~= nil then
                node.SetActive(node.state == 1);
            end
        end
    end
end
-------------------------------------------
function AddNodeInWay(node)
    node.state = 1;
    local near = nil;
    local side = nil;
    
    for i=1, 4 do
        if node.ls.real[i] ~= 0 and node.ls.GetLink(i).IsAnimating() == false then
            near = node.GetNearNode(i);
            side = getInverseSide(i);
            
            if near ~= nil and near.state == 0 and near.ls.GetLink(side) ~= nil and near.ls.GetLink(side).IsAnimating() == false then
                AddNodeInWay(near)
            end
        end
    end
end
-------------------------------------------
function FinalAnimation()
	for i = 1, game_state.rows do
		for j = 1, game_state.cols do
			node = GetNode(i, j);
			if(node and node.IsActive()) then
				local af = CreateAnimationEffector()
				af:AttachTo(node.active)
				af:SetKey(_TRACK_O, 0.0, 1.0)
				af:SetKey(_TRACK_O, 0.4, 0.5)
				af:SetKey(_TRACK_O, 0.8, 1.0)
				af:SetKey(_TRACK_O, 1.2, 1.5)
				af:SetKey(_TRACK_O, 2.0, 2.0)
				af:SetKey(_TRACK_O, 2.4, 1.0)
				af:SetKey(_TRACK_O, 2.8, 0.5)
				af:SetKey(_TRACK_O, 3.2, 1.0)
				af:SetKey(_TRACK_O, 3.6, 1.5)
				af:SetKey(_TRACK_O, 4.0, 2.0)
				af:SetKey(_TRACK_O, 4.4, 1.0)
				af:SetKey(_TRACK_O, 4.8, 0.5)
				af:SetKey(_TRACK_O, 5.2, 1.0)
				af:SetKey(_TRACK_O, 5.6, 1.5)
				af:SetKey(_TRACK_O, 6.0, 2.0)
				af:Play()
			end
		end
	end	
end
-------------------------------------------
function IsMGCompleted()
    local node = GetNode(4, 4)
    
    return (node ~= nil) and node.IsActive() == true and (node.ls.GetLink(1) ~= nil) and (node.ls.GetLink(1).IsAnimating() == false);
end
-------------------------------------------
function OnWinMiniGame()
	FinalAnimation();
    AddGameState('MiniGameCompleted', '19')
    TimerDoString("StartLevel('data/Scenes/19_Corridor_Main.lua')", 3., play_field:GetTimer())
    MakeSoundEntity('19_Switching_complete'):Play(false);
end
-------------------------------------------
function SkipMiniGame()
    game_state.rotateLinksTime = 0.3
    game_state.skiped = true;
end
-------------------------------------------
function OnSceneLoad(screen)
    scr = screen;

    LoadData(screen)
    DefaultOnSceneLoad('data/Scenes/19_Corridor_MG.lua', 'mini_game', 'data/Scenes/19_Corridor_Main.lua')

    game_state.cellWidth = 92;
    game_state.cellHeight = 91.3;
    game_state.startX = 380.5;
    game_state.startY = 238;
    
    game_state.rows = 4;
    game_state.cols = 4;
    
    game_state.connectR = 29;
    game_state.rotateLinksTime = 0.5;
    
    game_state.skiped    = false;
    game_state.completed = false;
    game_state.freeID = 0;
    game_state.field = {}
    game_state.fieldMask = {{3,0,2,3},
                            {3,2,0,1},
                            {1,2,3,2},
                            {3,3,0,2}};
    
    game_state.skipCompl = 0;
    game_state.skipArr    = {};
    for i=1, 7 do
        game_state.skipArr[i] = {}
    end
    game_state.skipArr[1].yi=1;     game_state.skipArr[1].xi=1; game_state.skipArr[1].nextLink=2;
    game_state.skipArr[2].yi=2;     game_state.skipArr[2].xi=1; game_state.skipArr[2].nextLink=1;
    game_state.skipArr[3].yi=2;     game_state.skipArr[3].xi=2; game_state.skipArr[3].nextLink=2;
    game_state.skipArr[4].yi=3;     game_state.skipArr[4].xi=2; game_state.skipArr[4].nextLink=1;
    game_state.skipArr[5].yi=3;     game_state.skipArr[5].xi=3; game_state.skipArr[5].nextLink=1;
    game_state.skipArr[6].yi=3;     game_state.skipArr[6].xi=4; game_state.skipArr[6].nextLink=2;
    game_state.skipArr[7].yi=4;     game_state.skipArr[7].xi=4; game_state.skipArr[7].nextLink=1;
                            
    BuildField(game_state.fieldMask, game_state.cols, game_state.rows);

	ShowMonolog('C_19_MINIGAME', 'C_19_MINIGAME')
	
	LoadSound('19_Switching_complete', 'data/Sound/19_Corridor/Switching_complete.ogg');
	LoadSound('19_Switching_nodes', 'data/Sound/19_Corridor/Switching_nodes.ogg');
end

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

function OnUpdate(dt)
    if game_state.completed == false then
        BuildWay(1,1,3);
        
        if game_state.skiped == true then
            local node = nil;
            
            for i=1, 7 do
                node = GetNode(game_state.skipArr[i].xi, game_state.skipArr[i].yi)
                
                if node.IsAnimating() == true then
                    break;
                end
                
                if node.IsActive() == false or node.ls.GetLink(game_state.skipArr[i].nextLink) == nil then
                    node.ls.Rotate();
                    MakeSoundEntity('19_Switching_nodes'):Play(false);
                    break;
                end
            end
        end
        
        if IsMGCompleted() == true then
            game_state.completed = true;
            
            
            
            OnWinMiniGame();
        end
    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)
    DefaultOnObjectMouseLeave(object)
end

function OnObjectMouseDown(object)
    local is_wrong_click = not DefaultOnObjectMouseDown(object)
    local obj_name = object:GetName()
    local pf = play_field
    
    if game_state.completed == false and game_state.skiped == false then
        local node    = nil;
        local isClick = false;
        
        for yi=1, game_state.rows do
            for xi=1, game_state.cols do
                node = GetNode(xi, yi);
                
                if node ~= nil and node.IsAnimating() == false and node.HasObject(obj_name) == true then
                    node.OnClick();
                    isClick = true;
                    break;
                end
            end
            
            if isClick == true then
                break;
            end
        end
    end
    
    DefaultOnObjectMouseDownProcessed(is_wrong_click);
end

function OnObjectMouseUp(object)
    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/Corridor/fon.JPG',0,0,1024,768,1,0)
	anim:SetHotSpot(512,384)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('fon',anim,512,384,100,0,1,1)
end

function FreeData(screen)
end
-- CLEAR_ON_SAVE_END
