tsc = {
	cmd = include "Server/tsc_cmd.lua",
	specialJump = {}
}

function tsc.load(filename,uiscript)
	local head = filename == "Head.tsc"

	file = io.open(filename,IO.READ,ENDIAN.LITTLE)
	file:seek(0,SEEK.END)
	local len = file:tell() -- Get the length of the file
	file:seek(math.floor(len/2),SEEK.SET)
	local off = file:getnum(1,UNSIGNED) -- Get the 'encryption' key of the file
	file:seek(0,SEEK.SET)
	local crypt = file:getstr(len,true) -- Get the entire data of the file (Must be less than 4 GB)
	file:close()

	local script = ""
	for i = 1,len
		local char
		-- Get the proper character to add to the script as the next letter.
		if i-1 == math.floor(len/2) -- Don't un-cypher the key into a random NULL!
			char = crypt:sub(i,i)
		else
			char = string.num(crypt:bin(i)-off)
		end
		if char ~= '\r' -- \n only!
			script = script .. char
		end
	end
	crypt = nil
	collectgarbage("collect")

	local special = ""
	local evSt,evEn,event,cmdSt,cmdEn,cmd,argSt,argEn,arg,last_cmd
	local last_event = 0
	local compiled = {
		name = filename
	}
	while true -- For each event in the script,
		evSt,evEn,event = script:find("#(%d%d%d%d)[^\n]*%s+",last_event+1)
		if not event break end

		if head
			if last_event+1 < evSt
				special = special .. script:sub(last_event+1,evSt-1)
			end
		end
		last_event = evEn

		--console.debug("Parsing event #"..event)
		event = tonumber(event)
		local e = {}
		compiled[event] = e

		cmdEn = evEn
		last_cmd = evEn
		while true -- For each command in the event,
			cmdSt,cmdEn,cmd = script:find("<([%w%p][%w%p][%w%p])",cmdEn+1)
			if not cmd break end
			--console.debug("Processing cmd <"..cmd)

			if last_cmd+1 < cmdSt
				local text = script:sub(last_cmd+1,cmdSt-1)
				if not e[1] and text:find("^%s*#%d%d%d%d")
					console.debug("Empty event #"..event)
					break
				end
				if text:find("#%d%d%d%d")
					console.print("WARNING: Event #"..event.." in "..filename.." seems to run into the event below it. Please use <EVE to switch events!")
				end
				table.insert(e,text)
				if text:find("%S")
					console.debug("Text: '"..text:gsub("\n+",' ').."'")
				end
			end
			last_cmd = cmdEn

			local c = {
				name = cmd,
				cmd = tsc.cmd[cmd],
				args = {}
			}

			argEn = cmdEn
			while true -- For each argument in the command,
				argSt,argEn,arg = script:find("^(%d%d%d%d):?",argEn+1)
				if not arg break end
				last_cmd = argEn
				table.insert(c.args,tonumber(arg))
				if argEn - argSt ~= 4 break end -- Makes the : seperation non-optional.
			end

			if not uiscript and c.name == "WAI" and c.cmd(nil,unpack(c.args))
				-- In the stage scripts, turn <WAI9999 into <END.
				-- Allows for dynamic detection of the end of Intro events by moving to title when the script ends,
				-- instead of relying on an entirely seperate timer like Pixel uses.
				cmd = "END"
				c = {
					name = cmd,
					cmd = tsc.cmd[cmd],
					args = {}
				}
			end

			--last_event = last_cmd
			if not c.cmd
				console.print("WARNING: Unknown TSC command '<"..cmd.."' in "..filename.." event #"..event)
				continue
			end
			table.insert(e,c)
			if c.cmd(nil,unpack(c.args))
				-- Command functions should return true or false if called without self.
				-- True means this command stops the script or calls a new event, i.e. the event ends here.
				-- False means the script continues.
				break -- Next event
			end
		end
		collectgarbage("step")
	end

	if head
		if last_event+1 < #script
			special = special .. script:sub(last_event+1)
		end
	end

	script = nil
	collectgarbage("collect")

	if DEBUG
		-- Debug script processing by writing out the final set of TSC command lists
		local eventlist = {}
		for k in pairs(compiled)
			if type(k) ~= "number"
				continue
			end
			table.insert(eventlist,k)
		end
		table.sort(eventlist)
		file = io.open(filename:sub(1,-4).."LOG",IO.WRITE|IO.TEXT)
		for _,v in ipairs(eventlist)
			local k = v
			v = compiled[k]
			file:putstr(string.format("#%04d\n",k))
			for _,v in ipairs(v)
				if type(v) == "string"
					file:putstr(v)
					continue
				end
				file:putstr('<'..v.name)
				for i,v in ipairs(v.args)
					if i ~= 1
						file:putstr(':')
					end
					file:putstr(string.format("%04d",v))
				end
			end
			file:putstr("\n\n")
		end
		file:close()
	end

	if head
		for flag,map in special:gmatch("(40%d%d):([%w%p]+)")
			console.debug("Set special jump "..flag.." to stage "..map)
			tsc.specialJump[tonumber(flag)] = map
		end
	end
	return compiled
end

function tsc.get_number(script,pos)
	return tonumber(script:sub(pos,pos+3))
end

tsc.Head = tsc.load("Head.tsc")
tsc.StageSelect = tsc.load("StageSelect.tsc",true)
tsc.ArmsItem = tsc.load("ArmsItem.tsc",true)

function tsc:start(event,script,force)
	if force == nil and type(script) == "boolean"
		force = script
		script = nil
	end
	if not force and self.tsc
		return
	end
	if not script
		script = self.stage.tsc
	end
	assert(script)
	if script[event]
		-- Given script or map script contains this event? Good!
	elseif tsc.Head[event]
		-- No? But the Head script contains this event? Use that, then!
		script = tsc.Head
	else
		-- Oh noes, event does not exist in any scripts we checked. :(
		console.print("WARNING: Script event "..search.." not found in "..script.name)
		return
	end
	self.tsc = {
		script = script,
		event = script[event],
		eventnum = event,
		pos = 1
	}
	tsc.run(self)
end

function tsc:run()
	-- No script? No run!
	if not self.tsc
		return
	end

	-- Wait longer if necessary
	if self.tsc.wait
		if type(self.tsc.wait) == "string"
			return
		end
		if self.tsc.wait == 9999
			return
		end
		self.tsc.wait = self.tsc.wait - 1
		if self.tsc.wait == 0
			self.tsc.wait = nil
		end
		return
	end

	-- Interpret commands
	while self.tsc.event[self.tsc.pos]
		local a = self.tsc.event[self.tsc.pos]
		self.tsc.pos = self.tsc.pos + 1
		if type(a) == "string"
			if not self.tsc.msg
				continue
			end
			console.debug(a)
			net.tsc_text(self.id,a)
			if not self.tsc.tur
				self.tsc.wait = "text"
				break
			end
			continue
		end
		a.cmd(self,unpack(a.args))
		-- Stop if wait or script end
		if not self.tsc or self.tsc.wait
			break
		end
	end
	if self.tsc and not self.tsc.event[self.tsc.pos]
		console.print("WARNING: Script event "..self.tsc.eventnum.." in "..self.tsc.script.." did not end properly! (EOF reached)")
		tsc.cmd['END'](self)
	end
end
