--[[
Fresco v 1.1.0
Matti Vapa, 2013
https://github.com/mattijv/fresco
http://pastebin.com/KU8RWSQd

This program will take as an argument a URL to an image file and request a copy of
it via a server that converts it to a proper format. It then reproduces the image
using Glowstone Illuminators from Thermal Expansion. Required mods are

	* ComputerCraft (duh),
	* Thermal Expansion,
	* OpenPeripheral.

Instead of a direct URL you can also input a Minecraft username with the -u handle.
The program will download the skin corresponding to the username and build the face
part of the skin.

Possible future features:
	
	* Ability to specify a part of the image to be build, so multiple turtles can
	  be used for the same picture. This will probably be implemented.
	* Add more comments so people can easily modify this code.

Changelog:

	0.1.0 - 0.8.0:
				* Added most features and tested until 6 AM.
	
	0.9.0:
				* Released for public testing.
	1.0.0:
				* Added option to build the image horizontally.
				* Cleaning up the code.
	1.1.0:
				* Updated to match the OpenPeripheral API changes.
				  For older modpacks, use the previous version:
				  http://pastebin.com/42KWALhR
				* Added the option to restart an uncomplete build.
				* Removed MiscPeripherals support as the mod is no
				  longer updated. :/
				* Removed cosmetic calls of sleep() in the startup.
				* Fixed a crash caused by someone standing in front
				  of the turtle while it was placing Illuminators.
				* Fixed punctuation in the change log.

Thanks to VerTiGo_Etrex and Mikrysoft for pointing out the changes in the
OpenPeripheral API.

]]--

if not http then
	print("HTTP must be enabled.")
	return
end


VERSION = "1.1.0"

print("Fresco v "..VERSION)

apiURL = "http://lakka.kapsi.fi:62096"
playerURL = "http://s3.amazonaws.com/MinecraftSkins/"

local chest = nil
local url
local restart
local skip
local maxsize
local height,width
local row,column
-- for finding the face in the skin file
local offsetY,offsetX = 0,0
local ascend = turtle.up
local descend = turtle.down

local usage = function()
	print("Usage:")
	print("fresco [-h] [-u playername] [url] [size] [-r]")
	print("Either a playername with -u option or url to image must be supplied.")
	print("With -u option the size parameter is the dimensions of the face (default 8 pixels).")
	print("The size option is the length of the longest size in the finished build.")
	print("Use -r to restart a failed build. You will be prompted for a number of columns to skip.")
	print("The number of columns should be equal to the number of completed columns.")

	--[[
	print("Options:")
	print("-u    Uses the face from the skin of the player with the name 'playername' as the image.")
	print("      If you use -u you don't need to provide the url as an argument.")
	print("url   The url to the desired picture in the form: http://www.images.com/coolpic.png.")
	print("      Not needed if you use -u.")
	print("size  The maximum lenght of the longer side. The image will be (down/up)scaled to fit this size.")
	print("      With -u the size is the size of the players face, default 8 pixels.")
	]]--
end

local move = function(f)
	while turtle.getFuelLevel() < 1 do
		print("Low on fuel. Please add fuel to my inventory and press return.")
		local e,c = os.pullEvent("key")
		while c ~= 28 do
			e,c = os.pullEvent("key")
		end
		print("Checking for fuel...")
		shell.run("refuel all")
	end
	while not f() do
		sleep(0.3)
	end
	sleep(0.1)
end


local resupply = function()
	local r = row
	local c = column
	while r < height do
		move(descend)
		r = r + 1
	end
	turtle.turnLeft()
	while c > 1 do
		move(turtle.forward)
		c = c - 1
	end
	turtle.turnRight()
	move(turtle.back)
	local slot = 1
	chest.condenseItems()
	while not chest.getStackInSlot(1) do
			print("Please add more Illuminators to the chest.")
			sleep(5)
			chest.condenseItems()
	end
	chest.pushItemIntoSlot("down",1,64,slot)
	slot = 2
	while slot < 16 do
		chest.condenseItems()
		if not chest.getStackInSlot(1) then
			break
		end
		chest.pushItemIntoSlot("down",1,64,slot)
		slot = slot + 1
	end
	move(turtle.forward)
	turtle.turnRight()
	while c < column do
		move(turtle.forward)
		c = c + 1
	end
	turtle.turnLeft()
	while r > row do
		move(ascend)
		r = r - 1
	end
end

local placeFront = function(color)
	if turtle.detect() then return end
	if turtle.getItemCount(1) < 2 then
		if turtle.getItemCount(1) == 0 then
			local slot = 2
			while slot <= 16 do
				turtle.select(slot)
				if turtle.transferTo(1) then
					break
				end
				slot = slot + 1
			end
			if slot > 16 then
				resupply()
			end
		end
	end
	turtle.select(1)
	while not turtle.place() do
		print("Outta my way!")
		sleep(1)
	end
	local lamp = peripheral.wrap("front")
	lamp.setColor(tonumber(color,16))
	turtle.attack()
end

local placeDown = function(color)
	if turtle.detectDown() then return end
	if turtle.getItemCount(1) < 2 then
		if turtle.getItemCount(1) == 0 then
			local slot = 2
			while slot <= 16 do
				turtle.select(slot)
				if turtle.transferTo(1) then
					break
				end
				slot = slot + 1
			end
			if slot > 16 then
				resupply()
			end
		end
	end
	turtle.select(1)
	while not turtle.placeDown() do
		print("Outta my way!")
		sleep(1)
	end
	local lamp = peripheral.wrap("bottom")
	lamp.setColor(tonumber(color,16))
	turtle.attackDown()
end

local calcFuelNeed = function (h,w)
	local fuel = (h-1)*w+w+1
	if w%2 == 1 then
		fuel = fuel + (h-1)
	end
	if chest then
		local fuelruns = math.floor((w*h-1)/1024)
		for i = 1,fuelruns do
			local columns, frac = math.modf(i*1024/h)
			-- horizontal movement
			fuel = fuel + 2*(columns+1)+2
			-- vertical movement
			if columns % 2 == 1 then
				fuel = fuel + 2*(h-math.floor(frac*h+0.5)-1)
			else
				fuel = fuel + 2*(math.floor(frac*h+0.5)-1)
			end
		end
	end
	return fuel
end


_args = {...}
args = {}


local place = placeFront

local i = 1
while i <= #_args do
	if _args[i] == "-h" then
		place = placeDown
		ascend = turtle.forward
		descend = turtle.back
	elseif _args[i] == "-r" then
		restart = true
	elseif _args[i]:sub(1,1) == "-" then
		args[_args[i]] = _args[i+1]
		i = i + 1
	elseif tonumber(_args[i]) ~= nil then
		args["maxsize"] = _args[i]
	else
		args["url"] = _args[i]
	end
	i = i + 1
end


if not args["url"] and not args["-u"] then
	usage()
	return
else
	if args["-u"] ~= nil then
		url = playerURL..args["-u"]..".png"
		if args["maxsize"] then
				maxsize = tostring(math.floor(tonumber(args["maxsize"])*(64/8)+0.5))
		end
	else
		url = args["url"]
		maxsize = args["maxsize"]
	end
	
end

if not url then
	usage()
	return
end

local vars = "url="..url
if maxsize then
	vars = vars.."&maxsize="..maxsize
end
print("Requesting image from")
print(url)
f = http.post(apiURL,vars)
if not f then
	print("No response from image server.")
	return
elseif f.getResponseCode() ~= 200 then
	print("Error while connecting to server!")
	print("Server response")
	print("Code: "..f.getResponseCode())
	print("Message:")
	print(f.readAll())
	return
end

local oldSize = f.readLine()
local newSize = f.readLine()
if oldSize ~= newSize then
	print("Original image size was (w,h): "..oldSize)
	print("New size is (w,h): "..newSize)
else
	print("Image size is (w,h): "..newSize)
end

local img = {}

line = f.readLine()
while line ~= nil do
	table.insert(img,line)
	line = f.readLine()
end
f.close()


-- X:8, Y:8 to X: 15, Y:15 (top left, bottom right of face)
maxsize = tonumber(maxsize)
if args["-u"] then
	if maxsize then
		-- multiply by 8/64 to get the face size and hack a rounding function
		local size = math.floor(maxsize*0.125+0.5)
		height,width = size,size
		offsetY,offsetX = size,size
	else
		height,width = 8,8
		offsetY,offsetX = 8,8
	end
else
	height,width = #img, #img[1]/8
	row,column = height,1
end

row,column = height,1
print("Press return to continue.")
local e,c = os.pullEvent("key")
while c ~= 28 do
	e,c = os.pullEvent("key")
end


if peripheral.getType("top") == nil then
	print("No valid inventory found on top.")
	return
end
chest = peripheral.wrap("top")
if chest.pushItemIntoSlot == nil then
	print("Inventory does not support required operation: pushItemIntoSlot.")
	return
end

print("Resupply inventory located.")
print("Calculating needed fuel...")
local fuelNeed = calcFuelNeed(height,width)
print("Fuel consumption will be (approximate): "..tostring(fuelNeed))
print("Current fuel level is: "..tostring(turtle.getFuelLevel()))
if turtle.getFuelLevel() < fuelNeed then
	while turtle.getFuelLevel() < fuelNeed do
		print(string.format("Need %d fuel units more.",fuelNeed-turtle.getFuelLevel()))
		print("Please put more fuel into my inventory and press return.")
		local e,c = os.pullEvent("key")
		while c ~= 28 do
			e,c = os.pullEvent("key")
		end
		print("Checking for fuel...")
		shell.run("refuel all")
	end
end
print("Fuel requirements met. Looking for building materials...")
while turtle.getItemCount(1) < 1 do
	print("Please put Glowstone Illuminators into the first slot of my inventory.")
	print("Press return to continue.")
	local e,c = os.pullEvent("key")
	while c ~= 28 do
		e,c = os.pullEvent("key")
	end
end

print("Good to go!")
print(string.format("The process will take around %f minutes to finish.",(1.2*fuelNeed)/60))
print("Press return to begin.")
e,c = os.pullEvent("key")
while c ~= 28 do
	e,c = os.pullEvent("key")
end

if restart then
	print("You wanted to restart.")
	print("Please input how many columns to skip (or press return to cancel).")
	while true do
		term.write("Columns: ")
		skip = io.read()
		if columns == "" then return end
		if not tonumber(skip) or tonumber(skip) < 0 or tonumber(skip) >= width then
			print("Bad input!")
		else
			print("Skipping "..skip.." columns.")
			skip = tonumber(skip)
			break
		end
	end
end

print("Starting printing...")

move(turtle.forward)
turtle.select(1)

if restart then
	turtle.turnRight()
	column = skip + 1
	local c = 1
	while c < column do
		move(turtle.forward)
		c = c + 1
	end
	turtle.turnLeft()
end

while true do
	if column > width then break end
	place(img[row+offsetY]:sub(1+8*(column-1+offsetX),1+8*(column-1+offsetX)+7))
	while row > 1 do
		move(ascend)
		row = row - 1
		place(img[row+offsetY]:sub(1+8*(column-1+offsetX),1+8*(column-1+offsetX)+7))
	end
	turtle.turnRight()
	move(turtle.forward)
	turtle.turnLeft()
	column = column + 1
	if column > width then break end
	place(img[row+offsetY]:sub(1+8*(column-1+offsetX),1+8*(column-1+offsetX)+7))
	while row < height do
		move(descend)
		row = row + 1
		place(img[row+offsetY]:sub(1+8*(column-1+offsetX),1+8*(column-1+offsetX)+7))
	end
	turtle.turnRight()
	move(turtle.forward)
	turtle.turnLeft()
	column = column + 1
end

if width % 2 == 1 then
	for i = 1, height-1 do
		move(descend)
	end
end