dofile("../defs/actionCommands.lua")

ActionSync = inherited("ActionSync", State)

ActionSync.packets = {
	{name = "addPlayer", host = true, callSelf = true, structure = {"g","i"}, prio = PACKETS.IMMEDIATE, mode = PACKETS.RELIABLE, 
		call = "addPlayer"},	
	{name = "playerLeft", host = true, callSelf = true, structure = {"g"}, prio = PACKETS.IMMEDIATE, mode = PACKETS.RELIABLE, 
		call = "playerLeft"},
	{name = "updatePlayer", host = true, callSelf = true, structure = {"g", "t"}, prio = PACKETS.IMMEDIATE, mode = PACKETS.RELIABLE_SEQUENCED, 
		call = "updatePlayer"},
	{name = "setNick", callSelf = true, structure = {"s"}, prio = PACKETS.IMMEDIATE, mode = PACKETS.RELIABLE, 
		relayCall = "setNick"},
	{name = "assignNick", host = true, structure = {"g", "s"}, prio = PACKETS.IMMEDIATE, mode = PACKETS.RELIABLE, 
		call = "assignNick"},
	{name = "start", host = true, callSelf = true, structure = {}, prio = PACKETS.IMMEDIATE, mode = PACKETS.RELIABLE, 
		call = "start"},
	{name = "createEnts", host = true, structure = {"it", "npt"}, prio = PACKETS.IMMEDIATE, mode = PACKETS.RELIABLE_SEQUENCED, 
		},
	{name = "removeEnts", host = true, structure = {"it"}, prio = PACKETS.IMMEDIATE, mode = PACKETS.RELIABLE_SEQUENCED, 
		call = "removeEnts"},
	{name = "assignEnts", host = true, structure = {"aet"}, prio = PACKETS.IMMEDIATE, mode = PACKETS.RELIABLE_SEQUENCED, 
		call = "assignEnts"},
	{name = "syncEnts", host = true, structure = {"esynct"}, prio = PACKETS.IMMEDIATE, mode = PACKETS.UNRELIABLE_SEQUENCED, 
		call = "syncEnts"},
	{name = "setMouse", callSelf = true,broadcast = true, structure = {"f","f"}, prio = PACKETS.IMMEDIATE, mode = PACKETS.UNRELIABLE_SEQUENCED, 
		relayCall = "setMouse"},
	{name = "relayInput", structure = {"f","f","b","b"}, prio = PACKETS.IMMEDIATE, mode = PACKETS.UNRELIABLE_SEQUENCED, 
		relayCall = "relayInput"},
	{name = "giveOptions", setup = "registerOptions", structure = {"s","tt"}, prio = PACKETS.IMMEDIATE, mode = PACKETS.UNRELIABLE_SEQUENCED, 
		call = "giveOptions"},
	{name = "selectOption", structure = {"i"}, prio = PACKETS.IMMEDIATE, mode = PACKETS.UNRELIABLE_SEQUENCED, 
		relayCall = "selectOption"},
}

function ActionSync:new(gameString, versionString)
	local g = instance(self)
	g.gameString = gameString
	g.versionString = versionString
	g.started = false
	g.players = {}
	g.nextEid = 2 -- 1 is the instances of each faked simulation
	return g
end

function ActionSync:setCallback(c)
	self.callback = c
end

function ActionSync:generateEid()
	if self.hosted then
		local eid = self.nextEid
		self.nextEid = self.nextEid + 1
		return eid
	end
end

function ActionSync:broadcast(packetType, ...)
	self.network:sendPacket( packetType, ...)
end

function ActionSync:sendTo(player, packetType, ...)
	self.network:sendPacketTo(player.guid, packetType, ...)
end

function ActionSync:request(player, packetType, ...)
	if not player.requests[packetType] then
		player.requests[packetType] = true
		self:sendTo(player, packetType, ...)
	end
end

function ActionSync:host(port, expectedPlayers, nick)
	self.expectedPlayers = expectedPlayers
	self.network = Network:new(self.gameString, self.versionString, self.packets)
	self.network:setCallback(self)
	if self.network:host(port, expectedPlayers) then
		self.hosted = true
		local player = self:addPlayer(self.network:getGuid(), self:getFreePlayerId())
		return true
	end
end

function ActionSync:replaceDevice(d)
	if not self.hosted then
		self.network:replaceDevice(d)
		self.hosted = true
		self:addPlayer(self.network:getGuid(), self:getFreePlayerId())
	end
end

function ActionSync:join(ip, port)
	self.network = Network:new(self.gameString, self.versionString, self.packets)
	self.network:setCallback(self)
	self.network:join(ip, port)
end

function ActionSync:getNumberOfPlayers()
	local num = 0
	for index, player in pairs(self.players) do
		num = num +1
	end
	return num
end

function ActionSync:getLocalPlayer()
	return self.localPlayer
end

function ActionSync:getPlayerId(guid)
	for index, player in pairs(self.players) do
		if player.guid == guid then
			return player.id
		end
	end
end

function ActionSync:getPlayer(guid)
	for index, player in pairs(self.players) do
		if player.guid == guid then
			return player
		end
	end
end

function ActionSync:getFreePlayerId()
	local free = 1
	local found = true
	while found do
		found = false
		for index, player in pairs(self.players) do
			if free == player.id then
				free = free + 1
				found = true
				break
			end
		end
	end
	return free
end

function ActionSync:update(time)
	if self.network then
		self.network:update(time)
	end
	
	if self.started then
	end
end

--[[

                                                        
  ,ad8888ba,                              88            
 d8"'    `"8b                             88            
d8'                                       88            
88            88,dPYba,,adPYba,   ,adPPYb,88 ,adPPYba,  
88            88P'   "88"    "8a a8"    `Y88 I8[    ""  
Y8,           88      88      88 8b       88  `"Y8ba,   
 Y8a.    .a8P 88      88      88 "8a,   ,d88 aa    ]8I  
  `"Y8888Y"'  88      88      88  `"8bbdP"Y8 `"YbbdP"'  
                                                                                                                                                                      

]]


--[[
                                                        
 ad88888ba                                              
d8"     "8b              ,d                             
Y8,                      88                             
`Y8aaaaa,    ,adPPYba, MM88MMM 88       88 8b,dPPYba,   
  `"""""8b, a8P_____88   88    88       88 88P'    "8a  
        `8b 8PP"""""""   88    88       88 88       d8  
Y8a     a8P "8b,   ,aa   88,   "8a,   ,a88 88b,   ,a8"  
 "Y88888P"   `"Ybbd8"'   "Y888  `"YbbdP'Y8 88`YbbdP"'   
                                           88           
                                           88         
]]

function ActionSync:registerOptions(guid, optionsId, options)
	local player = self:getPlayer(guid)
	if player then
		player.options = options
		player.optionsId = optionsId
	end
end



--[[
                                          
  ,ad8888ba,             88 88            
 d8"'    `"8b            88 88            
d8'                      88 88            
88            ,adPPYYba, 88 88 ,adPPYba,  
88            ""     `Y8 88 88 I8[    ""  
Y8,           ,adPPPPP88 88 88  `"Y8ba,   
 Y8a.    .a8P 88,    ,88 88 88 aa    ]8I  
  `"Y8888Y"'  `"8bbdP"Y8 88 88 `"YbbdP"'  
       
                                                                                  
]]

function ActionSync:playerLeft(id)
	if self.hosted then
		--local player = self.players[id]
		--print("remove player stuff?")
	end
	if id then
		self.players[id] = nil
	end
end

function ActionSync:giveOptions(description, options)
	if self.callback and self.callback.setOptions then
		self.callback:setOptions(description, options)
	end
end

function ActionSync:start()
	self.started = true
	if self.callback and self.callback.onStarted then
		self.callback:onStarted()
	end
end

function ActionSync:addPlayer(guid, id)
	for index, player in pairs(self.players) do
		if player.guid == guid then
			return player
		end
	end
	local player = {guid = guid, nick = "-", id = id, x = 0, y = 0, requests = {}}
	self.players[id] = player
	
	if self.hosted then
		if self.callback and self.callback.onJoined then
			self.callback:onJoined(player)
		end
		if self:getNumberOfPlayers() >= self.expectedPlayers then
			self.network:sendPacket("start")
		end
		self:onMessage("players "..self:getNumberOfPlayers().."/" ..self.expectedPlayers)
	end

	if guid == self.network.guid then
		self.localPlayer = player
	end
	
	return player
end

function ActionSync:removeEnts(eids)
	if self.callback and self.callback.removeEnt then
		for index, eid in ipairs(eids) do
			self.callback:removeEnt(eid)
		end
	end
end

function ActionSync:assignEnts(assigns)
	if self.callback and self.callback.assignEnt then
		for index, def in ipairs(assigns) do
			self.callback:assignEnt(def.parent, def.index, def.child)
		end
	end
end

function ActionSync:syncEnts(ents)
end

function ActionSync:updatePlayer(guid, data)
	local player = self:getPlayer(guid)
	if player then
		for index, var in pairs(data) do
			player[index] = var
		end
	end
end

function ActionSync:assignNick(guid, nick)
	local player = self:getPlayer(guid)
	if player then
		player.nick = nick
	end
end


--[[
                                                           
88888888ba            88                                   
88      "8b           88                                   
88      ,8P           88                                   
88aaaaaa8P' ,adPPYba, 88 ,adPPYYba, 8b       d8 ,adPPYba,  
88""""88'  a8P_____88 88 ""     `Y8 `8b     d8' I8[    ""  
88    `8b  8PP""""""" 88 ,adPPPPP88  `8b   d8'   `"Y8ba,   
88     `8b "8b,   ,aa 88 88,    ,88   `8b,d8'   aa    ]8I  
88      `8b `"Ybbd8"' 88 `"8bbdP"Y8     Y88'    `"YbbdP"'  
                                        d8'                
                                       d8'                 

]]



function ActionSync:setNick(guid, nick)
	local player = self:getPlayer(guid)
	if player then
		player.nick = nick
		self:broadcast("assignNick",guid, nick)
	end
end


function ActionSync:setMouse(guid, x, y)
	local player = self:getPlayer(guid)
	if player then
		player.x = x
		player.y = y
	end
end

function ActionSync:relayInput(guid, angle, amount, action, alt)
	local player = self:getPlayer(guid)
	if player then
		if player.pilot then
			player.pilot:setControls(angle, amount, action, alt)
		end
	end
end


function ActionSync:selectOption(guid, i)
	local player = self:getPlayer(guid)
	if player then
		if player.options then
			if self.callback.selectOption then
				self.callback:selectOption(player, player.optionsId, player.options[i])
			end
		end
	end
end

--[[
                                                                                     
888b      88                                                              88         
8888b     88              ,d                                              88         
88 `8b    88              88                                              88         
88  `8b   88  ,adPPYba, MM88MMM 8b      db      d8  ,adPPYba,  8b,dPPYba, 88   ,d8   
88   `8b  88 a8P_____88   88    `8b    d88b    d8' a8"     "8a 88P'   "Y8 88 ,a8"    
88    `8b 88 8PP"""""""   88     `8b  d8'`8b  d8'  8b       d8 88         8888[      
88     `8888 "8b,   ,aa   88,     `8bd8'  `8bd8'   "8a,   ,a8" 88         88`"Yba,   
88      `888  `"Ybbd8"'   "Y888     YP      YP      `"YbbdP"'  88         88   `Y8a  
                                                                                     
                                                                                     
                                                                                      
  ,ad8888ba,             88 88 88                                88                   
 d8"'    `"8b            88 88 88                                88                   
d8'                      88 88 88                                88                   
88            ,adPPYYba, 88 88 88,dPPYba,  ,adPPYYba,  ,adPPYba, 88   ,d8  ,adPPYba,  
88            ""     `Y8 88 88 88P'    "8a ""     `Y8 a8"     "" 88 ,a8"   I8[    ""  
Y8,           ,adPPPPP88 88 88 88       d8 ,adPPPPP88 8b         8888[      `"Y8ba,   
 Y8a.    .a8P 88,    ,88 88 88 88b,   ,a8" 88,    ,88 "8a,   ,aa 88`"Yba,  aa    ]8I  
  `"Y8888Y"'  `"8bbdP"Y8 88 88 8Y"Ybbd8"'  `"8bbdP"Y8  `"Ybbd8"' 88   `Y8a `"YbbdP"'  
  
]]


function ActionSync:createEnt(eid, classRef, ...)
	if self.callback and self.callback.createEnt then
		self.callback:createEnt(eid, classRef, ...)
	end
end


function ActionSync:onConnectedToServer(myGuid)
	self.connected = true
	self:onMessage("Connected to server")
end

function ActionSync:onClientConnected(guid)
	if self:getNumberOfPlayers() < self.expectedPlayers then
		self.network:sendPacket("addPlayer", guid, self:getFreePlayerId())
		for index, player in pairs(self.players) do
			self.network:sendPacketTo(guid, "addPlayer", player.guid, player.id)
		end
		self:onMessage("Client connected to us")
	else
		--TODO: disconnect connection
		self:onMessage("Game is full")
	end
end

function ActionSync:onClientDisconnected(guid)
	local player = self:getPlayer(guid)
	if player then
		if player.pilot then
			if player.pilot.ship then
				player.pilot.ship.health = 0
			end
			player.pilot:remove()
		end
	end
	self.network:sendPacket("playerLeft", self:getPlayerId(guid))
end

function ActionSync:onDisconnected()
	if self.callback then
		if self.callback.onGameEnded then
			self.callback.onGameEnded(self.callback)
		end
	end
end

function ActionSync:getEntId(ent)
	return ent.eid
end

function ActionSync:onEntChanged(ent)
	if self.callback.onEntChanged then
		return self.callback:onEntChanged(ent)
	end
end

function ActionSync:getEnt(eid)
	if self.callback.getEnt then
		return self.callback:getEnt(eid)
	else
		print("cannot get entity", eid, ", doesn't have getEnt in callback")
	end
end

function ActionSync:renderPlayers(x,y,scale, angle, a, r, g, b)
	for index, player in pairs(self.players) do
		video.renderSpriteState(SPRITES.circleOutline, x + player.x*scale,y+ player.y*scale, scale/4,angle, a, r, g, b )
	end
end


