const PHASE_REINFORCE = 1;
const PHASE_ATTACK = 2;
const PHASE_FORTIFY = 3;

class Player {
    constructor(id, name) {
        this.name = name;
        this.timeout = null;
        this.id = id;
        this.ready = false;

        // Data which is reset every turn
        this.isPlaying = false;
        this.reinforcementsPlaced = 0;
        this.reinforcementsAvailable = 0;
        this.turnPhase = PHASE_REINFORCE;

        this.resetColor();
    }

    resetTimeout() {
        if (this.timeout !== null) {
            window.clearTimeout(this.timeout);
        }

        this.timeout = window.setTimeout(() => {
            if (players[this.id] !== undefined) {
                delete players[this.id];
            }
            updateDom();
        }, TIMEOUT);
    }

    /**
     * Get our color as used on the board.
     */
    getColor() {
        return this.color;
    }

    resetColor() {
        let randomColor = Math.random() * 360;
        this.color = `hsl(${randomColor} 57% 50%)`;
    }

    /**
     * Claim a region of the map.
     *
     * @param data Data received via socket.
     */
    claim(data) {
        let region = Region.getRegion(data.region);

        if (region.owner === null) {
            region.claim(this);
            return true;
        } else {
            return false;
        }
    }

    /**
     * Reinforce a region of the map.
     *
     * @param data Data received via socket.
     */
    reinforce(data) {
        let region = Region.getRegion(data.region);

        if (region.owner === this) {
            region.reinforce(1);
            return true;
        } else {
            return false;
        }
    }

    /**
     * Process a generic action packet representing a player's move.
     *
     * @param data Data received via socket
     * @returns {boolean} Whether this player's turn has ended or not.
     */
    act(data) {
        if (this.turnPhase === PHASE_REINFORCE) {
            if (this.reinforce(data)) {
                this.reinforcementsPlaced += 1;
            }

            if (this.reinforcementsPlaced === this.reinforcementsAvailable) {
                this.turnPhase = PHASE_ATTACK;
            }

            return false;
        } else {
            // End turn prematurely.
            if (data.action === "END") {
                return true;
            }

            if (this.turnPhase === PHASE_ATTACK && data.action === "ATTACK") {
                if (this.attack(data)) {
                    this.turnPhase = PHASE_FORTIFY;
                }

                return false;
            }

            this.turnPhase = PHASE_FORTIFY;

            if (data.action === "FORTIFY") {
                return this.fortify(data);
            }
        }
        return false;
    }

    /**
     * Process an action which is to attack another region.
     *
     * @param data Data received via socket
     */
    attack(data) {}

    /**
     * Process an action which is to attack another region.
     *
     * @param data Data received via socket
     */
    fortify(data) {
        let sender = Region.getRegion(data.sender);
        let receiver = Region.getRegion(data.receiver);

        if (
            sender.owner === this &&
            receiver.owner === this &&
            sender.strength > data.strength
        ) {
            receiver.reinforce(data.strength);
            sender.strength -= data.strength;
            return true;
        } else {
            return false;
        }
    }

    /**
     * Calculate the number of additional reinforcements to gain on this turn.
     */
    additionalReinforcements() {
        return Math.min(
            3,
            Math.floor(
                Object.values(REGIONS).filter((region) => region.owner === this).length /
                    3
            )
        );
    }

    /**
     * Start a player's turn.
     */
    startTurn() {
        this.isPlaying = true;
        this.reinforcementsPlaced = 0;
        this.reinforcementsAvailable = this.additionalReinforcements();
        this.turnPhase = PHASE_REINFORCE;
    }

    /**
     * End player's turn
     */
    endTurn() {
        this.isPlaying = false;
        this.nextPlayer().startTurn();
    }

    nextPlayer() {
        let sorted = Object.values(players).sort((a, b) => (a.id < b.id ? -1 : 1));
        let ourIndex = sorted.findIndex((player) => player.id === this.id);

        return sorted[(ourIndex + 1) % sorted.length];
    }
}
