LayoutConnector = {}
setmetatable(LayoutConnector, Layout)
-- ----------------------------------------------------------------------------
LayoutConnector.__index = function(t, k)

	if rawget(t, k) ~= nil then
		return rawget(t, k)
	elseif LayoutConnector[k] ~= nil then
		return LayoutConnector[k]
	end
	
end
-- ----------------------------------------------------------------------------
function LayoutConnector:setup(connections)

	assert(connections)
	self.mConnections = connections
	self.mSources = {}
	self.mDestinations = {}
	self.mCurrentSource = nil
	self.mCurrentConnection = nil
	self.mPuzzle = true

end
-- ----------------------------------------------------------------------------
function LayoutConnector:onInitialize()

	for connection, pair in pairs(self.mConnections) do
		assert(pair[1])
		assert(pair[2])
		-- register all sources and destinations as IOs		
		self.mIOs[pair[1]] = { cursor = kCursorSelect }
		self.mIOs[pair[2]] = {}
		
		-- store them in lookup tables
		table.insert(self.mSources, pair[1])
		table.insert(self.mDestinations, pair[2])
		
		-- set them to 'not connected'
		pair[1].connected = false
		pair[1].connection = nil
		pair[2].connected = false
		pair[2].connection = nil
		
		-- relocate their anchors
		-- for distance calculation
		-- this assumes the destination is graphically above the source.
		pair[1]:relocateAnchorPoint("top")
		pair[2]:relocateAnchorPoint("bottom")
	end	
	
	-- now call the base onInitialize,
	-- so our newly created IOs will still be handled
	Layout.onInitialize(self)	

end
-- ----------------------------------------------------------------------------
function LayoutConnector:onTick(time)

	Layout.onTick(self, time)
	
	if (self.mCurrentSource) then
		-- hide current connection
		if (self.mCurrentConnection) then
			self.mCurrentConnection:ss(kInactive)
			self.mCurrentConnection:setAlpha(1)
		end
	
		-- find the closest destination
		-- that's not connected!
		local closestDestination = self:calcClosestUnconnectedDestination(cursor:getX(), cursor:getY())
		
		-- find the connection to show
		self.mCurrentConnection = self:calcConnection(self.mCurrentSource, closestDestination)
		
		-- show current connection
		if (self.mCurrentConnection) then
			self.mCurrentConnection:ss(kIdle)
			self.mCurrentConnection:setAlpha(0.5)			
		end
	end

end
-- ----------------------------------------------------------------------------
function LayoutConnector:onClickIO(object, x, y)

	if (self.mCurrentSource) then
		-- check if we clicked a destination
		-- if so, connect it to the source
		for i, destination in ipairs(self.mDestinations) do
			if (destination == object) then
				self:connect(self.mCurrentConnection)
				self:ssSources(kActive, false, true)
				self:ssDestinations(kIdle, false, true)
				self:ssDestinations(kActive, true, false)
				
				self.mCurrentSource = nil
				self.mCurrentConnection = nil
				
				if (self:calcSolved()) then
					self:solve()				
				end
				
				return true			
			end		
		end	
	else
		-- check if we clicked a source
		for i, source in ipairs(self.mSources) do
			if (source == object) then
				self:ssSources(kIdle, false, true)
				self:ssDestinations(kActive, false, true)
				self:ssDestinations(kIdle, true, false)
				
				self.mCurrentSource = object
				self.mCurrentSource:ss(kInactive)
				self.mCurrentConnection = nil				
				return true
			end
		end	
		
		-- check if we clicked a destination
		-- if so, unconnect it, if it's connected
		for i, destination in ipairs(self.mDestinations) do
			if (destination == object) then
				if (destination.connected) then
					self:unconnect(destination.connection)			
				end
			end
		end			
	end	
	
	return Layout.onClickIO(self, object, x, y)

end
-- ----------------------------------------------------------------------------
function LayoutConnector:onSolve()

	Layout.onSolve(self)
	
	-- unconnect all 
	for connection, pair in pairs(self.mConnections) do
		self:unconnect(connection)
	end	
	
	-- connect all good ones
	for connection, pair in pairs(self.mConnections) do
		if (pair[3]) then
			self:connect(connection)	
		end
	end
	
	self:ssSources(kInactive, true, true)
	self:ssDestinations(kIdle, true, true)

end
-- ----------------------------------------------------------------------------
function LayoutConnector:ssSources(state, doConnected, doUnconnected)

	if (doConnected == nil) then
		doConnected = true
	end
	
	if (doUnconnected == nil) then
		doUnconnected = true
	end	

	for i, source in ipairs(self.mSources) do
		if (doConnected and source.connected) then
			source:ss(state)
		elseif (doUnconnected and not source.connected) then
			source:ss(state)
		end
	end

end
-- ----------------------------------------------------------------------------
function LayoutConnector:ssDestinations(state, doConnected, doUnconnected)

	if (doConnected == nil) then
		doConnected = true
	end
	
	if (doUnconnected == nil) then
		doUnconnected = true
	end	

	for i, destination in ipairs(self.mDestinations) do
		if (doConnected and destination.connected) then
			destination:ss(state)
		elseif (doUnconnected and not destination.connected) then
			destination:ss(state)
		end
	end

end
-- ----------------------------------------------------------------------------
function LayoutConnector:connect(connection)

	assert(connection)
	assert(self.mConnections[connection])
	
	connection:ss(kIdle)
	connection:setAlpha(1)
	
	local pair = self.mConnections[connection]
	assert(not pair[1].connected)
	assert(not pair[1].connection)
	pair[1].connected = true
	pair[1].connection = connection
	pair[1]:ss(kInactive)
	assert(not pair[2].connected)
	assert(not pair[2].connection)	
	pair[2].connected = true
	pair[2].connection = connection
	pair[2]:ss(kActive)

	self:playConnectSound(connection)
end
-- ----------------------------------------------------------------------------
function LayoutConnector:unconnect(connection)

	assert(connection)
	assert(self.mConnections[connection])
	
	connection:ss(kInactive)	
	connection:setAlpha(1)
	
	local pair = self.mConnections[connection]
	if (pair[1].connection == connection) then
		pair[1].connected = false
		pair[1].connection = nil
		pair[1]:ss(kActive)
	end
	if (pair[2].connection == connection) then
		pair[2].connected = false
		pair[2].connection = nil
		pair[2]:ss(kIdle)
	end
	
	self:playDisconnectSound(connection)
		
end
-- ----------------------------------------------------------------------------
function LayoutConnector:calcClosestUnconnectedDestination(x, y)

	local closestDestination = nil
	local closestDistance = math.huge
	for i, destination in ipairs(self.mDestinations) do
		if (not destination.connected) then
			local dx = destination:screenX()
			local dy = destination:screenY()
			local distance = math.distance(x, y, dx, dy)
			
			if (distance < closestDistance) then
				closestDestination = destination
				closestDistance = distance
			end
		end
	end	
	return closestDestination

end
-- ----------------------------------------------------------------------------
-- given a source and a destination, returns a possible connection between
-- the two. or nil.
function LayoutConnector:calcConnection(source, destination)

	if (source and destination) then
		for connection, pair in pairs(self.mConnections) do
			if (pair[1] == source) then
				if (pair[2] == destination) then
					return connection
				end
			end
		end	
	end
	return nil

end
-- ----------------------------------------------------------------------------
-- checks if all connections that should be idle are in fact idle.
function LayoutConnector:calcSolved()

	for connection, pair in pairs(self.mConnections) do
		if (pair[3]) then
			if (not connection:isIdle()) then
				return false
			end
		end	
	end
	
	return true

end
-- ----------------------------------------------------------------------------
function LayoutConnector:calcCursorProperties()

	Layout.calcCursorProperties(self)
	
    local cursorX = cursor:getX()
    local cursorY = cursor:getY()
    if (self:isInside(cursorX, cursorY)) then
		if (cursor:getCursor() == kCursorDefault) then
			if (self.mCurrentSource) then
				cursor:setCursor(kCursorSelected)
			end	
		end
	end
	
end

-- ----------------------------------------------------------------------------
function LayoutConnector:playConnectSound(connection)
end

-- ----------------------------------------------------------------------------
function LayoutConnector:playDisconnectSound(connection)
end