
dofile("table.lua")

local playerInfo = playerInfo

multiplayer = {}
multiplayer.replayFile = {mapSize = 2, mapSeed = 1, turns = {} }
_replayExtension = ".hg3"

-- defines:
local NUM_CLIENTS = 1
local HOST_IP = "localhost"


local numClients = 0
local connectState, myId = 0, 1000
multiplayer.gameStarted = false

-- game turn synching:
local turnCount, frameTime = 0, 0
local currentTurn, nextCommandTurn = 1, 3
local turnCountLength, latencyWaitTime = 3, 0

local localNextTurnCommands = {}

local turnData = {
	{ sequence = 0, allDone = true, popped = false },
	{ sequence = 1, allDone = true, popped = false },
	{ sequence = 2, allDone = false, popped = false }
}

--[[
	PACKET LIST

	5 - New Player
	6 - Remove Player
	
	7 - (Special) Our Globe game info

	15 - turn completed, client
	16 - turn completed, server
]]

--[[
	COMMAND LIST

	1 - Move: x, y, unitCount, units ...
	2 - Create: unit
	3 - Cancel Purchase
	4 - Move: target, unitCount, units ...
	5 - Stop: unitCount, units ...
	6 - Hold: unitCount, units ...
	7 - Chat: text
	
	100 - New latency setting: turncount
]]

multiplayer.saveReplayFile = function()
	if multiplayer.replayFile then
		print("Saving replay...")
		local path = daisy.getUserFolderPath("replays/")
		local filename = path .. "LastReplay" .. _replayExtension
		table.save(multiplayer.replayFile, filename)
		multiplayer.replayFile = nil
		
		print("Saved to: " .. filename)
	end
end

multiplayer.runReplay = function(filename)

	local path = daisy.getUserFolderPath("replays/")
	multiplayer.replayFile = table.load(path .. filename)
	if multiplayer.replayFile then
		_mapSize, _mapSeed = multiplayer.replayFile.mapSize, multiplayer.replayFile.mapSeed
		multiplayer.gameStarted = true
		multiplayer.isReplay = true
		
		playerInfo.addPlayer(1000, multiplayer.replayFile.hostName) -- host
		playerInfo.addPlayer(1, multiplayer.replayFile.clientName) -- client
		
		-- copy all commands to the player list
		
		for index, turn in pairs(multiplayer.replayFile.turns) do
			local sequence = turn.sequence
			
			for listIn, list in pairs(turn.actions) do
				playerInfo.addPlayerCommands(list.id, sequence, list.commands)
			end
		end
		
		-- prevent recording replays of replays
		multiplayer.replayFile = nil
	end

end

local function checkTurnAllDone(turn)
	turn.allDone = playerInfo.areAllPlayersDone(turn.sequence) or multiplayer.isReplay
	return turn.allDone
end

local function clearAllDone(turn)
	turn.allDone = false
	turn.popped = false
end

local function sendServerCommands(sequence)


	local packet = {sequence}
	local commandList = playerInfo.getPlayerCommands(sequence, false)
	local position = 2
	for p=1, #commandList do

		local id = commandList[p].id
		local commands = commandList[p].commands

		for c=1,#commands do
			packet[position] = id
			position = position + 1

			for i=1, #(commands[c]) do
				packet[position] = commands[c][i]
				position = position + 1
			end
		end
	end

	daisy.broadcastPacket(16, packet)
end

local function checkTurnFromServer(values)

	sequence = values[1]

	local playerCommands = {}
	local position = 2
	while position <= #values do

		local id = values[position]
		position = position + 1

		playerCommands[id] = playerCommands[id] or {}

		local command = values[position]
		
		-- The game is responsible for creating a multiplayer.unpackPacket function that
		-- will pick the correct number of variables from the packet array and return
		-- it as a list (the first variable must be the command id)
		
		local unpacked, skipLength = multiplayer.unpackPacket(command, values, position)
		position = position + skipLength
		table.insert(playerCommands[id], unpacked)
		
		--print("User " .. id .. ", " .. command .. ", " .. skipLength)

	end

	local players = playerInfo.players
	for i=1, #players do
		local id = players[i].id
		playerInfo.addPlayerCommands(id, sequence, playerCommands[id] or {})
	end
end

local function checkTurnFromClient(sender, values)

	local sequence = values[1]
	local commands = {}

	if #values > 1 then

		local position = 2
		while position <= #values do

			local command = values[position]
			
			-- The game is responsible for creating a multiplayer.unpackPacket function that
			-- will pick the correct number of variables from the packet array and return
			-- it as a list (the first variable must be the command id)
			
			local unpacked, skipLength = multiplayer.unpackPacket(command, values, position)
			position = position + skipLength
			table.insert(commands, unpacked)
			
		end
	end

	playerInfo.addPlayerCommands(sender, sequence, commands)

	if playerInfo.areAllPlayersDone(sequence) then
		sendServerCommands(sequence)
	end
end

local function currentTurnCompleted()
	clearAllDone(turnData[currentTurn])
	turnData[currentTurn].sequence = turnData[currentTurn].sequence + 3

	currentTurn = currentTurn + 1
	if currentTurn == 4 then
		currentTurn = 1
	end
	turnCount = 0

	if connectState == 3 then
		playerInfo.addPlayerCommands(myId, turnData[nextCommandTurn].sequence, localNextTurnCommands)
		if playerInfo.areAllPlayersDone(turnData[nextCommandTurn].sequence) then
			sendServerCommands(turnData[nextCommandTurn].sequence)
		end
	else
		-- notify the server
		local packet = { turnData[nextCommandTurn].sequence }
		if #localNextTurnCommands > 0 then
			local position = 2
			for i=1, #localNextTurnCommands do
				local command = localNextTurnCommands[i]

				for c=1,#command do
					packet[position] = command[c]
					position = position + 1
				end
			end
			--print("P: " .. position .. " S: " .. turnData[nextCommandTurn].sequence)
		end
		daisy.sendPacket(1000, 15, packet)
	end
	localNextTurnCommands = {}

	nextCommandTurn = nextCommandTurn + 1
	if nextCommandTurn == 4 then
		nextCommandTurn = 1
	end
end

multiplayer.frameUpdate = function(time, nextFrameFunction)

	local w,h = daisy.getScreenSize()

	if multiplayer.gameStarted then

		frameTime = frameTime + time
		while frameTime > .00625 do

			frameTime = frameTime - .00625

			if turnData[currentTurn].allDone or checkTurnAllDone(turnData[currentTurn]) then

				if not turnData[currentTurn].popped then
					turnData[currentTurn].popped = true
					local actions = playerInfo.getPlayerCommands(turnData[currentTurn].sequence, true)
					
					if #actions > 0 and multiplayer.replayFile then
						table.insert(multiplayer.replayFile.turns, {sequence = turnData[currentTurn].sequence, actions = actions})
					end

					-- parse commands
					for listIn, list in pairs(actions) do

						for cIn, command in pairs(list.commands) do
						
							-- the game must create this callback function
							
							multiplayer.parseCommand(list.id, command)

							--for i=1, #command do
							--	print("i " .. i .. ", c " .. (command[i] or "nil"))
							--end

						end
					end
				end
				
				nextFrameFunction(.00625)

				turnCount = turnCount + 1
				if turnCount == turnCountLength then
					currentTurnCompleted()
				end
			else
				latencyWaitTime = latencyWaitTime + .00625
				if latencyWaitTime > 3 and connectState == 3 then
					latencyWaitTime = 0
					multiplayer.addTurnCommand({100, turnCountLength + 1})
					--print("Latency adjustment to " .. (turnCountLength + 1))
				end
			end
		end

	else
	end
end

multiplayer.setLatencyLength = function(length)
	turnCountLength = length
	--print("Latency adjustment to " .. turnCountLength)
end

multiplayer.addTurnCommand = function(packetList)

	if not multiplayer.isReplay then
		table.insert(localNextTurnCommands, packetList)
	end

end

multiplayer.joinGame = function(ip, port, userName)
	if connectState == 0 then
		daisy.joinHost("luaGame", userName or "luaClient", ip, port)
		connectState = 1
	end
end

multiplayer.hostGame = function(port, numClients, username)
	if connectState == 0 then
		username = username or "luaHost"
		daisy.startHost("luaGame", username, port, numClients + 1)
		connectState = 3
		multiplayer.replayFile.mapSeed, multiplayer.replayFile.mapSize = _mapSeed, _mapSize

		playerInfo.addPlayer(1000, username)
	end
end

local function connectFailed()
	print("connectFailed")
end

local function connectSucceeded(localId)
	--print("connectSucceeded " .. localId)
	connectState = 2
	myId = localId
	--multiplayer.gameStarted = true

	--playerInfo.addPlayer(1000)
	--playerInfo.addPlayer(myId)

	--if NUM_CLIENTS == 1 then
	--	multiplayer.gameStarted = true
	--end
end

local function clientConnected(username, id)
	--print("clientConnected " .. id .. ", " .. username)
	numClients = numClients + 1

	local players = playerInfo.players
	for i=1,#players do
		--if players[i].id ~= 1000 then
			daisy.sendPacket(id, 5, {players[i].id, players[i].name})
		--end
	end
	playerInfo.addPlayer(id, username)

	daisy.broadcastPacket(5, {id, username})
	--nextClientObject = nextClientObject + 1

	if numClients == NUM_CLIENTS then
		--print("sent " .. _mapSeed .. ", " .. _mapSize)
		daisy.broadcastPacket(7, {_mapSeed, _mapSize })
		
		multiplayer.replayFile.hostName = playerInfo.getPlayer(1000).name
		multiplayer.replayFile.clientName = playerInfo.getPlayer(id).name
		
		multiplayer.gameStarted = true
	end

end

local function clientDisconnected(id)
	print("clientDisconnected")
	numClients = numClients - 1
	
	if multiplayer.gameStarted then
		multiplayer.saveReplayFile()
	end
	
	multiplayer.gameStarted = false
end

local function hostDisconnected()
	print("hostDisconnected")

	if multiplayer.gameStarted then
		multiplayer.saveReplayFile()
	end

end

local function packetReceived(id, values, sender)
	--print("packetReceived: " .. id .. ", connectState " .. connectState)

	if connectState == 3 then
		if id == 15 then
			checkTurnFromClient(sender, values)
		end
	elseif connectState > 0 then
		-- server->client packets
		if id == 16 then
			checkTurnFromServer(values)
		elseif id == 5 then
			playerInfo.addPlayer(values[1], values[2])

			--if values[1] == myId then
			--	myObject = values[2]
			--end

			--if #playerInfo.players == (NUM_CLIENTS+1) then
			--	multiplayer.gameStarted = true
			--end
		elseif id == 7 then
		
			--print("rec " .. values[1] .. ", " .. values[2])
			_mapSeed, _mapSize = values[1], values[2]
			multiplayer.replayFile.mapSeed, multiplayer.replayFile.mapSize = _mapSeed, _mapSize
			multiplayer.replayFile.hostName = playerInfo.getPlayer(1000).name
			multiplayer.replayFile.clientName = playerInfo.getPlayer(myId).name
			multiplayer.gameStarted = true
		
		end
	end

end

hook.add("connectFailed", connectFailed)
hook.add("connectSucceeded", connectSucceeded)
hook.add("clientConnected", clientConnected)
hook.add("clientDisconnected", clientDisconnected)
hook.add("hostDisconnected", hostDisconnected)
hook.add("packetReceived", packetReceived)
