


MinigameSafe = {}
MinigameSafe.mDisplay = level_mgr:get_layer('MinigameSafe')
MinigameSafe.mFlyQueue = {}

MinigameSafe.mGemsCount = 24

MinigameSafe.mRotateLock = false
MinigameSafe.mGemsClickLock = false

MinigameSafe.mPushes = { 'magenta', 'green', 'yellow' , 'left' , 'right', 'blue', 'darkblue', 'red' }
MinigameSafe.mRounds = {}

MinigameSafe.mConnectors = { 
["round_magenta"] = { [1] = {"down","left","top"} } ,
["round_green"] =   { [1] = {"down","right","top"} },
["round_yellow"] =   { [1] = {"right","left","left"} },
["round_blue"] =   { [1] = {"left","right","right"} },
["round_darkblue"] =   { [1] = {"top","left","down"} },
["round_red"] =   { [1] = {"top","right","down"} }, 

["round_left"] = {
[1] = {"top","magenta","down"} ,
[2] =  {"left","yellow","right"} ,
[3] =  {"down","darkblue","top"} ,
[4] =  {"right","right","left"}
},

["round_right"] = {
[1] =  {"top","green","down"} ,
[2] =  {"right","blue","left"} ,
[3] =  {"down","red","top"} ,
[4] =  {"left","left","right"}
} 

}

GemsRound = {}

function GemsRound:update()
    for id = 1, 4 do
        if self.mGems[id] ~= 0 then
            self.mGems[id]:setPos( self.mGemsPoints[id]:getPos(true) )
        end
    end
end

function GemsRound:attachGem(gem,id)
    self.mGems[id] = gem
    if gem ~= 0 then
        MinigameSafe.mDisplay:set_element_eh( gem:getName() , function() MinigameSafe:onGemDown(self,gem) end)
    end
end

function GemsRound:detachGemFromId(id)
    self.mGems[id] = 0
end

function GemsRound:detachGem(name)
    for id = 1, 4 do
        if self.mGems[id]:getName() == name then self.mGems[id] = nil end
    end
end

function GemsRound:getGem(name)
    for id = 1, 4 do
        if self.mGems[id]:getName() == name then return self.mGems[id] end
    end
end

function GemsRound:getGemFromId(id)
    return self.mGems[id]
end

function GemsRound:rotate()
    timer_mgr:add_timer(function() 
            self.mElement:setAngle( self.mElement:getAngle() + (1 * 0.0174532925) ) 
            self:update()
        end, 0.005, 90)
    timer_mgr:add_timer(function() MinigameSafe.mGemsClickLock = false end, 0.01*90, 1, "lock_timer") -- essence evil !
    self.mTop = self.mTop - 1
    if self.mTop == 0 then self.mTop = 4 end
end

function GemsRound:getSideGemsId(side) 
    local side_id = 1
    if side == 'top' then
        side_id =  self.mTop
    elseif side == 'right' then
        side_id =  self.mTop + 1
    elseif side == 'down' then
        side_id =  self.mTop + 2
    elseif side == 'left' then
        side_id =  self.mTop + 3
    end
    if side_id > 4 then side_id = side_id - 4 end
    
    return side_id
end

function GemsRound:getSideGems(side) 
    return self.mGems[self:getSideGemsId(side)]
end

function GemsRound:getSidePoint(side) 
    return self.mGemsPoints[self:getSideGemsId(side)]
end

function GemsRound:new(name)
    local concrete = {}
	setmetatable(concrete, self)
	self.__index = self
	concrete.mName = name
	concrete.mElement = MinigameSafe.mDisplay:get_element(name)
    concrete.mGems = {}
    concrete.mGemsPoints = {}
    concrete.mTop = 1

	for id = 1, 4 do
	    local point = concrete.mElement:findChild('gem'..id)
	    concrete.mGemsPoints[id] = point
	    concrete.mGems[id] = 0
	end
	
	return concrete	
end

FlyItem = {}

function FlyItem:new(object, params) -- { start_pos, end_pos }
    local concrete = {}
	setmetatable(concrete, self)
	self.__index = self
	
	if not params then params = {} end
	if not params.func then params.func = function() end end
	if not params.start_pos then params.start_pos = Vec2(0,0) end
	if not params.end_pos then params.end_pos = Vec2(0,0) end
	
	concrete.mItem = object
	concrete.mFunc = params.func
    concrete.mStartPos = params.start_pos
    concrete.mPos = concrete.mStartPos
    concrete.mItem:setPos(concrete.mPos)
    concrete.mEndPos = params.end_pos
 
	return concrete	
end

----------------------------------------------------------------------------------------------------
function MinigameSafe:toFlyQueue(item, params)
    local fly_item = FlyItem:new(item,params)
    table.insert(MinigameSafe.mFlyQueue, fly_item)
end

----------------------------------------------------------------------------------------------------
function MinigameSafe:onFlyEnd(item)

    if MinigameSafe:winCheck() then
        -- win
        MinigameWin:show(3,function() level_mgr:switch_to_level('Map') fade_panel:fade_out() end)
        return
    end
    
    MinigameSafe.mRotateLock = false
    MinigameSafe.mGemsClickLock = false
    
end

----------------------------------------------------------------------------------------------------
function MinigameSafe:onPressPush(id)
    if not MinigameSafe.mRotateLock then MinigameSafe:RotateRound(id) end
end

----------------------------------------------------------------------------------------------------
function MinigameSafe:RotateRound(id)
    local round = MinigameSafe.mRounds[id]
    MinigameSafe.mGemsClickLock = true
    round:rotate()
end

----------------------------------------------------------------------------------------------------
function MinigameSafe:getRound(name)
    for i = 1, #MinigameSafe.mRounds do
        if MinigameSafe.mRounds[i].mName == name then
            return MinigameSafe.mRounds[i]
        end
    end
    return nil
end

----------------------------------------------------------------------------------------------------
function MinigameSafe:winCheck()
    for i = 1, #MinigameSafe.mRounds do
        for j = 1, 4 do
            local gem = MinigameSafe.mRounds[i]:getGemFromId(j)
            if gem ~= 0 then
                local len = string.len(MinigameSafe.mRounds[i].mName) - 6
                local sub_name =  string.sub(gem:getName(), 5, 5+len-1)
                if MinigameSafe.mRounds[i].mName ~= 'round_'..sub_name then
                    return false
                end
            end
        end
    end
    return true
end

----------------------------------------------------------------------------------------------------
function MinigameSafe:onGemDown(round, gem)
        
        if MinigameSafe.mGemsClickLock then return end
        
        for c_side = 1, #MinigameSafe.mConnectors[round.mName] do
            local connector = MinigameSafe.mConnectors[round.mName][c_side]
            local l_gem = round:getSideGems(connector[1])
            if l_gem == gem then
                local a_id = round:getSideGemsId(connector[1])
                round:detachGemFromId(a_id)
                
                -- lock rotate
                MinigameSafe.mRotateLock = true
                MinigameSafe.mGemsClickLock = true
                
                --find other round and detach gem
                local other_round = MinigameSafe:getRound('round_'..connector[2])
                local other_id = other_round:getSideGemsId(connector[3])
                local r_gem = other_round:getGemFromId(other_id)
                other_round:detachGemFromId(other_id)
                other_round:attachGem(l_gem,other_id)
                
                local a_point = round:getSidePoint(connector[1])
                local b_point = other_round:getSidePoint(connector[3])
                
                MinigameSafe:toFlyQueue( l_gem , {start_pos = a_point:getPos(true) , end_pos = b_point:getPos(true)} )
                if r_gem ~= 0 then
                    round:attachGem(r_gem,a_id)
                    MinigameSafe:toFlyQueue( r_gem , {start_pos = b_point:getPos(true) , end_pos = a_point:getPos(true)} )
                end
                return
            end
        end
end

----------------------------------------------------------------------------------------------------
function MinigameSafe:getRandomGems()
    
    --plane gems
    local gems = {}
    for i = 1, #MinigameSafe.mPushes do
        local color = MinigameSafe.mPushes[i]
        for j = 1, 4 do
           local gem =  MinigameSafe.mDisplay:get_element('gem_'..color..'_'..j)
           table.insert(gems,gem)
        end
    end
    
    -- shuffle gems
    table.foreach(gems,
            function(idx)
                local rand = get_random(1, #gems + 1)
                local random_gem = gems[rand]
                gems[rand] = gems[idx]
                gems[idx] = random_gem
            end)
            
    return gems
end

----------------------------------------------------------------------------------------------------
function MinigameSafe:onShow()

    Panel:show(false)
    Panel:showTimer(false)

    for i = 1, #MinigameSafe.mPushes do
        MinigameSafe.mDisplay:set_element_eh( 'push_'..MinigameSafe.mPushes[i] , 
        function() 
            MinigameSafe:onPressPush(i) 
        end)
    end
    
    local random_gems = MinigameSafe:getRandomGems()
    
    local gems_idx = 1
    for i = 1, #MinigameSafe.mPushes do 
        local round = GemsRound:new( 'round_'..MinigameSafe.mPushes[i] )
        if MinigameSafe.mPushes[i] ~= 'left' and MinigameSafe.mPushes[i] ~= 'right' then
            for j = 1,4 do
                round:attachGem( random_gems[gems_idx], j)
                gems_idx = gems_idx + 1
            end
            round:update()
        end
            table.insert(MinigameSafe.mRounds, round)
    end

    addUpdateListener(MinigameSafe)
    
end


----------------------------------------------------------------------------------------------------
function MinigameSafe:onHide()
    removeUpdateListener(MinigameSafe)
end


----------------------------------------------------------------------------------------------------
function MinigameSafe:update(aElapsed)

    table.foreach(MinigameSafe.mFlyQueue,
        function(item_id)
            local elem = MinigameSafe.mFlyQueue[item_id]
            elem.mPos = elem.mItem:getPos(true)
            local dir =  (MinigameSafe.mFlyQueue[item_id].mEndPos - elem.mPos)
            local distance = dir:len()
            dir:norm()
            local new = elem.mPos + dir
            if distance < 1 then
                elem.mItem:setPos(Vec2(MinigameSafe.mFlyQueue[item_id].mEndPos.x, MinigameSafe.mFlyQueue[item_id].mEndPos.y))
                table.remove(MinigameSafe.mFlyQueue, fly_item)
                MinigameSafe:onFlyEnd(elem.mItem)
            else
                elem.mItem:setPos(new)
            end
        end)
end