
--  
--  Lighshot Screen Recorder
--  Made by GravityScore
--  


--  -------- Variables

--  Lower the loop rate to decrease recording lag but to decrease compression
--  Do not set to below 50 else the file could become really big
local loopRate = 300

-- Version
local version = "1.3"
local args = {...}

-- Terminal
local oldTerm = {}
local newTerm = {}

-- Events
local event_exitRecording = "lightshot_exitRecordingEvent"

-- Locations
local lightshotURL = "https://raw.github.com/GravityScore/Lightshot/master/lightshot.lua"
local lightshotLocation = "/" .. shell.getRunningProgram()

-- Variables
local clock = 0
local nfaRecording = false
local recordData = [[

local function sp(...) return sleep(...) end
local function c(...) return term.write(...) end
local function d(...) return term.setCursorPos(...) end
local function e(...) return term.setBackgroundColor(...) end
local function f(...) return term.setTextColor(...) end
local function g(...) return term.clear(...) end
local function h(...) return term.clearLine() end
local function i(...) return term.setCursorBlink(...) end
local function j(...) return term.scroll(...) end

]]

local recordHeader = [[
--  
--  Recorded by Lightshot
--  


]]


--  -------- Updating

local function download(url, path)
	for i = 1, 3 do
		local response = http.get(url)
		if response then
			local data = response.readAll()
			response.close()
			if path then
				local f = io.open(path, "w")
				f:write(data)
				f:close()
			end
			return true
		end
	end

	return false
end

local function updateClient()
	local updateLocation = "/.lightshot-update"
	fs.delete(updateLocation)

	-- Update
	download(lightshotURL, updateLocation)
	local a = io.open(updateLocation, "r")
	local b = io.open(lightshotLocation, "r")
	local new = a:read("*a")
	local cur = b:read("*a")
	a:close()
	b:close()

	if cur ~= new then
		fs.delete(lightshotLocation)
		fs.move(updateLocation, lightshotLocation)
		return true
	else
		fs.delete(updateLocation)
		return false
	end
end


--  -------- Compression

local sD = {}
sD[1] = {}
local cD = {}

local function proccessFunction(data)
	if data:len() < 8 then
		if data:sub(-1,-1) == "]" then
			return "[[" .. data .. "] .. \"]\""
		else
			return "[[" .. data .. "]]"
		end
	end
	if cD[v] then return cD[v] end
	for k, v in pairs(sD[#sD]) do
		if v == data then
			cD[v] = ("sD[" .. #sD .. "][" .. k .. "]")
			return("sD[" .. #sD .. "][" .. k .. "]")
		end
	end
	table.insert(sD[#sD], data)
	local returnData = ("sD[".. #sD .. "][" .. #sD[#sD] .. "]")
	if #sD[#sD] > loopRate then
		sD[#sD+1] = {}
	end
	return returnData
end


--  -------- Terminal Override

local bg, tc, blnk = -1, -1, nil
for k, v in pairs(term.native) do oldTerm[k] = v end

newTerm.write = function(...)
	local text = ""
	for k, v in pairs({...}) do text = text .. tostring(v) end
	local a = ""
	if os.clock() - clock > 0 then a = "sp(" .. os.clock() - clock .. ") " end

	text = proccessFunction(tostring(text))
	local b = "c(" .. text .. ")\n"
	recordData = recordData .. a .. b
	clock = os.clock()
	if not nfaRecording then
		return oldTerm.write(...)
	end
end

newTerm.setCursorPos = function(x, y)
	local a = ""
	if os.clock() - clock > 0 then
		a = "sp(" .. os.clock() - clock .. ") "
	end

	recordData = recordData .. a .. "d(" .. tostring(x) .. ", " .. tostring(y) .. ")\n"
	clock = os.clock()

	return oldTerm.setCursorPos(x, y)
end

newTerm.getCursorPos = function(...)
	return oldTerm.getCursorPos(...)
end

newTerm.setBackgroundColor = function(col)
	if bg ~= col then
		local a = ""
		if not nfaRecording then
			if os.clock() - clock > 0 then
				a = "sp(" .. os.clock() - clock .. ") "
			end
		end

		recordData = recordData .. a .. "e(" .. tostring(col) .. ")\n"
		clock = os.clock()
		bg = col
	end

	return oldTerm.setBackgroundColor(col)
end

newTerm.setTextColor = function(col)
	if tc ~= col then
		local a = ""
		if not nfaRecording then
			if os.clock() - clock > 0 then
				a = "sp(" .. os.clock() - clock .. ") "
			end
		end

		recordData = recordData .. a .. "f(" .. tostring(col) .. ")\n"
		clock = os.clock()
		tc = col
	end

	return oldTerm.setTextColor(col)
end

newTerm.setBackgroundColour = function(col)
	return term.setBackgroundColor(col)
end

newTerm.setTextColour = function(col)
	return term.setTextColor(col)
end

newTerm.clear = function(...)
	local a = ""
	if os.clock() - clock > 0 then
		a = "sp(" .. os.clock() - clock .. ") "
	end

	recordData = recordData .. a .. "g()\n"
	clock = os.clock()

	return oldTerm.clear(...)
end

newTerm.clearLine = function(...)
	local a = ""
	if os.clock() - clock > 0 then
		a = "sp(" .. os.clock() - clock .. ") "
	end

	recordData = recordData .. a .. "h()\n"
	clock = os.clock()

	return oldTerm.clearLine(...)
end

newTerm.setCursorBlink = function(flag)
	if flag ~= blnk then
		local a = ""
		if os.clock() - clock > 0 then
			a = "sp(" .. os.clock() - clock .. ") "
		end

		recordData = recordData .. a .. "i(" .. tostring(flag) .. ")\n"
		clock = os.clock()
		blnk = flag
	end

	return oldTerm.setCursorBlink(flag)
end

newTerm.getSize = function(...)
	return oldTerm.getSize(...)
end

newTerm.scroll = function(n)
	local a = ""
	if os.clock() - clock > 0 then
		a = "sp(" .. os.clock() - clock .. ") "
	end

	recordData = recordData .. a .. "j(" .. tostring(n) .. ")\n"
	clock = os.clock()

	return oldTerm.scroll(n)
end

newTerm.redirect = function(...)
	return oldTerm.redirect(...)
end

newTerm.restore = function(...)
	return oldTerm.restore(...)
end

newTerm.isColor = function(...)
	return oldTerm.isColor and oldTerm.isColor(...)
end

newTerm.isColour = function(...)
	return term.isColor(...)
end


--  -------- Recording

local function record(location)
	while true do
		local e, key = os.pullEventRaw()
		if (e == "key" and key == 59) or e == event_exitRecording or e == "terminate" then
			local a = ""
			if os.clock() - clock > 0 then
				a = "sp(" .. os.clock() - clock .. ") "
			end
			recordData = recordData .. a

			recordData = recordData .. "term.setCursorBlink(false)\n"
			recordData = recordData .. "if term.isColor() then term.setTextColor(colors.yellow)\n"
			recordData = recordData .. "else term.setTextColor(colors.white) end\n"
			recordData = recordData .. "term.setBackgroundColor(colors.black)\n"
			recordData = recordData .. "term.clear()\n"
			recordData = recordData .. "term.setCursorPos(1, 1)\n"
			recordData = recordData .. "print(\"End of Recording!\")\n"

			recordHeader = recordHeader .. "local sD = " .. textutils.serialize(sD) .. "\n"
			recordData = recordHeader .. recordData

			term.restore()

			term.setCursorBlink(false)
			if term.isColor() then term.setTextColor(colors.yellow)
			else term.setTextColor(colors.white) end
			term.setBackgroundColor(colors.black)
			term.clear()
			term.setCursorPos(1, 1)
			print("Recording Saved!")

			local f = io.open(location, "w")
			f:write(recordData)
			f:close()
			error()
		end
	end
end


--  -------- Movie

local function loadNfa(path)
	local ret = {}
	if fs.exists(path) and not(fs.isDir(path)) then
		local f = io.open(path, "r")
		local l = f:read("*l")
		local curFrame = ""
		while l do
			if l ~= "" and l ~= "~" then
				curFrame = curFrame .. l .. "\n"
			elseif l == "~" then
				table.insert(ret, curFrame)
				curFrame = ""
			end
			l = f:read("*l")
		end
		f:close()
	end

	return ret
end

local function movie(location, duration)
	nfaRecording = true
	local frames = loadNfa(location)
	if frames ~= {} then
		for i, v in ipairs(frames) do
			term.setTextColor(colors.white)
			term.setBackgroundColor(colors.black)
			term.clear()

			local tempImageLocation = "/.lightshot-temp-image"
			local f = io.open(tempImageLocation, "w")
			f:write(v)
			f:close()
			local a = paintutils.loadImage(tempImageLocation)
			paintutils.drawImage(a, 2, 1)
			fs.delete(tempImageLocation)
			recordData = recordData .. "sp(" .. duration .. ")\n"
		end

		recordData = recordData .. "sp(" .. duration .. ")\n"
	end

	recordData = recordData .. "term.setCursorBlink(false)\n"
	recordData = recordData .. "if term.isColor() then term.setTextColor(colors.yellow)\n"
	recordData = recordData .. "else term.setTextColor(colors.white) end\n"
	recordData = recordData .. "term.setBackgroundColor(colors.black)\n"
	recordData = recordData .. "term.clear()\n"
	recordData = recordData .. "term.setCursorPos(1, 1)\n"
	recordData = recordData .. "print(\"The End! :D\")\n"

	nfaRecording = false
	term.restore()
	term.setCursorBlink(false)
	if term.isColor() then term.setTextColor(colors.yellow)
	else term.setTextColor(colors.white) end
	term.setBackgroundColor(colors.black)
	term.clear()
	term.setCursorPos(1, 1)
	print("Movie Recorded Successfully!")

	local f = io.open(location:sub(1, -5), "w")
	f:write(recordData)
	f:close()
	error()
end


--  -------- Main

local function menu()
	local usages = false
	if #args > 0 then
		if args[1] == "update" then
			print("Downloading Updates...")
			local a = updateClient()
			if a then print("Updated!")
			else print("No Updates Found!") end
		elseif args[1] == "tomovie" then
			if #args >= 3 and args[2]:sub(-4, -1) == ".nfa" then
				local loc = "/" .. shell.resolve(args[2])
				if not(fs.exists(loc)) then print("File does not exist!")
				elseif fs.isDir(loc) then print("File is a directory!")
				else
					local dur = tonumber(args[3])
					if dur then return "tomovie", loc, dur
					else print("Duration Must be an Integer!") end
				end
			else usages = true end
		else
			local loc = "/" .. shell.resolve(args[1])
			if fs.exists(loc) then fs.delete(loc) end

			if fs.isReadOnly(loc) then print("File is read only! D:")
			else
				term.setTextColor(colors.white)
				term.setBackgroundColor(colors.black)
				term.clear()
				term.setCursorPos(1, 3)
				print("You can press F1 to end the recording!")

				if term.isColor and term.isColor() then term.setTextColor(colors.yellow) end
				if args[2] ~= "--skip" then
					for i = 1, 3 do
						term.setCursorPos(1, 1)
						term.clearLine()
						write("Recording in " .. 4 - i .. "...")
						sleep(1)
					end
				end

				return "record", loc
			end
		end
	else usages = true end

	if usages then
		print("Usages:")
		print("lightshot <path>")
		print("  - Record a video")
		print("lightshot tomovie <path> <duration>")
		print("  - Turn a .nfa animation into a movie")
		print("    Wait <duration> seconds between each frame")
		print("lightshot update")
		print("  - Update Lightshot")
	end
end

local function main()
	local action, location, duration = menu()
	if action and location then
		clock = os.clock()
		term.redirect(newTerm)

		term.setTextColor(colors.white)
		term.setBackgroundColor(colors.black)
		term.clear()
		term.setCursorPos(1, 1)
		if action == "record" then
			parallel.waitForAll(function()
				record(location)
			end, function()
				shell.run("/rom/programs/shell")
				os.queueEvent(event_exitRecording)
			end)
		elseif action == "tomovie" then
			movie(location, duration)
		end
	end
end

-- Run
local oldDir = shell.dir()
main()
shell.setDir(oldDir)