func (g *Game) listenClientMessage(kill chan bool) {
	serverlog.General("listenClientMessage has started for", g.Identification())
	for {
		select {
		case message, more := <-g.Initiator.IncommingMessages:
			if !more {
				serverlog.General(g.Initiator.Identification(), "(player 1) has disconnected while in", g.Identification(),
					"so sending disconnect message to game instance")
				g.UDS.Write(newDisconnectedMessage(true))
			} else {
				g.interpretClientMessage(true, message)
			}
		case message, more := <-g.Player2.IncommingMessages:
			if !more {
				serverlog.General(g.Initiator.Identification(), "(player 2) has disconnected while in", g.Identification(),
					"so sending disconnect message to game instance")
				g.UDS.Write(newDisconnectedMessage(false))
			} else {
				g.interpretClientMessage(false, message)
			}
		case <-kill:
			serverlog.General(g.Identification(), "listenClientMessage gorutine received kill signal so terminating")
			close(kill)
			return
		}
	}
}
// JoinGame handles the case where a player sends a JoinGame message
func JoinGame(conn *connection.Conn, allGames map[string]*games.Game, message messages.JoinGameMessage) {
	serverlog.General("Received JoinGame message from conn:", conn.Alias)

	if !conn.Registered {
		serverlog.General("Unregistered", conn.Identification, "called JoinGame")
		denied := messages.NewJoinGameDeniedMessage("You have not registered an Alias")
		conn.Write(denied.Bytes())
		return
	}

	if conn.InGame {
		serverlog.General(conn.Identification(), "attempted to join a game but is already in game:", allGames[conn.GameID].Name)
		denied := messages.NewJoinGameDeniedMessage("You re already in a game")
		conn.Write(denied.Bytes())
		return
	}

	if _, ok := allGames[message.GameID]; !ok {
		serverlog.General(conn.Identification(), "attempted to join a non existing game:", message.GameID)
		denied := messages.NewJoinGameDeniedMessage("No games with that ID")
		conn.Write(denied.Bytes())
		return
	}

	game := allGames[message.GameID]
	serverlog.General(conn.Identification(), "Successfully joined", game.Identification())
	conn.InGame = true
	conn.GameID = game.ID
	game.Start(conn)
	go listenFinished(game, allGames)
}
func (g *Game) deleteSocket() {
	serverlog.General("Deleting socket:", g.UDSPath, "for", g.Identification())
	err := os.Remove(g.UDSPath)
	if err != nil {
		if os.IsNotExist(err) {
			serverlog.General("socket:", g.UDSPath, "does not exist so can't be deleted")
		} else {
			serverlog.Warning("Failed to delete socket:", g.UDSPath, "for", g.Identification(), "err:", err)
		}
	}
}
// RequestGameList handles the case where a player sends a RequestGameList message
func RequestGameList(conn *connection.Conn, allGames map[string]*games.Game, message messages.RequestGameListMessage) {
	serverlog.General("Received RequestGameList message from", conn.Identification())

	if !conn.Registered {
		serverlog.General("Unregistered", conn.Identification(), "called RequestGameList")
		return
	}

	serverlog.General("Sending Game list to", conn.Identification())
	gameList := games.NewGameListMessage(allGames)
	conn.Write(gameList.Bytes())
}
// startWriter starts the writer for the connection
func (conn *Conn) startWriter() {
	serverlog.General("Writer started for", conn.Identification())
	for {
		messageBytes, more := <-conn.outgoingMessages
		if !more {
			serverlog.General("outgoingMessages killed: Writer closed for", conn.Identification())
			return
		}
		length := uint16(len(messageBytes))
		lengthBytes := make([]byte, 2)
		binary.LittleEndian.PutUint16(lengthBytes, length)
		messageToWrite := append(lengthBytes, messageBytes...)
		conn.Socket.Write(messageToWrite)
	}
}
func (g *Game) createSocket() {
	serverlog.General("Creating socket:", g.UDSPath, "for", g.Identification())
	_, err := os.Create(g.UDSPath)
	if err != nil {
		serverlog.Fatal("Failed to create socket:", g.UDSPath, "for", g.Identification(), "err:", err)
	}
}
func (g *Game) interpretGameMessage(message []byte) {
	switch message[0] {
	case 1: // ready
		i := messages.NewStartGameMessage(true,
			0, 0, 0, 0, g.Player2.Alias, g.ID, g.Name)
		p := messages.NewStartGameMessage(false,
			0, 0, 0, 0, g.Initiator.Alias, g.ID, g.Name)
		g.Initiator.Write(i.Bytes())
		g.Player2.Write(p.Bytes())
	case 13: // status
		g.Initiator.Write(message)
		g.Player2.Write(message)
	case 3: // finished
		fin := newFinishedMessage(message)
		serverlog.General(g.Identification(), "Has finished with status", fin)
		inMs := messages.NewGameOverMessage(fin.p1score, fin.p2score, 0)
		p2Ms := messages.NewGameOverMessage(fin.p2score, fin.p1score, 0)
		if fin.p1won {
			inMs.Status = 0
			p2Ms.Status = 1
		} else {
			inMs.Status = 1
			p2Ms.Status = 0
		}
		g.Initiator.Write(inMs.Bytes())
		g.Player2.Write(p2Ms.Bytes())
		g.Kill()
	}
}
// startReader starts the connection reader
func (conn *Conn) startReader() {
	serverlog.General("Reader started for", conn.Identification())
	var messageBuffer bytes.Buffer
	var bytesToRead int

	for {
		buf := make([]byte, 1400)
		dataSize, err := conn.Socket.Read(buf)
		if err != nil {
			serverlog.General("TCP connection closed for", conn.Identification())
			serverlog.General("Reader closing net.conn socket for", conn.Identification())
			conn.Socket.Close()
			serverlog.General("Reader closing OutgoingMessages channel for", conn.Identification())
			close(conn.IncommingMessages)
			serverlog.General("Reader closing IncomingMessages channel for", conn.Identification())
			close(conn.outgoingMessages)
			return
		}

		data := buf[0:dataSize]
		messageBuffer.Write(data)

		for messageBuffer.Len() > 1 {
			if bytesToRead == 0 {
				btrBuffer := make([]byte, 2)
				_, err := messageBuffer.Read(btrBuffer)
				if err != nil {
					serverlog.Fatal("Error happened in reader bts:2 for", conn.Identification(), "Error:", err)
					serverlog.Fatal(err)
				}
				bytesToRead = int(binary.LittleEndian.Uint16(btrBuffer))
			}
			if messageBuffer.Len() >= bytesToRead {
				message := make([]byte, bytesToRead)
				_, err := messageBuffer.Read(message)
				if err != nil {
					serverlog.Fatal("Error happened in reader bts:var for", conn.Identification(), "Error:", err)
				}
				conn.IncommingMessages <- message
				bytesToRead = 0
			} else {
				break
			}
		}
	}
}
// Kill destroys all game related gorutines
func (g *Game) Kill() {
	serverlog.General("Kill called on", g.Identification(), "closing UDS and sending message through FinChan")
	g.Initiator.InGame = false
	if g.Ready {
		g.UDS.Close()
		g.Player2.InGame = false
	}
	g.FinChan <- true
}
func (g *Game) interpretClientMessage(player1 bool, message []byte) {
	switch message[0] {
	case messages.TypeLeaveGame:
		serverlog.General("Someone disconnected from ongoing", g.Identification(), "Telling game instance")
		g.UDS.Write(newDisconnectedMessage(player1))
	case messages.TypeMove:
		mv := messages.NewMoveMessageFromBytes(message)
		g.UDS.Write(newMovementMessage(player1, mv.Position))
	}
}
func (g *Game) listenGameMessage() {
	serverlog.General("listenGameMessage gorutine started for", g.Identification())
	clientListenerStarted := false
	clientKill := make(chan bool, 1)
	for {
		message, more := <-g.gameMessage
		if !more {
			serverlog.General("gameMessage channel closed for", g.Identification(),
				"so listenGameMessage is sending a kill signal to the client listeners and terminating")
			clientKill <- true
			return
		}
		if !clientListenerStarted && message[0] == 1 { // ready message sent
			serverlog.General("Received ready message for", g.Identification(), "so starting clientListeners")
			go g.listenClientMessage(clientKill)
			clientListenerStarted = true
		}
		g.interpretGameMessage(message)
	}
}
// LeaveGame handles the case where a player sends a LeaveGame message
func LeaveGame(conn *connection.Conn, allGames map[string]*games.Game, message messages.LeaveGameMessage) {
	serverlog.General("Received LeaveGame message from", conn.Identification())

	if !conn.Registered {
		serverlog.General("Unregistered", conn.Identification(), "called LeaveGame")
		return
	}

	if !conn.InGame {
		serverlog.General(conn.Identification(), "attempted to leave a game but isn't in a game:", allGames[conn.GameID].Name)
		return
	}

	if allGames[conn.GameID].Ready {
		// will be dealt with by game object
		return
	}

	conn.InGame = false
	delete(allGames, conn.GameID)
}
// RequestAlias handles the situation when a client sends a RequestAlias message
func RequestAlias(message messages.RequestAliasMessage, conn *connection.Conn, al map[string]bool) {
	serverlog.General("Received RequestAlias message from", conn.Identification())

	if conn.Registered {
		serverlog.General(conn.Identification(), "attempted to request new alias:", message.Alias)
		denied := messages.NewAliasDeniedMessage("Already registered with alias: " + conn.Alias)
		conn.Write(denied.Bytes())
		return
	}
	if _, ok := al[message.Alias]; ok {
		serverlog.General(conn.Identification(), "requested existing alias:", message.Alias)
		denied := messages.NewAliasDeniedMessage("That alias is taken")
		conn.Write(denied.Bytes())
		return
	}
	if len(message.Alias) < 3 || len(message.Alias) > 10 {
		serverlog.General(conn.Identification(), "requested too long or too short alias:", message.Alias)
		denied := messages.NewAliasDeniedMessage("An alias needs to be between 3 and 10 characters long (inclusive)")
		conn.Write(denied.Bytes())
		return
	}

	serverlog.General("Successful registration under alias:", message.Alias)
	conn.Alias = message.Alias
	conn.Registered = true

	serverlog.General("Adding:", message.Alias, "to takenAliases map")
	al[message.Alias] = true

	approved := messages.NewAliasApprovedMessage()
	conn.Write(approved.Bytes())
}
// CreateGame handles the case where a player sends a CreateGame message
func CreateGame(conn *connection.Conn, allGames map[string]*games.Game, message messages.CreateGameMessage) {
	serverlog.General("Received CreateGame message from", conn.Identification())

	if !conn.Registered {
		serverlog.General("Unregistered", conn.Identification(), "called createGame")
		denied := messages.NewCreateGameDeniedMessage(message.GameName, "You are not registered")
		conn.Write(denied.Bytes())
		return
	}

	if conn.InGame {
		serverlog.General(conn.Identification(), "tried to create a new game but is already in game:", allGames[conn.GameID].Name)
		denied := messages.NewCreateGameDeniedMessage(message.GameName, "You are already in a game")
		conn.Write(denied.Bytes())
		return
	}

	serverlog.General("Creating game:", message.GameName, "by", conn.Identification())
	game := games.NewGame(conn, message.GameName)
	serverlog.General(conn.Identification(), "setting InGame to true and Game to the game:", game.Name)
	conn.InGame = true
	conn.GameID = game.ID
	serverlog.General("Attatching", game.Identification(), "to games list")
	allGames[game.ID] = game

	approved := messages.NewCreateGameApprovedMessage(game.ID, game.Name)
	conn.Write(approved.Bytes())
}
// NewGame returns a pointer to a game instance given two connections
func NewGame(initiator *connection.Conn, name string) *Game {
	id := uuid.NewV4().String()
	serverlog.General("Initiating Game:", id, "with", initiator.Identification())
	return &Game{
		ID:        id,
		Name:      name,
		Initiator: initiator,
		InitTime:  time.Now(),
		Ready:     false,
		UDSPath:   path.Join("~", ".pppsrv", "sockets", id+".sock"),
		FinChan:   make(chan bool, 1),
	}
}
// Bytes returns an API friendly binary representation of the game object
// which can be sent to clients.
func (g *Game) Bytes() []byte {
	serverlog.General("Getting byte version of", g.Identification())
	var buf bytes.Buffer
	unixBytes := make([]byte, 4)
	binary.LittleEndian.PutUint32(unixBytes, uint32(g.InitTime.Unix()))
	buf.Write(unixBytes)
	buf.WriteString(g.ID)
	buf.WriteByte(0)
	buf.WriteString(g.Name)
	buf.WriteByte(0)
	buf.WriteString(g.Initiator.Alias)
	buf.WriteByte(0)
	return buf.Bytes()
}
func (g *Game) startUDS() {
	serverlog.General("Initiationg gameMessage channel for", g.Identification())
	g.gameMessage = make(chan []byte, 100)
	go g.listenGameMessage()
	g.deleteSocket()
	g.createSocket()
	defer g.deleteSocket()
	defer g.Kill()

	listener, err := net.Listen("unix", g.UDSPath)
	if err != nil {
		serverlog.Fatal("Failed to establish listener for Unix domain socket:", g.UDSPath)
	}

	cmd := exec.Command(path.Join("~", ".pppsrv", "game"), g.UDSPath, "60")
	err = cmd.Start()
	if err != nil {
		serverlog.Fatal("Failed to start game instance at:", path.Join("~", ".pppsrv", "game"), "err:", err)
	}

	g.UDS, err = listener.Accept()
	if err != nil {
		serverlog.Fatal("Failed to accept connection for unix domain socket:", g.UDSPath)
	}
	serverlog.General("Accepted connection on:", g.UDSPath)
	for {
		buffer := make([]byte, 1400)
		mSize, err := g.UDS.Read(buffer)
		if err != nil {
			serverlog.General("Unix domain socket closed for:", g.Identification(), "so closing gameMessage channel")
			close(g.gameMessage)
			return
		}
		g.gameMessage <- buffer[:mSize]
	}
}
// Close kills the reader and writer and closes the TCP connection
func (conn *Conn) Close() {
	serverlog.General("Close called on", conn.Identification())
	conn.Socket.Close()
}
// Open starts the reader and writer for a connection
func (conn *Conn) Open() {
	serverlog.General("Open called on", conn.Identification())
	go conn.startWriter()
	go conn.startReader()
}
func listenFinished(g *games.Game, allGames map[string]*games.Game) {
	<-g.FinChan
	serverlog.General("Received message through FinChan of", g.Identification(),
		"Removing it from allGames")
	delete(allGames, g.ID)
}
// Start will start the game
func (g *Game) Start(player2 *connection.Conn) {
	serverlog.General("Starting", g.Identification(), "with player 2", player2.Identification())
	g.Player2 = player2
	g.Ready = true
	g.startUDS()
}