"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Simulator = void 0;
const reference_data_1 = require("@firestone-hs/reference-data");
const debug_state_1 = require("../debug-state");
const utils_1 = require("../utils");
const attack_1 = require("./attack");
const auras_1 = require("./auras");
const start_of_combat_1 = require("./start-of-combat/start-of-combat");
const summon_when_space_1 = require("./summon-when-space");
class Simulator {
    constructor(gameState) {
        this.gameState = gameState;
        this.currentSpeedAttacker = -1;
        this.hasShowShortCircuitWarning = false;
    }
    simulateSingleBattle(playerState, opponentState) {
        var _a, _b, _c, _d, _e, _f, _g, _h;
        let playerBoard = playerState.board;
        let playerEntity = playerState.player;
        let opponentBoard = opponentState.board;
        let opponentEntity = opponentState.player;
        while (!playerEntity.startOfCombatDone ||
            !opponentEntity.startOfCombatDone ||
            ((playerBoard === null || playerBoard === void 0 ? void 0 : playerBoard.length) > 0 && (opponentBoard === null || opponentBoard === void 0 ? void 0 : opponentBoard.length) > 0)) {
            this.simulateSingleBattlePass(playerBoard, playerEntity, opponentBoard, opponentEntity);
            if (this.hasShowShortCircuitWarning) {
                break;
            }
            const areBothBoards0Attack = playerState.board.length > 0 &&
                playerState.board.every((entity) => entity.attack === 0) &&
                opponentState.board.length > 0 &&
                opponentState.board.every((entity) => entity.attack === 0);
            if (areBothBoards0Attack) {
                playerState.board = [];
                opponentState.board = [];
            }
            const isPlayerDefeated = playerState.board.length === 0 || playerState.player.hpLeft <= 0;
            const isOpponentDefeated = opponentState.board.length === 0 || opponentState.player.hpLeft <= 0;
            if (isPlayerDefeated) {
                playerBoard = (_a = playerState.teammate) === null || _a === void 0 ? void 0 : _a.board;
                playerState.board = [];
                playerEntity = (_b = playerState.teammate) === null || _b === void 0 ? void 0 : _b.player;
            }
            else {
                playerBoard = playerState.board;
                playerEntity = playerState.player;
            }
            if (isOpponentDefeated) {
                opponentBoard = (_c = opponentState.teammate) === null || _c === void 0 ? void 0 : _c.board;
                opponentState.board = [];
                opponentEntity = (_d = opponentState.teammate) === null || _d === void 0 ? void 0 : _d.player;
            }
            else {
                opponentBoard = opponentState.board;
                opponentEntity = opponentState.player;
            }
            if (isPlayerDefeated) {
                this.gameState.sharedState.deaths = this.gameState.sharedState.deaths.filter((e) => e.friendly !== this.gameState.gameState.player.player.friendly);
                this.gameState.gameState.player.teammate = {
                    board: this.gameState.gameState.player.board,
                    player: this.gameState.gameState.player.player,
                };
                this.gameState.gameState.player.player = playerEntity;
                this.gameState.gameState.player.board = playerBoard;
            }
            if (isOpponentDefeated) {
                this.gameState.sharedState.deaths = this.gameState.sharedState.deaths.filter((e) => e.friendly !== this.gameState.gameState.opponent.player.friendly);
                this.gameState.gameState.opponent.teammate = {
                    board: this.gameState.gameState.opponent.board,
                    player: this.gameState.gameState.opponent.player,
                };
                this.gameState.gameState.opponent.player = opponentEntity;
                this.gameState.gameState.opponent.board = opponentBoard;
            }
            if (!playerEntity || !opponentEntity) {
                break;
            }
        }
        if ((!(playerBoard === null || playerBoard === void 0 ? void 0 : playerBoard.length) && !(opponentBoard === null || opponentBoard === void 0 ? void 0 : opponentBoard.length)) ||
            ((playerBoard === null || playerBoard === void 0 ? void 0 : playerBoard.length) > 0 && (opponentBoard === null || opponentBoard === void 0 ? void 0 : opponentBoard.length) > 0)) {
            return {
                result: 'tied',
            };
        }
        if (!(playerBoard === null || playerBoard === void 0 ? void 0 : playerBoard.length) || playerEntity.hpLeft <= 0) {
            const damage = this.buildBoardTotalDamage(opponentBoard, (_f = (_e = this.gameState.gameState.opponent) === null || _e === void 0 ? void 0 : _e.teammate) === null || _f === void 0 ? void 0 : _f.board) +
                opponentEntity.tavernTier;
            this.gameState.spectator.registerOpponentAttack(playerBoard, opponentBoard, damage);
            return {
                result: 'lost',
                damageDealt: damage,
            };
        }
        const damage = this.buildBoardTotalDamage(playerBoard, (_h = (_g = this.gameState.gameState.player) === null || _g === void 0 ? void 0 : _g.teammate) === null || _h === void 0 ? void 0 : _h.board) +
            playerEntity.tavernTier;
        this.gameState.spectator.registerPlayerAttack(playerBoard, opponentBoard, damage);
        return {
            result: 'won',
            damageDealt: damage,
        };
    }
    simulateSingleBattlePass(playerBoard, playerEntity, opponentBoard, opponentEntity) {
        var _a, _b, _c, _d, _e;
        if (debug_state_1.debugState === null || debug_state_1.debugState === void 0 ? void 0 : debug_state_1.debugState.active) {
            debug_state_1.debugState.onBattleStart();
        }
        this.gameState.spectator.registerStartOfCombat(playerBoard, opponentBoard, playerEntity, opponentEntity);
        const effectivePlayerBoardLength = playerBoard.length;
        const effectiveOpponentBoardLength = opponentBoard.length;
        this.currentAttacker =
            effectivePlayerBoardLength > effectiveOpponentBoardLength
                ? 0
                : effectiveOpponentBoardLength > effectivePlayerBoardLength
                    ? 1
                    : Math.round(Math.random());
        this.gameState.sharedState.currentEntityId =
            Math.max(...playerBoard.map((entity) => entity.entityId), ...opponentBoard.map((entity) => entity.entityId), ...((_b = (_a = playerEntity.hand) === null || _a === void 0 ? void 0 : _a.map((entity) => entity.entityId)) !== null && _b !== void 0 ? _b : []), ...((_d = (_c = opponentEntity.hand) === null || _c === void 0 ? void 0 : _c.map((entity) => entity.entityId)) !== null && _d !== void 0 ? _d : [])) + 1;
        const suggestedNewCurrentAttacker = (0, start_of_combat_1.handleStartOfCombat)(playerEntity, playerBoard, opponentEntity, opponentBoard, this.currentAttacker, this.gameState);
        this.currentAttacker = suggestedNewCurrentAttacker;
        let counter = 0;
        if (debug_state_1.debugState === null || debug_state_1.debugState === void 0 ? void 0 : debug_state_1.debugState.active) {
            this.currentAttacker = (_e = debug_state_1.debugState.forcedCurrentAttacker) !== null && _e !== void 0 ? _e : this.currentAttacker;
        }
        while (playerBoard.length > 0 &&
            opponentBoard.length > 0 &&
            playerEntity.hpLeft > 0 &&
            opponentEntity.hpLeft > 0) {
            (0, summon_when_space_1.handleSummonsWhenSpace)(playerBoard, playerEntity, opponentBoard, opponentEntity, this.gameState);
            (0, auras_1.clearStealthIfNeeded)(playerBoard, playerEntity, opponentBoard, opponentEntity, this.gameState);
            if (playerBoard.some((entity) => entity.attackImmediately)) {
                this.currentSpeedAttacker = 0;
            }
            else if (opponentBoard.some((entity) => entity.attackImmediately)) {
                this.currentSpeedAttacker = 1;
            }
            else {
                this.currentSpeedAttacker = -1;
            }
            if (playerBoard.filter((e) => e.attack > 0).length === 0 &&
                opponentBoard.filter((e) => e.attack > 0).length === 0) {
                break;
            }
            if (this.currentSpeedAttacker === 0 || (this.currentSpeedAttacker === -1 && this.currentAttacker === 0)) {
                (0, attack_1.simulateAttack)(playerBoard, playerEntity, opponentBoard, opponentEntity, this.gameState);
            }
            else {
                (0, attack_1.simulateAttack)(opponentBoard, opponentEntity, playerBoard, playerEntity, this.gameState);
            }
            if (playerBoard.some((entity) => entity.attackImmediately)) {
                this.currentSpeedAttacker = 0;
            }
            else if (opponentBoard.some((entity) => entity.attackImmediately)) {
                this.currentSpeedAttacker = 1;
            }
            else {
                this.currentSpeedAttacker = -1;
                this.currentAttacker = (this.currentAttacker + 1) % 2;
            }
            counter++;
            if (counter > 400 && !this.hasShowShortCircuitWarning) {
                console.warn('short-circuiting simulation, too many iterations', counter, '\n', (0, utils_1.stringifySimple)(playerBoard, this.gameState.allCards), '\n', (0, utils_1.stringifySimple)(opponentBoard, this.gameState.allCards));
                this.hasShowShortCircuitWarning = true;
                break;
            }
        }
    }
    buildBoardTotalDamage(playerBoard, teammateBoard) {
        var _a;
        const damageFromPlayerBoard = playerBoard
            .map((entity) => (0, reference_data_1.getEffectiveTechLevel)(this.gameState.allCards.getCard(entity.cardId), this.gameState.allCards))
            .reduce((a, b) => a + b, 0);
        const numberOfTeamateMinionsToSummnon = 7 - playerBoard.length;
        const damageFromTeammateBoard = (_a = teammateBoard === null || teammateBoard === void 0 ? void 0 : teammateBoard.slice(0, numberOfTeamateMinionsToSummnon).map((entity) => (0, reference_data_1.getEffectiveTechLevel)(this.gameState.allCards.getCard(entity.cardId), this.gameState.allCards)).reduce((a, b) => a + b, 0)) !== null && _a !== void 0 ? _a : 0;
        return damageFromPlayerBoard + damageFromTeammateBoard;
    }
}
exports.Simulator = Simulator;
//# sourceMappingURL=simulator.js.map