const Phaser = require('phaser');

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

const CRYP_KEY_MAP = ['keydown_ONE', 'keydown_TWO', 'keydown_THREE'];
const SKILL_KEY_MAP = ['keydown_Q', 'keydown_W', 'keydown_E', 'keydown_R'];
const TARGET_KEY_MAP = ['keydown_SEVEN', 'keydown_EIGHT', 'keydown_NINE', 'keydown_ZERO'];

const CRYP_MARGIN = COMBAT.crypMargin();
const TEXT_MARGIN = COMBAT.textMargin();
const SKILL_WIDTH = COMBAT.width() / 10;
const SKILL_HEIGHT = COMBAT.height() / 30;

const skillPosition = (crypIter, skillIter) => {
    const skillTextX = COMBAT.width() / 3.8;
    const skillTextY = (TEXT_MARGIN * skillIter) * 1.5 + CRYP_MARGIN * crypIter + COMBAT.y() + COMBAT.height() * 0.07;
    return [skillTextX, skillTextY];
};

const skillCheckHitBox = (scenePlugin, pointer) => {
    const { list } = scenePlugin.get('CombatHitBox').children;
    for (let i = 0; i < list.length; i += 1) {
        if (Phaser.Geom.Rectangle.ContainsPoint(list[i].getBounds(),
            pointer.position)) return list[i];
    }
    // If we didn't find a hitbox deselect them all
    for (let i = 0; i < list.length; i += 1) list[i].deselect();
    return false;
};

class CrypSkill extends Phaser.GameObjects.Container {
    constructor(scene, x, y, skill, cryp) {
        // Avatar will be a property of cryp
        super(scene, x, y);
        const CD_TEXT = skill.cd ? `(${skill.cd}T)` : '';
        const SKILL_TEXT = `${skill.skill} ${CD_TEXT}`;
        this.origX = x; this.origY = y;

        this.skillBox = scene.add.rectangle(0, 0, SKILL_WIDTH, SKILL_HEIGHT, 0x222222);
        this.skillText = scene.add.text(0, 0, SKILL_TEXT, TEXT.NORMAL).setOrigin(0.5, 0.5);
        this.add(this.skillBox);
        this.add(this.skillText);

        this.state = 'deselect';
        this.cryp = cryp;
        this.skill = skill;
        this.scene = scene;

        this.setSize(SKILL_WIDTH, SKILL_HEIGHT);
        this.setInteractive();
    }

    clickHandler() {
        if (this.scene.phase === 'Skill') this.scene.activeSkill = this;
        this.select();
    }

    select() {
        this.scene.children.list.forEach((skill) => {
            if (skill.state === 'select') skill.deselect();
        });
        this.skillBox.setFillStyle(0x004bfe);
        this.state = 'select';
    }

    activate() {
        this.scene.children.list.forEach((skill) => {
            if (skill.state === 'select') skill.deselect();
        });
        this.skillBox.setFillStyle(0xff0000);
        this.state = 'activate';
    }

    deselect() {
        this.skillBox.setFillStyle(0x222222);
        this.state = 'deselect';
    }
}

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

    create(phase) {
        this.phase = phase;
        this.registry.events.off('changedata', this.updateData);
        this.registry.events.on('changedata', this.updateData, this);
        this.account = this.registry.get('account');

        this.input.on('dragstart', (pointer, box) => {
            box.clickHandler();
        });
        this.input.on('drag', (pointer, box, dragX, dragY) => {
            const hitBox = skillCheckHitBox(this.scene, pointer);
            if (hitBox) hitBox.select();
            box.setPosition(dragX, dragY);
        });
        this.input.on('dragend', (pointer, box) => {
            box.deselect();
            const hitBox = skillCheckHitBox(this.scene, pointer);
            if (hitBox) {
                hitBox.clickHandler();
            }
            box.setPosition(box.origX, box.origY);
        });

        if (phase === 'animating') return true;
        // can't set this.game cause of phaser class named the same
        const game = this.registry.get('game');
        this.renderSkills(game, phase);

        return true;
    }

    updateData(parent, key, data) {
        if (key === 'gamePhase' && data) {
            const shouldUpdate = data !== this.phase;
            if (shouldUpdate) {
                this.scene.get('CombatCryps').selectCryp(null);
                return this.scene.restart(data);
            }
            return false;
        }
        return true;
    }

    renderSkills(game, phase) {
        if (phase === 'Skill') return this.renderSkillPhase(game);
        return false;
    }

    renderSkillPhase(game) {
        const { account } = this;
        const { keyboard } = this.input;
        const { events } = this.game;

        const addSkill = (i, j, skill, cryp) => {
            const skillTextPos = skillPosition(i, j);
            const skillObj = new CrypSkill(this, skillTextPos[0], skillTextPos[1], skill, cryp);
            if (skill.cd) {
                skillObj.skillBox.setFillStyle(0x9d9ea0);
            } else {
                this.input.setDraggable(skillObj);
            }
            this.add.existing(skillObj);
            return skillObj;
        };

        const team = game.teams.find(t => t.id === account.id);
        const enemyTeam = game.teams.find(t => t.id !== account.id);

        team.cryps.forEach((cryp) => {
            // return early if KOd
            if (cryp.hp.value === 0) return true;

            // find the cryp position
            const { iter } = this.scene.get('CombatCryps').cryps.children.entries.find(c => c.cryp.id === cryp.id);

            // draw the skills
            const skillButtons = cryp.skills.map((skill, j) => addSkill(iter, j, skill, cryp));

            const bindCrypKeys = () => this.mapSkillKeys(skillButtons, game.id, cryp.id, team.id, enemyTeam.id, iter);

            // reset everything
            keyboard.on('keydown_ESC', bindCrypKeys, this);
            events.on('SEND_SKILL', bindCrypKeys, this);
            bindCrypKeys();

            return true;
        });

        return true;
    }

    // FIXME
    // needs to send crypId not team
    mapSkillKeys(skillButtons, gameId, crypId, alliesId, enemyId, i) {
        const { keyboard } = this.input;

        keyboard.removeListener(CRYP_KEY_MAP[i]);

        keyboard.on(CRYP_KEY_MAP[i], () => {
            SKILL_KEY_MAP.forEach(k => keyboard.removeListener(k));

            this.scene.get('CombatCryps').selectCryp(crypId);

            skillButtons.forEach((button, j) => {
                keyboard.on(SKILL_KEY_MAP[j], () => {
                    this.activeSkill = button;
                    button.select();

                    // clear existing keys
                    CRYP_KEY_MAP.forEach(k => keyboard.removeListener(k));
                    TARGET_KEY_MAP.forEach(k => keyboard.removeListener(k));

                    CRYP_KEY_MAP.forEach(k => keyboard.on(k, () => {
                        this.clearCrypActive(crypId);
                        button.activate();
                        this.activeSkill = null;
                        this.game.events.emit('SEND_SKILL', gameId, crypId, alliesId, button.skill.skill);
                    }));

                    TARGET_KEY_MAP.forEach(k => keyboard.on(k, () => {
                        this.clearCrypActive(crypId);
                        button.activate();
                        this.activeSkill = null;
                        this.game.events.emit('SEND_SKILL', gameId, crypId, enemyId, button.skill.skill);
                    }));
                }, this);
            });
        }, this);

        return true;
    }

    clearCrypActive(crypId) {
        this.scene.scene.children.list.forEach((s) => {
            if (s.cryp.id === crypId && s.state === 'activate') s.deselect();
        });
    }

    clearKeys() {
        TARGET_KEY_MAP.forEach(tKey => this.input.keyboard.removeListener(tKey));
        CRYP_KEY_MAP.forEach(tKey => this.input.keyboard.removeListener(tKey));
        SKILL_KEY_MAP.forEach(tKey => this.input.keyboard.removeListener(tKey));
    }

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

module.exports = CombatSkills;
