import Foundation
import WebSocketKit


let numberOfCardsPerPlayer = 8

class AbstractTable<TablePlayer> where TablePlayer: Player {

    /// The unique id of the table
    let id: TableId

    /// The name of the table
    let name: TableName

    /// Indicates that the table is visible to all players, and can be joined by anyone
    let isPublic: Bool

    let language: SupportedLanguage = .german

    /**
     The players sitting at the table.

     The players are ordered clockwise around the table, with the first player starting the game.
     */
    var players: [TablePlayer]

    var playedGame: GameType? {
        nil
    }

    init(table: ManageableTable, players: [TablePlayer]) {
        self.id = table.id
        self.name = table.name
        self.isPublic = table.isPublic
        self.players = players
    }

    init(id: TableId, name: TableName, isPublic: Bool, players: [TablePlayer]) {
        self.id = id
        self.name = name
        self.isPublic = isPublic
        self.players = players
    }

    func perform(action: PlayerAction, forPlayer name: PlayerName) -> (result: PlayerActionResult, table: ManageableTable?) {
        (.tableStateInvalid, nil)
    }

    func play(card: Card, player name: PlayerName) -> (result: PlayerActionResult, table: ManageableTable?) {
        (.tableStateInvalid, nil)
    }

    func cardStackPosition(ofPlayerAt index: Int) -> Int {
        index
    }

    func cards(forPlayerAt index: Int) -> [PlayableCard] {
        []
    }

    func games(forPlayerAt index: Int) -> [GameConvertible] {
        []
    }

    func gameIsSelected(byPlayerAt index: Int) -> Bool {
        false
    }

    func tableInfo(forPlayerAt index: Int) -> TableInfo {
        var info = TableInfo(id: id, name: name)
        info.player = playerInfo(forIndex: index)!
        info.playerLeft = playerInfo(forIndex: (index + 1) % 4)
        info.playerAcross = playerInfo(forIndex: (index + 2) % 4)
        info.playerRight = playerInfo(forIndex: (index + 3) % 4)
        info.playableGames = games(forPlayerAt: index).map { $0.id }
        info.actions = players[index].actions.map { $0.id }
        info.cards = cards(forPlayerAt: index).map { $0.cardInfo }
        info.playerSelectsGame = gameIsSelected(byPlayerAt: index)
        info.game = playedGame?.id
        return info
    }

}

extension AbstractTable: ManageableTable {

    var publicInfo: PublicTableInfo {
        .init(id: id, name: name, players: playerNames)
    }

    var playerNames: [PlayerName] {
        players.map { $0.name }
    }

    var allPlayers: [Player] {
        players
    }



    // MARK: Connection

    func connect(player name: PlayerName, using socket: WebSocket) -> Bool {
        guard let player = players.player(named: name) else {
            return false
        }
        player.connect(using: socket)
        sendUpdateToAllPlayers()
        return true
    }

    func disconnect(player name: PlayerName) {
        guard let player = players.player(named: name) else {
            return
        }
        guard player.disconnect() else {
            return
        }
        sendUpdateToAllPlayers()
        return
    }

    func sendUpdateToAllPlayers() {
        players.enumerated().forEach { playerIndex, player in
            guard player.isConnected else {
                return
            }
            let info = self.tableInfo(forPlayerAt: playerIndex)
            player.send(info)
        }
    }

    // MARK: Client info

    func playerInfo(forIndex index: Int) -> PlayerInfo? {
        guard let player = players.at(index) else {
            return nil
        }
        let height = cardStackPosition(ofPlayerAt: index)
        var info = player.info
        info.positionInTrick = height
        return info
    }

    func tableInfo(forPlayer player: PlayerName) -> TableInfo {
        let index = players.index(of: player)
        return tableInfo(forPlayerAt: index)
    }

}

