masterId = nil
homeZPosition = 0

function forwardConflict(n, minY, maxY)
	local i
	for i = 1,n do
		if not aturtle.forward() then
			while true do
				local r = math.random(3)
				if r == 1 and (minY == nil or aturtle.getY() > minY) then
					if not aturtle.down() then os.sleep(1) end
				elseif r == 2 and (maxY == nil or aturtle.getY() < maxY) then
					if not aturtle.up() then os.sleep(1) end
				else
					os.sleep(math.random())
				end
				if aturtle.forward() then break end
			end
		end
	end
end

function homeRowMoveZ(z)
	-- Move to an acceptable Y level
	if aturtle.getY() < 5 then
		aturtle.moveToY(5, "check")
	end
	if aturtle.getZ() > z then
		aturtle.changeDirection(aturtle.DIR_NEG_Z)
		forwardConflict(aturtle.getZ() - z, 5, 255)
	end
	if aturtle.getZ() < z then
		aturtle.changeDirection(aturtle.DIR_POS_Z)
		forwardConflict(z - aturtle.getZ(), 5, 255)
	end
end

function goHomeFromConstruct()
	-- Move to an acceptable Y level
	if aturtle.getY() < 5 then
		aturtle.moveToY(5, "check")
	end
	-- Move to home row
	aturtle.moveToX(0, "check")
	-- Move to the home Z from the home row
	homeRowMoveZ(homeZPosition)
	-- Move to home Y (1)
	aturtle.moveToY(1, "check")
end

function goConstructFromHome(x, y, z)
	-- Move to an acceptable Y level
	if aturtle.getY() < 5 then
		aturtle.moveToY(5, "check")
	end
	-- Move to home row (but should already be there ...)
	aturtle.moveToX(0, "check")
	-- Move to the desired Z position
	homeRowMoveZ(z)
	-- If desired Y is above 5, then move there now
	if y >= 5 then
		aturtle.moveToY(y, "check")
	end
	-- Move to the desired X
	aturtle.moveToX(x, "force")
	-- Move to the desired Y
	aturtle.moveToY(y, "force")
end

function goChestsFromHome()
	aturtle.moveToY(1, "check")
	aturtle.moveToX(1, "check")
end

function goHomeFromChests()
	aturtle.moveToZ(homeZPosition, "check")
	aturtle.moveToX(0, "check")
	aturtle.moveToY(1, "check")
end

function goConstructFromChests(x, y, z)
	goConstructFromHome(x, y, z)
end

function discoverLocation()
	-- Assumes the turtle is currently in "attached" position - 1 block above home.
	-- Place 1 block at the end of the chest access row for the turtle to bump into.
	aturtle.changeDirection(aturtle.DIR_POS_X)
	aturtle.checkForward()
	aturtle.changeDirection(aturtle.DIR_NEG_Z)
	while not turtle.detect() do
		aturtle.checkForward()
	end
	local oldX, oldY, oldZ = aturtle.getPosition()
	-- The turtle's location is now 1, 1, 0
	aturtle.resetPosition(1, 1, 0, DIR_NEG_Z)
	-- Calculate the turtle's home position
	homeZPosition = 0 - oldZ
	print("Home Z position: " .. homeZPosition)
end

function doLocationAndChestDiscover()
	-- After location discovery, will be at 1, 1, 0 (base of chest access row)
	discoverLocation()
	pcons.discoverChestsFromCurrentPos()
	-- Go home
	aturtle.moveToZ(homeZPosition, "check")
	aturtle.moveToX(0, "check")
	aturtle.changeDirection(aturtle.DIR_POS_X)
	pcons.validateNumChests()
end

function requestChestAccess()
	rrp.send(masterId, "pconsp_chestrequest")
	while true do
		sender, message, dist = rrp.receive()
		if sender == masterId and message == "pconsp_chestaccessgranted" then
			return
		end
	end
end

function releaseChestAccess()
	rrp.send(masterId, "pconsp_chestrelease")
end

-- ONLY CALL WHEN ON CHEST ROW
function checkBatchFuel(batch)
	local requireFuel = pcons.getEstBatchFuel(batch)
	if turtle.getFuelLevel() < requireFuel then
		if pcons.getHasFuelChest() then
			pcons.refuelFromChest(requireFuel)
		else
			aturtle.checkFuel(requireFuel)
		end
	end
end

function doBatch(batch)
	if aturtle.getX() ~= 0 or aturtle.getY() ~= 1 then
		print("Not at home position!")
		return
	end
	if #(batch.blockLog) < 1 then return end
	requestChestAccess()
	goChestsFromHome()
	checkBatchFuel(batch)
	pcons.getItemsForBatch(batch)
	goHomeFromChests()
	releaseChestAccess()
	local toX, toY, toZ = batch.blockLog[1].x, batch.blockLog[1].y + 1, batch.blockLog[1].z
	local originX, originY, originZ = pcons.toOriginPosition(toX, toY, toZ)
	goConstructFromHome(originX, originY, originZ)
	pcons.placeBatchFromBlockLog(batch)
	goHomeFromConstruct()
	if pcons.hasBatchExtraItems(batch) then
		requestChestAccess()
		goChestsFromHome()
		pcons.returnBatchExtraItems(batch)
		goHomeFromChests()
		releaseChestAccess()
	end
end

function listenMain()
	while true do
		sender, message, dist = rrp.receive()
		if message == "pconsp_discover" and masterId == nil then
			print("Responding to discover.")
			rednet.send(sender, "pconsp_discover_resp")
		end
		if message == "pconsp_attach" and masterId == nil then
			print("Attached to master " .. sender)
			masterId = sender
			aturtle.resetPosition(0, 0, 0, aturtle.DIR_POS_X)
			aturtle.checkUp()
			rrp.send(sender, "pconsp_attached")
		end
		if message == "pconsp_release" and masterId ~= nil and sender == masterId then
			print("Released from master " .. masterId)
			masterId = nil
			aturtle.checkDown()
			aturtle.changeDirection(aturtle.DIR_POS_X)
		end
		if message == "pconsp_chestdiscover" and masterId ~= nil and sender == masterId then
			print("Discovering location and chests.")
			doLocationAndChestDiscover()
			print("Finished discovering.")
			rrp.send(sender, "pconsp_chestdiscoversuccess")
		end
		local offsetX, offsetY, offsetZ, numMaterials, hasFuelChest = message:match("^pconsp_initproject ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([01])$")
		if offsetX ~= nil and masterId ~= nil and sender == masterId then
			print("Initializing project.")
			pcons.initValues(tonumber(offsetX), tonumber(offsetY), tonumber(offsetZ), tonumber(numMaterials))
			if tonumber(hasFuelChest) == 1 then
				print("Using fuel chest.")
				pcons.setHasFuelChest(true)
			else
				pcons.setHasFuelChest(false)
			end
		end
		local batchSpecStr = message:match("^pconsp_dobatch (.*)$")
		if batchSpecStr ~= nil and masterId ~= nil and sender == masterId then
			print("Doing batch.")
			doBatch(textutils.unserialize(batchSpecStr))
			print("Batch done.")
			rrp.send(sender, "pconsp_batchdone")
		end
	end
end

function start()
	aturtle.checkFuel(100)
	rrp.openall()
	math.randomseed(os.getComputerID())
	listenMain()
end

