-- ----------------------------------------------------------------------------
layoutBoathouseLock.mExits =
{
}
layoutBoathouseLock.mPOIs = 
{
}
layoutBoathouseLock.mHOs = 
{
}
layoutBoathouseLock.mButtons =
{
	[objectIoBoathouseLockReset] =
	{
	}
}

layoutBoathouseLock.mPieces = 
{
    objectIoBoathouseLockPiece1,
    objectIoBoathouseLockPiece2,
    objectIoBoathouseLockPiece3,
    objectIoBoathouseLockPiece4
}

layoutBoathouseLock.mSlotsSlide = 
{
    { x=240, y=169 },
    { x=310, y=169 },
    { x=381, y=169 },
    { x=452, y=169 },
    { x=452, y=240 },
    { x=452, y=311 },
    { x=452, y=381 },
    
}

layoutBoathouseLock.mSlotsDial =
{
    { x=120, y=50, 	piece=nil  	}
}

layoutBoathouseLock.mDialLinks =
{
	{
    	[2] =
    	{
	        rotationIdx = 1
	    },
	    [6] =
	    {
        	rotationIdx = 2
    	},
    },    
}

layoutBoathouseLock.mDialAngles = 
{
    0, 90
}

layoutBoathouseLock.mPieceAngles = 
{
    0, 90, 180, 270
}

layoutBoathouseLock.mLockPins = {
    objectBoathouseLockPin1,
    objectBoathouseLockPin2,
    objectBoathouseLockPin3,
    objectBoathouseLockPin4,
}

layoutBoathouseLock.mLockLights =
{
	objectBoathouseLockLight1,
	objectBoathouseLockLight2,
	objectBoathouseLockLight3,
	objectBoathouseLockLight4,
}

layoutBoathouseLock.mLockLightsOff =
{
	objectBoathouseLockLightOff1,
	objectBoathouseLockLightOff2,
	objectBoathouseLockLightOff3,
	objectBoathouseLockLightOff4,
}

layoutBoathouseLock.mSlotsStart =
{
    [objectIoBoathouseLockPiece1] = { idx = 1, rotationIdx = 1 },
    [objectIoBoathouseLockPiece2] = { idx = 2, rotationIdx = 3 },
    [objectIoBoathouseLockPiece3] = { idx = 3, rotationIdx = 3 },
    [objectIoBoathouseLockPiece4] = { idx = 4, rotationIdx = 3 }
}

layoutBoathouseLock.mSlotsSolution = 
{
    [objectIoBoathouseLockPiece1] = { idx = 4, rotationIdx = 1 },
    [objectIoBoathouseLockPiece2] = { idx = 3, rotationIdx = 1 },
    [objectIoBoathouseLockPiece3] = { idx = 2, rotationIdx = 1 },
    [objectIoBoathouseLockPiece4] = { idx = 1, rotationIdx = 1 }
}

layoutBoathouseLock.mSlots             = {}
layoutBoathouseLock.mDial              = objectIoBoathouseLockDial
layoutBoathouseLock.mDialRotationIdx   = 1
layoutBoathouseLock.mPieceSelected     = nil
layoutBoathouseLock.mNeighborRadius    = 75
layoutBoathouseLock.mDialDirection     = layoutBoathouseLock.kDirectionCW

layoutBoathouseLock.kDirectionCCW      = 1
layoutBoathouseLock.kDirectionCW       = 2
layoutBoathouseLock.kSlotSlide         = 1
layoutBoathouseLock.kSlotDial          = 2
layoutBoathouseLock.kRotateDuration    = 300
layoutBoathouseLock.kDeg2Rad           = 0.017453292519943295769236907684886
layoutBoathouseLock.kPinAnimDeltaY     = 100
layoutBoathouseLock.kPinAnimDuration   = 400
layoutBoathouseLock.kLightAnimDuration = 200

layoutBoathouseLock.mPuzzle = true
layoutBoathouseLock.mFirstTime = true

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:onConstruct()

	Layout.onConstruct(self)
    objectIoBoathouseLockReset:ss(kActive)
    objectIoBoathouseLockDial:ss(kIdle)
    for i, piece in ipairs(self.mPieces) do
        piece:ss(kIdle)
    end
end
-- ----------------------------------------------------------------------------
function layoutBoathouseLock:onInitialize()
	Layout.onInitialize(self)
	-- build slot table
	for i, settings in ipairs(self.mSlotsSlide) do
	    local slot = {}
	    self:initSlot("slide" .. i, self, slot, settings, self.kSlotSlide)
	    table.insert(self.mSlots, slot)
	    slot.slideIdx = i
	    slot.dialIdx = nil
    end	
    for i, settings in ipairs(self.mSlotsDial) do
        local slot = {}
        self:initSlot("dial" .. i, self.mDial, slot, settings, self.kSlotDial)
	    table.insert(self.mSlots, slot)
	    slot.slideIdx = nil
	    slot.dialIdx = i
    end	
    for i, pin in ipairs(self.mLockPins) do
        pin.lockY = pin:y()
        pin.openY = pin.lockY + self.kPinAnimDeltaY
    end
    for i, light in ipairs(self.mLockLightsOff) do
    	light:setAlpha(0)
    end
    for i, light in ipairs(self.mLockLights) do
    	light:setAlpha(0)
    end
    -- reset pieces
    self:initPieces()
    -- reset puzzle
    self:resetPuzzle()
    -- set pivot point dial
    self.mDial:setPivotPoint("center")
    self.mDial:animate(
    	{ rotation=self.mDialAngles[1] }
    )
end
-- ----------------------------------------------------------------------------
function layoutBoathouseLock:onShow()
	Layout.onShow(self)
	if (self.mFirstTime) then	
    	think("MESSAGE_BOATHOUSE_LOCK")	
		self.mFirstTime = false
	end
end
-- ----------------------------------------------------------------------------
function layoutBoathouseLock:onClickButton(button)
	
	if (button == objectIoBoathouseLockReset) then
		self:resetPuzzle()
		--self:solve()
		return true	
	end
	
	return Layout.onClickButton(self, button)

end
-- ----------------------------------------------------------------------------
function layoutBoathouseLock:initSlot(name, parent, slot, settings, type)
    slot.piece  = nil
    slot.type   = type
    slot.sprite = parent:newChildSprite( { name = name, x = settings.x, y = settings.y } )
    slot.sprite.globalX = parent.globalX
    slot.sprite.globalY = parent.globalY
    slot.sprite.localX = parent.localX
    slot.sprite.localY = parent.localY
end
-- ----------------------------------------------------------------------------
function layoutBoathouseLock:onStartStart()
	Layout.onStartStart(self)
	-- HELP reset
	queueHelpDialog(27, 381, 239)
	queueHelpArrow(28, 27, 347, 478)
end
-- ----------------------------------------------------------------------------
function layoutBoathouseLock:onSolve()
	Layout.onSolve(self)
    for i, light in ipairs(self.mLockLightsOff) do
		light:setAlpha(1)
	end
	-- solve
    if (not self:checkSolved()) then
        self:resetPieces()
        for piece, settings in pairs(self.mSlotsSolution) do
            piece.rotationIdx = settings.rotationIdx
            local slot = self.mSlots[settings.idx]
            self:setPiece(piece, slot)
        end
    end
end
-- ----------------------------------------------------------------------------
function layoutBoathouseLock:onSolveStart()
	Layout.onSolveStart(self)
    -- anim
    objectBoathouseDoorOpen:ss(kIdle)
    objectPoiBoathouseOpen:ss(kActive)
    self:animSolved()
end
-- ----------------------------------------------------------------------------
function layoutBoathouseLock:onSolveFinish()
	Layout.onSolveFinish(self)
	objectExitDoorEntrance:ss(kActive)
    
    objectPoiDoorLock:ss(kDone)
    objectPoiBoathouseClosed:ss(kDone)
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:onMouseDown(x, y)
	local result = Layout.onMouseDown(self, x, y)
    if (self:isPuzzling()) then
        for i, piece in ipairs(self.mPieces) do
            if (piece:isInside(x, y)) then
               -- select piece
               self:selectPiece(piece)
               return true, false
            end
        end
        if (self.mDial:isInside(x, y)) then
            self:rotateDial()
            return true, false
        end
    end
	return result
end
-- ----------------------------------------------------------------------------
function layoutBoathouseLock:onMouseMoved(x, y)
    local result = Layout.onMouseMoved(self, x, y)
    if (self:isPuzzling()) then
        if (self.mPieceSelected ~= nil) then
            -- get nearest free neighbor
            local minHypSlot        = math.huge
            local nearestSlot	    = nil
            local minHypNeighbor    = math.huge
            local nearestNeighbor   = nil
            local slotPieceSelected = self.mPieceSelected.slot
            local nearestPath		= {}
            for i, slot in ipairs(self.mSlots) do
                if ((slotPieceSelected == slot) or (self:isNeighbor(slotPieceSelected, slot) and self:isFreeSlot(slot))) then
                    local slotx = slot.sprite:globalX(0, 0)
                    local sloty = slot.sprite:globalY(0, 0)
                    -- get delta x and y                
                    local dx = math.abs(slotx - x)
                    local dy = math.abs(sloty - y)
                    -- get delta hyp
                    local h  = math.sqrt((dx * dx) + (dy * dy))
                    -- check if closer then previous
                    if (h < minHypSlot) then
                        minHypSlot  = h
                        nearestSlot = slot
                    end
                    if (slot ~= slotPieceSelected and h < minHypNeighbor) then
						minHypNeighbor	 = h
						nearestNeighbor	 = slot
						-- set path props
						nearestPath.sx   = math.min(slotPieceSelected.sprite:globalX(0, 0), slotx)
			            nearestPath.sy   = math.min(slotPieceSelected.sprite:globalY(0, 0), sloty)
						nearestPath.ex   = math.max(slotPieceSelected.sprite:globalX(0, 0), slotx)
						nearestPath.ey   = math.max(slotPieceSelected.sprite:globalY(0, 0), sloty)
                    end
                end
            end
            -- move to nearest
            if (nearestSlot ~= nil and nearestSlot ~= slotPieceSelected) then
                self:setPiece(self.mPieceSelected, nearestSlot)
                playAudioTrack("SLIDE_BLOCK")
            end
            -- move piece to path
            if (nearestNeighbor ~= nil) then
                local px = math.min(nearestPath.ex, math.max(x, nearestPath.sx))
                local py = math.min(nearestPath.ey, math.max(y, nearestPath.sy))
				self.mPieceSelected:setX(self.mPieceSelected.slot.sprite:localX(px, py))
				self.mPieceSelected:setY(self.mPieceSelected.slot.sprite:localY(px, py)) 
			end
        end
    end
    return result    
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:onMouseUp(x, y)
    local result = Layout.onMouseUp(self, x, y)
    if (self:isPuzzling()) then
        if (self.mPieceSelected ~= nil) then
		    self:setPiece(self.mPieceSelected, self.mPieceSelected.slot)
		end
        self:deselectPiece()
		if (self:checkSolved()) then
			self:solve()
		end
    end
    return result
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:onRotateDialFinished()
    self.mIsDialRotating = false
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:initPieces()
    for i, piece in ipairs(self.mPieces) do
        piece:setAnchorPoint("center")
        piece:setPivotPoint("center")
    end
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:resetPieces()
    -- reset slots and pieces
    for i, slot in ipairs(self.mSlots) do
        if (slot.piece ~= nil) then
            slot.piece.slot = nil
            slot.piece = nil
        end
    end
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:resetPuzzle()
    self:resetPieces()
    for piece, settings in pairs(self.mSlotsStart) do
        piece.rotationIdx = settings.rotationIdx
        self:setPiece(piece, self.mSlots[settings.idx])
    end
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:checkSolved()
    for i, piece in ipairs(self.mPieces) do
        if (not self:isPieceSolved(piece)) then
            return false
        end
    end
    return true
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:setPiece(piece, slot)
    if (piece.slot ~= nil) then
        piece.slot.piece = nil
    end
    -- slot should be free, clear all slots before reset
    assert(slot.piece == nil)
    slot.piece = piece
    piece.slot = slot
    local rotation = self.mPieceAngles[piece.rotationIdx]
    if (slot.sprite:parent() == self.mDial) then
        rotation = rotation - self.mDialAngles[self.mDialRotationIdx]
    end
    piece:becomeChildOf(slot.sprite)
    piece:setRotation(rotation * self.kDeg2Rad)
    piece:setX(0)
    piece:setY(0)
    for i, slot in ipairs(self.mSlots) do
        if (slot.piece ~= nil and self:isPieceSolved(slot.piece)) then
            -- anim piece solved
            self:animSlotCorrectPiece(slot)
        else
            -- anim piece unsolved
            self:animSlotIncorrectPiece(slot)
        end
    end
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:hasPieceSelected()
    return self.mPieceSelected ~= nil
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:selectPiece(piece)
    self.mPieceSelected = piece
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:deselectPiece()
    self.mPieceSelected = nil
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:isPieceSolved(piece)
    local settings = self.mSlotsSolution[piece]
    local slot     = self.mSlots[settings.idx]
    if (slot.piece ~= piece) then
        return false
    end
    if (slot.piece ~= nil) then
        if (slot.piece.rotationIdx ~= settings.rotationIdx) then
            return false
        end
    end
    return true
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:isFreeSlot(slot)
   return slot.piece == nil
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:isNeighbor(slot, neighbor)
    
    -- dial slot check
    if (slot.type == self.kSlotDial) then
        if (self:isNeighborDial(neighbor, slot)) then
            return true
        end
        return false
    end
    -- dial neighbor check
    if (neighbor.type == self.kSlotDial) then
        if (self:isNeighborDial(slot, neighbor)) then
            return true
        end
        return false
    end
    -- slide neighbor check
    -- vertical neighbor check
    if (slot.sprite:x() == neighbor.sprite:x()) then
        if (slot.sprite:y() ~= neighbor.sprite:y()) then
            local dif = math.abs(slot.sprite:y() - neighbor.sprite:y())
            if (dif < self.mNeighborRadius) then
                return true
            end
        end
    end
    -- horizontal neighbor check
    if (slot.sprite:y() == neighbor.sprite:y()) then
        if (slot.sprite:x() ~= neighbor.sprite:x()) then
            local dif = math.abs(slot.sprite:x() - neighbor.sprite:x())
            if (dif < self.mNeighborRadius) then
                return true
            end
        end
    end
    return false
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:isNeighborDial(slot, dialSlot)
    if (self.mDialLinks[dialSlot.dialIdx] ~= nil and self.mDialLinks[dialSlot.dialIdx][slot.slideIdx] ~= nil) then
        if (self.mDialLinks[dialSlot.dialIdx][slot.slideIdx].rotationIdx == self.mDialRotationIdx) then
            return true
        end
    end
    return false
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:rotateDial()
    if (not self.mIsDialRotating) then
        local prevAngle = self.mDialAngles[self.mDialRotationIdx]
        if (self.mDialRotationIdx <= 1) then
            -- rotate CW
            self.mDialDirection = self.kDirectionCW
        end
        if (self.mDialRotationIdx >= #self.mDialAngles) then
            -- rotate CCW
            self.mDialDirection = self.kDirectionCCW
        end
        if (self.mDialDirection == self.kDirectionCW) then
            -- increase rotation
            self.mDialRotationIdx = self.mDialRotationIdx + 1
        end
        if (self.mDialDirection == self.kDirectionCCW) then
            -- decrease rotation
            self.mDialRotationIdx = self.mDialRotationIdx - 1
        end        
        local rotation = self.mDialAngles[self.mDialRotationIdx] - prevAngle
        if (rotation ~= 0) then
            self.mIsDialRotating = true
            self.mDial:animate(
                { drotation=rotation, duration=self.kRotateDuration }
            )
            self:animate(
                { delay=self.kRotateDuration                                            },
                { call=function() layoutBoathouseLock.onRotateDialFinished(self)  end   }
            )
            -- Play the audio feedback
            playAudioTrack("ROTATE_DISC")
            -- correct rotation of pieces in dial
            for i, slot in ipairs(self.mSlots) do
                if (slot.type == self.kSlotDial) then
                    local piece = slot.piece
                    if (piece ~= nil) then
                        local idx   = piece.rotationIdx
                        local angle = self.mPieceAngles[idx] + rotation
                        if (angle < 0) then
                            angle = angle + 360
                        end
                        if (angle >= 360) then
                            angle = angle - 360
                        end
                        -- find angle idx
                        for j, angleCompare in ipairs(self.mPieceAngles) do
                            if (angle == angleCompare) then
                                idx = j
                            end
                        end
                        piece.rotationIdx = idx
                    end
                end
            end
        end 
    end
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:animSlotCorrectPiece(slot)
    local pin = self.mLockPins[slot.slideIdx]
    if (pin ~= nil) then
        self:animSlotPin(pin, pin.openY)
    end
    local light = self.mLockLights[slot.slideIdx]
    if (light ~= nil) then
		self:animSlotLight(light, 1)
	end
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:animSlotIncorrectPiece(slot)
    local pin = self.mLockPins[slot.slideIdx]
    if (pin ~= nil) then
        self:animSlotPin(pin, pin.lockY)
    end
    local light = self.mLockLights[slot.slideIdx]
    if (light ~= nil) then
		self:animSlotLight(light, 0)
	end
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:animSlotPin(pin, y)
    pin:cancelAnimations()
    local fac       = math.abs(pin:y() - y) / self.kPinAnimDeltaY
    local duration  = self.kPinAnimDuration * fac
    pin:animate(
        { y=y,  duration=duration }
    )
    -- Only play sounds if the pin needs to move
    if (duration > 0) then
		if (pin:y() < y) then
			self:playPinSound(pin, "PIN_OPEN")
		else
			self:playPinSound(pin, "PIN_CLOSED")
		end
    end
end

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:animSlotLight(light, alpha)
	light:cancelAnimations()
	local fac		= math.abs(light:alpha() - alpha)
	local duration  = self.kLightAnimDuration * fac
	light:animate(
		{ alpha = alpha, duration=duration }
	)
end	

-- ----------------------------------------------------------------------------
function layoutBoathouseLock:animSolved()
	
	local delay = 0
	
	-- animate lights
	for i=1,3 do
		for j, light in ipairs(self.mLockLights) do
			light:animate(
				{ delay=delay					},
				{ command="playTrack track=LED_OFF", target=self },
				{ alpha=0,		duration=100	}
			)
			light:animate(
				{ delay=delay+100				},
				{ command="playTrack track=LED_ON", target=self },
				{ alpha=1, 		duration=100 	}
			)
		end	
		delay = delay + 500
	end
	
	-- animate 
	objectBoathouseDoorOpen:animate(
		{ delay=delay					},
		{ alpha=0						},
		{ delay=100						},
		{ alpha=1,		duration=300 	}
	)

	local audioDelay = delay-1000
	if (audioDelay < 0) then
		audioDelay = 0
	end
    self:animate(
    	{ delay=audioDelay						},
    	{ command="playTrack track=UNLOCKED", target=self },
    	{ delay=1000							},
    	{ call=layoutBoathouseLock.hide 		},
    	{ delay=400								},
        { call=layoutBoathouseLock.solveFinish 	}
    )   
end

function layoutBoathouseLock:playPinSound(pin, trackName)
	local pinIndex = -1
	local nrPins = table.getn(self.mLockPins)
	for i = 1, nrPins do
		if (pin == self.mLockPins[i]) then
			pinIndex = i
			break
		end
	end
	
	if (pinIndex > 0) then
		local trackTag = "BOATHOUSE_PIN_" .. pinIndex
		getAudioManager():playTrack({tag=trackTag, definition=trackName})
	end
end