cost_only = false
blocks = 0
fuel = 0

positionx = 0
positiony = 0
facing = 0

function writeOut(message)
	print(message)
end
function checkResources()
	while turtle.getItemCount(activeslot) <= 0 do
		if activeslot == 16 then
			writeOut("Turtle is empty, please put building block in slots and press enter to continue")
			io.read()
			activeslot = 1
			turtle.select(activeslot)
		else
			activeslot = activeslot+1
			writeOut("Turtle slot empty, trying slot "..activeslot)
			turtle.select(activeslot)
		end
		os.sleep(0.2)
	end
end
function checkFuel()
	while turtle.getFuelLevel() < 50 do
		writeOut("Turtle almost out of fuel, pausing. Please drop fuel in inventory. And press enter.")
		io.read()
		turtle.refuel()
	end
end
function placeBlock()
	-- Cost calculation mode - don't move
	blocks = blocks + 1
	if cost_only then
		return
	end

	if turtle.detectDown() and not turtle.compareDown() then
		turtle.digDown()
	end

	checkResources()

	turtle.placeDown()
end
-- Navigation features
-- allow the turtle to move while tracking its position
-- this allows us to just give a destination point and have it go there

function turnRightTrack()
	if cost_only then
		return
	end

	turtle.turnRight()
	facing = facing + 1
	if facing >= 4 then
		facing = 0
	end
end

function turnLeftTrack()
	if cost_only then
		return
	end

	turtle.turnLeft()
	facing = facing - 1
	if facing < 0 then
		facing = 3
	end
end
function turnAroundTrack()
	turnLeftTrack()
	turnLeftTrack()
end
function safeForward()
	fuel = fuel + 1
	if cost_only then
		return
	end

	checkFuel()
	success = false
	while not success do
		success = turtle.forward()
		if not success then
			while turtle.detect() do
				if not turtle.dig() then
					print("Blocked attempting to move forward.")
					print("Please clear and press enter to continue.")
					io.read()
				end
			end
		end
	end
end

function safeBack()
	fuel = fuel + 1
	if cost_only then
		return
	end

	checkFuel()
	success = false
	while not success do
		success = turtle.back()
		if not success then
			turnAroundTrack();
			while turtle.detect() do
				if not turtle.dig() then
					break;
				end
			end
			turnAroundTrack()
			success = turtle.back()
			if not success then
				print("Blocked attempting to move back.")
				print("Please clear and press enter to continue.")
				io.read()
			end
		end
	end
end

function safeUp()
	fuel = fuel + 1
	if cost_only then
		return
	end

	checkFuel()
	success = false
	while not success do
		success = turtle.up()
		if not success then
			while turtle.detectUp() do
				if not turtle.digUp() then
					print("Blocked attempting to move up.")
					print("Please clear and press enter to continue.")
					io.read()
				end
			end
		end
	end
end

function safeDown()
	fuel = fuel + 1
	if cost_only then
		return
	end

	checkFuel()
	success = false
	while not success do
		success = turtle.down()
		if not success then
			while turtle.detectDown() do
				if not turtle.digDown() then
					print("Blocked attempting to move down.")
					print("Please clear and press enter to continue.")
					io.read()
				end
			end
		end
	end
end

function moveY(targety)
	if targety == positiony then
		return
	end

	if (facing ~= 0 and facing ~= 2) then -- check axis
		turnRightTrack()
	end

	while targety > positiony do
		if facing == 0 then
			safeForward()
		else
			safeBack()
		end
		positiony = positiony + 1
	end

	while targety < positiony do
		if facing == 2 then
			safeForward()
		else
			safeBack()
		end
		positiony = positiony - 1
	end
end

function moveX(targetx)
	if targetx == positionx then
		return
	end

	if (facing ~= 1 and facing ~= 3) then -- check axis
		turnRightTrack()
	end

	while targetx > positionx do
		if facing == 1 then
			safeForward()
		else
			safeBack()
		end
		positionx = positionx + 1
	end

	while targetx < positionx do
		if facing == 3 then
			safeForward()
		else
			safeBack()
		end
		positionx = positionx - 1
	end
end

function navigateTo(targetx, targety)
	if facing == 0 or facing == 2 then -- Y axis
		moveY(targety)
		moveX(targetx)
	else
		moveX(targetx)
		moveY(targety)
	end
end
function line(length)
	if length <= 0 then
		error("Error, length can not be 0")
	end
	local i
	for i=1, length do
		placeBlock()
		if i ~= length then
			safeForward()
		end
	end
end
function rectangle(depth, width)
	if depth <= 0 then
		error("Error, depth can not be 0")
	end
	if width <= 0 then
		error("Error, width can not be 0")
	end
	local lengths = {depth, width, depth, width }
	local j
	for j=1,4 do
		line(lengths[j])
		turnRightTrack()
	end
end
function square(width)
	rectangle(width, width)
end
function wall(length, height)
	turnRightTrack()
	local i
	local j
	for i = 1, length do
		for j = 1, height do
			placeBlock()
			if j < height then
				safeUp()
			end
		end
		safeForward()
		for j = 1, height-1 do
			safeDown()
		end
	end
	turnLeftTrack()
end
function platform(x, y)
	local forward = true
	for cy = 0, y-1 do
		for cx = 0, x-1 do
			if forward then
				navigateTo(cx, cy)
			else
				navigateTo(x - cx - 1, cy)
			end
			placeBlock()
		end
		if forward then
			forward = false
		else
			forward = true
		end
	end
end
function stair(width, height)
	turnRightTrack()
	local cx=1
	local cy=0
	local goforward=0
	while cy < height do
		while cx < width do
			placeBlock()
			safeForward()
			cx = cx + 1
		end
		placeBlock()
		cx = 1
		cy = cy + 1
		if cy < height then
			if goforward == 1 then
				turnRightTrack()
				safeUp()
				safeForward()
				turnRightTrack()
				goforward = 0
			else
				turnLeftTrack()
				safeUp()
				safeForward()
				turnLeftTrack()
				goforward = 1
			end
		end
	end
end
function circle(radius)
	radius = tonumber(radius)

	-- Main dome and sphere building routine

	width = radius * 2 + 1
	sqrt3 = 3 ^ 0.5
	boundary_radius = radius + 1.0
	boundary2 = boundary_radius ^ 2

	zstart = radius

	-- This loop is for each vertical layer through the sphere or dome.
	for z = zstart,zstart do
		--writeOut("Layer " .. z)
		cz2 = (radius - z) ^ 2

		limit_offset_y = (boundary2 - cz2) ^ 0.5
		max_offset_y = math.ceil(limit_offset_y)

		-- We do first the +x side, then the -x side to make movement efficient
		for side = 0,1 do
			-- On the right we go from small y to large y, on the left reversed
			-- This makes us travel clockwise around each layer
			if (side == 0) then
				ystart = radius - max_offset_y
				yend = radius + max_offset_y
				ystep = 1
			else
				ystart = radius + max_offset_y
				yend = radius - max_offset_y
				ystep = -1
			end

			for y = ystart,yend,ystep do
				cy2 = (radius - y) ^ 2

				remainder2 = (boundary2 - cz2 - cy2)


				if remainder2 >= 0 then
					-- This is the maximum difference in x from the centre we can be without definitely being outside the radius
					max_offset_x = math.ceil((boundary2 - cz2 - cy2) ^ 0.5)

					-- Only do either the +x or -x side
					if (side == 0) then
						-- +x side
						xstart = radius
						xend = radius + max_offset_x
					else
						-- -x side
						xstart = radius - max_offset_x
						xend = radius - 1
					end

					-- Reverse direction we traverse xs when in -y side
					if y > radius then
						temp = xstart
						xstart = xend
						xend = temp
						xstep = -1
					else
						xstep = 1
					end

					for x = xstart,xend,xstep do
						cx2 = (radius - x) ^ 2
						distance_to_centre = (cx2 + cy2 + cz2) ^ 0.5
						-- Only blocks within the radius but still within 1 3d-diagonal block of the edge are eligible
						if distance_to_centre < boundary_radius and distance_to_centre + sqrt3 >= boundary_radius then
							offsets = {{0, 1, 0}, {0, -1, 0}, {1, 0, 0}, {-1, 0, 0}, {0, 0, 1}, {0, 0, -1}}
							for i=1,6 do
								offset = offsets[i]
								dx = offset[1]
								dy = offset[2]
								dz = offset[3]
								if ((radius - (x + dx)) ^ 2 + (radius - (y + dy)) ^ 2 + (radius - (z + dz)) ^ 2) ^ 0.5 >= boundary_radius then
									-- This is a point to use
									navigateTo(x, y)
									placeBlock()
									break
								end
							end
						end
					end
				end
			end
		end
	end

	-- Return to where we started in x,y place and turn to face original direction
	-- Don't change vertical place though - should be solid under us!
	navigateTo(0, 0)
	while (facing > 0) do
		turnLeftTrack()
	end
end
function dome(type, radius)
	type = type
	radius = tonumber(radius)

	-- Main dome and sphere building routine

	width = radius * 2 + 1
	sqrt3 = 3 ^ 0.5
	boundary_radius = radius + 1.0
	boundary2 = boundary_radius ^ 2

	if type == "dome" then
		zstart = radius
	elseif type == "sphere" then
		zstart = 0
	else
		print("Usage: sdbuild <shape> <radius> [-c]")
		os.exit(1)
	end
	zend = width - 1

	-- This loop is for each vertical layer through the sphere or dome.
	for z = zstart,zend do
		if not cost_only and z ~= zstart then
			safeUp()
		end
		--writeOut("Layer " .. z)
		cz2 = (radius - z) ^ 2

		limit_offset_y = (boundary2 - cz2) ^ 0.5
		max_offset_y = math.ceil(limit_offset_y)

		-- We do first the +x side, then the -x side to make movement efficient
		for side = 0,1 do
			-- On the right we go from small y to large y, on the left reversed
			-- This makes us travel clockwise around each layer
			if (side == 0) then
				ystart = radius - max_offset_y
				yend = radius + max_offset_y
				ystep = 1
			else
				ystart = radius + max_offset_y
				yend = radius - max_offset_y
				ystep = -1
			end

			for y = ystart,yend,ystep do
				cy2 = (radius - y) ^ 2

				remainder2 = (boundary2 - cz2 - cy2)


				if remainder2 >= 0 then
					-- This is the maximum difference in x from the centre we can be without definitely being outside the radius
					max_offset_x = math.ceil((boundary2 - cz2 - cy2) ^ 0.5)

					-- Only do either the +x or -x side
					if (side == 0) then
						-- +x side
						xstart = radius
						xend = radius + max_offset_x
					else
						-- -x side
						xstart = radius - max_offset_x
						xend = radius - 1
					end

					-- Reverse direction we traverse xs when in -y side
					if y > radius then
						temp = xstart
						xstart = xend
						xend = temp
						xstep = -1
					else
						xstep = 1
					end

					for x = xstart,xend,xstep do
						cx2 = (radius - x) ^ 2
						distance_to_centre = (cx2 + cy2 + cz2) ^ 0.5
						-- Only blocks within the radius but still within 1 3d-diagonal block of the edge are eligible
						if distance_to_centre < boundary_radius and distance_to_centre + sqrt3 >= boundary_radius then
							offsets = {{0, 1, 0}, {0, -1, 0}, {1, 0, 0}, {-1, 0, 0}, {0, 0, 1}, {0, 0, -1}}
							for i=1,6 do
								offset = offsets[i]
								dx = offset[1]
								dy = offset[2]
								dz = offset[3]
								if ((radius - (x + dx)) ^ 2 + (radius - (y + dy)) ^ 2 + (radius - (z + dz)) ^ 2) ^ 0.5 >= boundary_radius then
									-- This is a point to use
									navigateTo(x, y)
									placeBlock()
									break
								end
							end
						end
					end
				end
			end
		end
	end

	-- Return to where we started in x,y place and turn to face original direction
	-- Don't change vertical place though - should be solid under us!
	navigateTo(0, 0)
	while (facing > 0) do
		turnLeftTrack()
	end

end
writeOut("Shape Maker 1.1. Created by Michiel using a bit of Vliekkie's code")
writeOut("Fixed and made readable by Aeolun ;)")
writeOut("");
writeOut("What should be built?")
writeOut("+---------+-----------+-------+-------+")
writeOut("| line    | rectangle | wall  | room  |")
writeOut("| square  | platform  | stair | dome  |")
writeOut("| pyramid | cylinder  | circle|       |")
writeOut("+---------+-----------+-------+-------+")
writeOut("")

local choice = io.read()
writeOut("Building a "..choice)
writeOut("Want to just calculate the cost? [y/n]")
local yes = io.read()
if yes == 'y' then
	cost_only = true
end

if not cost_only then
	turtle.select(1)
	activeslot = 1
	if turtle.getItemCount(activeslot) == 0 then
		writeOut("Please put building blocks in the first slot (and more if you need them)")
		while turtle.getItemCount(activeslot) == 0 do
			os.sleep(2)
		end
	end
end
if choice == "rectangle" then -- fixed
	writeOut("How deep do you want it to be?")
	h = io.read()
	h = tonumber(h)
	writeOut("How wide do you want it to be?")
	v = io.read()
	v = tonumber(v)
	rectangle(h, v)
end
if choice == "square" then --fixed
	writeOut("How long does it need to be?")
	local s = io.read()
	s = tonumber(s)
	square(s)
end
if choice == "line" then --fixed
	writeOut("How long does the line need to be?")
	local ll = io.read()
	ll = tonumber(ll)
	line(ll)
end
if choice == "wall" then --fixed
	writeOut("How long does it need to be?")
	local wl = io.read()
	wl = tonumber(wl)
	writeOut("How high does it need to be?")
	local wh = io.read()
	wh = tonumber(wh)
	if  wh <= 0 then
		error("Error, the height can not be zero")
	end
	if wl <= 0 then
		error("Error, the length can not be 0")
	end
	wall(wl, wh)
end
if choice == "platform" then
	writeOut("How long do you want it to be?")
	x = io.read()
	x = tonumber(x)
	writeOut("How wide do you want it to be?")
	y = io.read()
	y = tonumber(y)
	platform(x, y)
	writeOut("Done")
end
if choice == "stair" then --fixed
	writeOut("How wide do you want it to be?")
	x = io.read()
	x = tonumber(x)
	writeOut("How high do you want it to be?")
	y = io.read()
	y = tonumber(y)
	stair(x, y)
	writeOut("Done")
end
if choice == "room" then
	writeOut("How deep does it need to be?")
	local cl = io.read()
	cl = tonumber(cl)
	writeOut("How wide does it need to be?")
	local ch = io.read()
	ch = tonumber(ch)
	writeOut("How high does it need to be?")
	local hi = io.read()
	hi = tonumber(hi)
	if hi < 3 then
		hi = 3
	end
	if cl < 3 then
		cl = 3
	end
	if ch < 3 then
		ch = 3
	end

	platform(cl, ch)
	while (facing > 0) do
		turnLeftTrack()
	end
	turnAroundTrack()
	for i = 1, hi-2 do
		safeUp()
		rectangle(cl, ch)
	end
	safeUp()
	platform(cl, ch)
end
if choice == "dome" then
	writeOut("What radius do you need it to be?")
	local rad = io.read()
	rad = tonumber(rad)
	dome("dome", rad)
end
if choice == "sphere" then
	writeOut("What radius do you need it to be?")
	local rad = io.read()
	rad = tonumber(rad)
	dome("sphere", rad)
end
if choice == "circle" then
	writeOut("What radius do you need it to be?")
	local rad = io.read()
	rad = tonumber(rad)
	circle(rad)
end
if choice == "cylinder" then
	writeOut("What radius do you need it to be?")
	local rad = io.read()
	rad = tonumber(rad)
	writeOut("What height do you need it to be?")
	local height = io.read()
	height = tonumber(height)

	for i = 1, height do
		circle(rad)
		safeUp()
	end
	for i = 1, height do
		safeDown()
	end
end
if choice == "pyramid" then
	writeOut("What width/depth do you need it to be?")
	local width = io.read()
	width = tonumber(width)
	writeOut("Do you want it to be hollow [y/n]?")
	local hollow = io.read()
	if hollow == 'y' then
		hollow = true
	else
		hollow = false
	end


	height = math.ceil(width / 2)
	for i = 1, height do
		if hollow then
			rectangle(width, width)
		else
			platform(width, width)
			navigateTo(0,0)
			while facing ~= 0 do
				turnRightTrack()
			end
		end
		if i ~= height then
			safeUp()
			safeForward()
			turnRightTrack()
			safeForward()
			turnLeftTrack()
			width = width - 2
		end
	end
end

print("Blocks used: " .. blocks)
print("Fuel used: " .. fuel)