import Foundation

final class FinishedTable: AbstractTable<FinishedPlayer> {

    let game: GameType

    let totalNumberOfDoubles: Int

    let leadingTrumps: Int

    /// Required for the last cards on the table
    let indexOfTrickStarter: Int

    var cost: Int {
        guard !isBettel else {
            return game.basicCost * 2^^totalNumberOfDoubles
        }
        var cost = game.basicCost
        if isSchwarz {
            cost += 10
        } else if isSchneider {
            cost += 5
        }
        cost += 5 * leadingTrumps
        guard game != .hochzeit else {
            // Wedding is doubled
            return cost * 2^^(totalNumberOfDoubles + 1)
        }
        return cost * 2^^totalNumberOfDoubles
    }

    var gameSelector: FinishedPlayer {
        players.first { $0.selectedGame }!
    }

    let selectorDidWin: Bool

    var coPlayers: [FinishedPlayer] {
        let selector = gameSelector
        return players.filter { $0 != selector && $0.leadsGame == selector.leadsGame }
    }

    var winners: [FinishedPlayer] {
        let selectorLeads = gameSelector.leadsGame
        return players.filter { $0.leadsGame == (selectorDidWin == selectorLeads) }
    }

    var selectorTeamPoints: Int {
        gameSelector.points + coPlayers.map { $0.points }.reduce(0, +)
    }

    var isBettel: Bool {
        game == .bettel
    }

    var isHochzeit: Bool {
        game == .hochzeit
    }

    var isSchwarz: Bool {
        !isBettel && (selectorTeamPoints == 0 || selectorTeamPoints == 120)
    }

    var isSchneider: Bool {
        guard !isBettel else {
            return false
        }
        let points = selectorTeamPoints
        let leads = gameSelector.leadsGame
        let limit = leads ? 31 : 30
        return points < limit || 120 - points < limit
    }

    override var playedGame: GameType? {
        game
    }

    init(table: PlayingTable)  {
        let players = table.players.map(FinishedPlayer.init)
        let selector = table.players.first { $0.selectsGame }!
        self.game = table.game
        self.totalNumberOfDoubles = table.totalNumberOfDoubles
        self.indexOfTrickStarter = table.indexOfTrickStarter
        defer {
            for player in winners {
                player.isNextActor = true
            }
        }

        guard table.game != .bettel else {
            self.selectorDidWin = selector.wonTricks.isEmpty
            self.leadingTrumps = 0
            super.init(table: table, players: players)
            return
        }
        let selectorLeads = selector.leadsGame
        let teamPoints = players
            .filter { $0.leadsGame == selectorLeads }
            .map { $0.points }
            .reduce(0, +)
        self.selectorDidWin = teamPoints > (selectorLeads ? 60 : 59)
        self.leadingTrumps = table.leadingTrumps
        super.init(table: table, players: players)
    }

    /**
     Perform a deal action on the finished table.

     - Parameter action: The action to perform
     - Parameter player: The name of the player
     */
    override func perform(action: PlayerAction, forPlayer name: PlayerName) -> (result: PlayerActionResult, table: ManageableTable?) {
        // Only dealing is allowed...
        guard action == .deal else {
            return (.tableStateInvalid, nil)
        }
        guard let player = players.player(named: name) else {
            print("Unexpectedly missing player \(name) for deal action at finished table \(self.name)")
            return (.tableStateInvalid, nil)
        }

        guard player.canPerform(.deal) else {
            print("Finished table: Player \(name) can't perform deal")
            return (.tableStateInvalid, nil)
        }
        let waiting = WaitingTable(oldTableAdvancedByOne: self)
        let table = DealingTable(table: waiting)
        return (.success, table)
    }

    override func tableInfo(forPlayerAt index: Int) -> TableInfo {
        var info = super.tableInfo(forPlayerAt: index)
        info.summary = GameSummary(table: self, language: language)
        return info
    }

    override func cardStackPosition(ofPlayerAt index: Int) -> Int {
        (4 + index - indexOfTrickStarter) % 4
    }
}
