const Phaser = require('phaser');
const genAvatar = require('./avatar');
const StatBar = require('./elements/combat.statbar');

const { DELAYS, TEXT, POSITIONS: { COMBAT } } = require('./constants');

const CRYP_MARGIN = COMBAT.crypMargin();
const TEXT_MARGIN = COMBAT.textMargin();

const crypAvatarText = (team, iter) => {
    const nameX = COMBAT.width() * team;
    const nameY = COMBAT.y() + CRYP_MARGIN * iter + COMBAT.height() * 0.07;
    const statusX = COMBAT.width() * team;
    const statusY = COMBAT.y() + TEXT_MARGIN * 6 + CRYP_MARGIN * iter + COMBAT.height() * 0.07;
    return { statusX, statusY, nameX, nameY };
};

const crypEffects = (team, iter) => {
    const crypEffectsX = team ? COMBAT.width() - COMBAT.width() / 6.5 : COMBAT.width() / 6.5;
    const crypEffectsY = TEXT_MARGIN * 2 + CRYP_MARGIN * iter;
    return { crypEffectsX, crypEffectsY };
};

const crypPosition = (team, iter) => {
    const crypAvatarX = team ? COMBAT.width() - COMBAT.width() / 6 : COMBAT.width() / 6;
    const crypAvatarY = TEXT_MARGIN * 5 + CRYP_MARGIN * iter;
    return { crypAvatarX, crypAvatarY };
};


class Effects extends Phaser.GameObjects.Group {
    constructor(scene, team, iter) {
        super(scene);
        this.scene = scene;
        const { crypEffectsX, crypEffectsY } = crypEffects(team, iter);
        this.x = crypEffectsX; this.y = crypEffectsY;
        this.effectCount = 0;
    }

    addEffect(effect) {
        const y = this.y + this.effectCount * TEXT_MARGIN;
        const text = `${effect.effect} for ${effect.duration} turn`;
        const e = this.scene.add.text(this.x, y, text, TEXT.NORMAL);
        e.effect = effect.effect;
        this.add(e);
        this.effectCount += 1;
    }

    removeEffect(effect) {
        this.children.entries.forEach((e) => {
            if (e.effect === effect) e.destroy();
        });
    }

    update(effects) {
        this.effectCount = 0;
        this.children.entries.forEach(e => e.destroy());
        effects.forEach((effect) => {
            this.addEffect(effect);
        });
        return true;
    }
}

class CrypImage extends Phaser.GameObjects.Image {
    constructor(scene, team, iter, cryp) {
        // Get coords
        const { crypAvatarX, crypAvatarY } = crypPosition(team, iter);
        const { statusX, statusY, nameX, nameY } = crypAvatarText(team, iter);

        // Cryp display
        // const avatar = team ? 'magmar' : 'alk';
        super(scene, crypAvatarX, crypAvatarY, 'aztec', genAvatar(cryp.name));
        this.setScale(0.5);

        if (!team) this.flipX = true;

        // Save position and cryp details
        this.scene = scene;
        this.iter = iter;
        this.team = team;
        this.cryp = cryp;
        this.state = 'deselect';
        // Add cryp name
        scene.add.text(nameX, nameY, cryp.name, TEXT.NORMAL).setOrigin(team, 0);
        // Add cryp stat bars
        this.health = scene.add.existing(new StatBar(scene, this, 'HP'));
        this.red_shield = scene.add.existing(new StatBar(scene, this, 'Red Shield'));
        this.blue_shield = scene.add.existing(new StatBar(scene, this, 'Blue Shield'));
        // this.evasion = scene.add.existing(new StatBar(scene, this, 'Evasion'));

        this.effects = scene.add.existing(new Effects(scene, team, iter));
        this.statusText = scene.add.text(statusX, statusY, '', TEXT.NORMAL);
    }

    select() {
        this.setTint('0x00bb00');
        this.state = 'select';
    }

    setKo() {
        this.state = 'ko';
        this.setTint('0x9d9ea0');
    }

    deselect() {
        if (this.state !== 'ko') {
            this.clearTint();
            this.state = 'deselect';
        }
    }

    clearStatus() {
        this.statusText.text = '';
    }

    reduceDefense(amount, type) {
        if (type === 'PhysDmg') {
            this.red_shield.takeDamage(amount);
        } else if (type === 'BlueDmg') {
            this.blue_shield.takeDamage(amount);
        }
    }

    takeDamage(props) {
        const { amount, mitigation, category } = props;
        if (mitigation) this.reduceDefense(mitigation, category);
        this.setTint(0xff0000);
        this.health.takeDamage(amount);
        this.scene.time.delayedCall(DELAYS.DAMAGE_TICK, () => {
            if (this.state !== 'ko') this.clearTint();
        });
    }

    takeHealing(amount) {
        this.setTint(0x00bb00);
        this.health.takeDamage(amount * -1);
        this.scene.time.delayedCall(DELAYS.DAMAGE_TICK, () => {
            if (this.state !== 'ko') this.clearTint();
        });
    }
}

class CombatCryps extends Phaser.Scene {
    constructor() {
        super({ key: 'CombatCryps' });
    }

    create(game) {
        this.cryps = this.add.group();
        this.phase = game.phase;
        this.account = this.registry.get('account');
        this.drawCryps(game);
        this.registry.events.on('changedata', this.updateData, this);
        this.registry.set('crypStatusUpdate', false);
        this.teams = game.teams.length;
    }

    updateData(parent, key, data) {
        if (key === 'game' && data) {
            if (data.teams.length !== this.teams) this.scene.restart(data);
            const isAnimating = this.phase === 'animating';
            this.game = data;
            if (isAnimating) return false;
        }

        if (key === 'gamePhase' && data) {
            const shouldUpdate = data !== this.phase;
            this.phase = data;
            if (shouldUpdate) {
                this.cryps.children.entries.forEach(c => c.clearStatus());
                this.drawCryps(this.game);
            }
        }

        if (key === 'crypStatusUpdate' && data) {
            this.updateCrypStatus(data);
        }

        return true;
    }

    drawCryps(game) {
        const renderCryp = (cryp, iter, team) => {
            // Add Image Avatar Class
            const crypObj = new CrypImage(this, team, iter, cryp);
            this.add.existing(crypObj);
            this.cryps.add(crypObj);
            return crypObj;
        };

        const renderTeam = (cryp, iter, team) => {
            const crypObj = this.cryps.children.entries
                .find(c => c.cryp.id === cryp.id)
                || renderCryp(cryp, iter, team);
            crypObj.health.val = cryp.hp.value;
            crypObj.red_shield.val = cryp.red_shield.value;
            crypObj.blue_shield.val = cryp.red_shield.value;

            crypObj.health.drawStatBar();
            crypObj.red_shield.drawStatBar();
            crypObj.blue_shield.drawStatBar();
            crypObj.effects.update(cryp.effects);
        };

        const allyTeam = game.teams.find(t => t.id === this.account.id);
        // in future there will be more than one
        const [enemyTeam] = game.teams.filter(t => t.id !== this.account.id);

        allyTeam.cryps.forEach((cryp, i) => renderTeam(cryp, i, 0));
        if (!enemyTeam) return false;
        enemyTeam.cryps.forEach((cryp, i) => renderTeam(cryp, i, 1));
        return true;
    }

    selectCryp(crypId) {
        this.cryps.children.entries.forEach(c => c.deselect());
        if (crypId) this.cryps.children.entries.find(c => c.cryp.id === crypId).select();
    }

    updateCrypStatus(status) {
        const sourceCryp = this.cryps.children.entries
            .find(c => c.cryp.id === status.id);

        const targetCryp = this.cryps.children.entries
            .find(c => c.cryp.id === status.target);

        if (this.phase === 'Skill') {
            sourceCryp.statusText.text = `${status.skill} on ${targetCryp.cryp.name}`;
        }
    }

    cleanUp() {
        this.registry.events.off('changedata', this.updateData);
        this.scene.remove();
    }
}

module.exports = CombatCryps;
