
import { Observable, Subject, Subscription } from "rxjs"
import { first } from "rxjs/operators"
import { MD2Service } from "../../services/MD2/md2.service"
import { StringUtils } from "../../utilities/string-utils"
import { BossActivationComponent } from "./boss-fight/boss-activation/boss-activation.component"
import { TreasureType, AttackInfo, AttackType, MD2Icon, MD2HeroInfo, AttackTarget, MobInfo, MobType } from "./massive-darkness2.model"
import { RollingBlackDice } from "./massive-darkness2.model.dice"
import { MD2DiceSet, MD2MobSkill } from "./massive-darkness2.db.model"


export enum MobSkillType {
    Attack,
    Defense,
    Combat,
    Passive,
    ConditionalSkill,
    OtherWiseSkill,
    ActiveSkill,
    MeleeAttack = 15,
    RangeAttack,
    MagicAttack,
}
export class MobSkill {
    constructor(config: Partial<MobSkill> = {}) {
        let defaultConfig = {
            type: MobSkillType.Combat,
            skillRoll: 1
        } as Partial<MobSkill>;
        Object.assign(defaultConfig, config);
        Object.assign(this, defaultConfig);
        // if (StringUtils.isNullOrWhitespace(this.name)) {
        //     this.name=`${MobSkillType[this.type]} ${this.skillRoll} ${}`
        // }
    }
    type: MobSkillType;
    clawRoll: number;
    skillRoll: number;
    name: string
    description: string
    targetHeros: MD2HeroInfo[]
}
export interface IBossFight {
    name: string
    addTreasureToken: Subject<TreasureType>
    spawnMob: Subject<void>
    spawnRoamingMonster: Subject<void>
    rounds: number
    actions: number
    activatedTimes: number
    info: MobInfo
    actionBlackDice: number
    imgUrl: string
    standUrl: string
    extraRules: string
    md2Service: MD2Service
    activating(): boolean
    prepareForBossFight(): void
    darknessPhase(): void
}

export abstract class BossFight implements IBossFight {
    name: string
    addTreasureToken: Subject<TreasureType>
    spawnMob: Subject<void>
    spawnRoamingMonster: Subject<void>
    rounds: number
    actions: number
    activatedTimes: number
    info: MobInfo
    actionBlackDice: number
    imgUrl: string
    standUrl: string
    extraRules: string
    protected subscription: Subscription

    constructor(public md2Service: MD2Service) {
        this.rounds = 1;
    }
    activating(): boolean {
        this.activatedTimes = this.actions;
        this.runAction();
        return true;
    }

    runAction() {
        this.bossAction().pipe(first()).subscribe(result => {
            this.activatedTimes--;
            if (this.activatedTimes) {
                this.runAction();
            } else {
                if (false == this.md2Service.heros.some(h => h.remainActions > 0)) {
                    this.md2Service.darknessPhase();
                }
            }
        });
    }
    abstract bossAction(): Observable<boolean>;
    protected actionEnd
    prepareForBossFight(): void {
        throw new Error("Method not implemented.")
    }
    darknessPhase(): void {
        throw new Error("Method not implemented.")
    }

}
export class BossMicheal extends BossFight {
    constructor(public md2Service: MD2Service) {
        super(md2Service);
        this.corruptionTokenHtml = this.md2Service.imgHtml('Tokens/CorruptToken.png');

        this.name = 'Michael - The Corrupted Archangel';
        this.imgUrl = md2Service.imgUrl('/Boss/Michael - The Corrupted Archangel.jpg');
        this.standUrl = md2Service.imgUrl('/Boss/Michael.png');

        this.info = new MobInfo({
            description: this.name,
            type: MobType.Boss,
            hpPerHero: 15,
            level: 10,
            imageUrl: md2Service.imgUrl('/Boss/Michael.png')
        });
        if (!this.info.skills) {
            this.info.skills = [];
        }
        this.info.skills.push({
            name: `Combat 1  ${this.md2Service.iconHtml(MD2Icon.EnemySkill)}`,
            description: `Deal 1 Wound for each ${this.corruptionTokenHtml} on the attacking or defending Hero. Discard the tokens afterwards(once per combat).`,
            type: MobSkillType.Combat,
            skillRoll: 1
        } as MD2MobSkill);
        this.info.defenseInfo = { blue: 5, black: 1 } as MD2DiceSet;
        this.info.attackInfos = [new AttackInfo(MD2Icon.Melee, 2, 2, 0, 1)];
        this.actions = 1;
        this.actionBlackDice = 2;

        this.extraRules = `Archangel Michael can’t be the target of any attack, skill, ability or take Wounds until there are no Corruption tokens in the whole Tile.<br><br>` +
            `Any Hero on a Zone with a ${this.corruptionTokenHtml} may spend 1 action to remove it. Each time a Hero removes a ${this.corruptionTokenHtml} from a Zone they must roll 1 ${this.md2Service.iconHtml(MD2Icon.BlackDice)}.` +
            `If ${this.md2Service.iconHtml(MD2Icon.EnemyClaw)} the Hero takes 1 Wound.<br>If ${this.md2Service.iconHtml(MD2Icon.EnemySkill)} place 1 ${this.corruptionTokenHtml} on their Dashboard.<br>` +
            `If ${this.md2Service.iconHtml(MD2Icon.EnemyClaw)}/${this.md2Service.iconHtml(MD2Icon.EnemySkill)} the Hero takes 1 Wound and places 1 ${this.corruptionTokenHtml} on their Dashboard.`
    }
    activatedTimes: number
    acted: number
    name: string
    addTreasureToken: Subject<TreasureType>
    spawnMob: Subject<void>
    spawnRoamingMonster: Subject<void>
    rounds: number
    actions: number
    info: MobInfo
    actionBlackDice: number
    imgUrl: string
    standUrl: string
    corruptionTokenHtml: string

    bossAction(): Observable<boolean> {

        let actionResult = new RollingBlackDice().roll(this.actionBlackDice);
        let actionHtml = '';
        let beenAttackedHero = [] as MD2HeroInfo[];
        let bossAction: MobSkill;
        switch (actionResult.claws) {
            case 0:
                //Justice From Above
                beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.MostCorruption, true);
                bossAction = new MobSkill(
                    {
                        name: 'Justice From Above',
                        description: `Place Michael in the Zone at ${this.md2Service.getTargetHerosHtml(beenAttackedHero)} and attack Him/Her.`
                    });

                break;
            case 1:
                //Lance Dash
                beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.LeastCorruption, true);
                bossAction = new MobSkill({
                    name: 'Lance Dash',
                    description:
                        `Move Michael and Place 1 ${this.corruptionTokenHtml} in the Zone at ${this.md2Service.getTargetHerosHtml(beenAttackedHero)} and attack Him/Her.`
                });
                break;
            case 2:
                //Dark Blessing
                bossAction = new MobSkill({
                    name: 'Dark Blessing',
                    description:
                        `Place Michael on the central Zone and add 1 ${this.corruptionTokenHtml} to the Corruption Stone Zone with the least amount of ${this.corruptionTokenHtml}.<br>` +
                        `Deal <b>${this.darkBlessingCorruptionAmt}</b> Wounds per ${this.corruptionTokenHtml} to all Heros in each Tiles <b>distributed as they wish</b>.`
                });
                break;

            default:
                break;
        }
        return this.md2Service.dlgService.open(BossActivationComponent, { context: { boss: this, bossAction: bossAction, currentAction: this.activatedTimes, allActions: this.actions } }).onClose;

    }
    prepareForBossFight(): void {
        this.md2Service.heros.forEach(hero => {
            hero.uiShowCorruptionToken = true;
        });
        this.md2Service.msgBoxService.show('Prepare Boss Fight', {
            text: `<h6>Place ${this.md2Service.heros.length * 2} ${this.corruptionTokenHtml} on the Corruption Stone Zones (Shadow 
                Zones).<br>Players choose the Zones, but must distribute 
                the tokens as equally as possible among them.</h6>`
        });
    }
    darkBlessingCorruptionAmt: number = 1;
    darknessPhase(): void {
        this.rounds++;
        switch (this.rounds) {
            case 3:
            case 5:
                this.darkBlessingCorruptionAmt++;
                break;
            case 2:
            case 4:
                this.info.defenseInfo.black += 1;
                this.info.attackInfos[0].black += 1;
                break;
            // case 4:
            //     this.defInfo.black += 2;
            //     this.atkInfos[0].black += 2;
            //     break;

            default:
                break;
        }
    }
}


export class BossReaper extends BossFight {

    constructor(public md2Service: MD2Service) {
        super(md2Service);
        this.timeTokenHtml = this.md2Service.imgHtml('Tokens/TimeToken.png');

        this.name = 'The Reaper';
        this.imgUrl = md2Service.imgUrl('/Boss/The Reaper.jpg');
        this.standUrl = md2Service.imgUrl('/Boss/The Reaper-Stand.png');

        this.info = new MobInfo({
            description: this.name,
            type: MobType.Boss,
            hpPerHero: 25,
            level: 10,
            imageUrl: md2Service.imgUrl('/Boss/The Reaper-Stand.png')
        });
        if (!this.info.skills) {
            this.info.skills = [];
        }
        this.info.skills.push({
            description: `If the Hero has no ${this.md2Service.iconHtml(MD2Icon.Mana_Color)}, they take 1 ${this.md2Service.iconHtml(MD2Icon.FrozenToken)}`,
            type: MobSkillType.Attack,
            skillRoll: 1
        } as MD2MobSkill);
        this.info.defenseInfo = { blue: 4, black: 3 } as MD2DiceSet;
        this.info.attackInfos = [new AttackInfo(MD2Icon.Melee, 1, 2, 0, 3)];
        this.actions = 1;
        this.actionBlackDice = 2;
        this.extraRules = `A Hero standing in the Hourglass Zone may spend 1 action to add 1 ${this.timeTokenHtml} in each Hourglass Zone.`;
    }
    name: string
    addTreasureToken: Subject<TreasureType>
    spawnMob: Subject<void>
    spawnRoamingMonster: Subject<void>
    rounds: number
    actions: number
    info: MobInfo
    actionBlackDice: number
    imgUrl: string
    standUrl: string
    timeTokenHtml: string
    bossAction() {

        let actionResult = new RollingBlackDice().roll(this.actionBlackDice);
        let actionHtml = '';
        let beenAttackedHero = [] as MD2HeroInfo[];
        let bossAction: MobSkill;
        switch (actionResult.claws) {
            case 0:
                //Justice From Above
                beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.LeastMp, true);
                bossAction = new MobSkill(
                    {
                        name: 'Soul Drain',
                        description: `Place The Reaper in the Zone at ${this.md2Service.getTargetHerosHtml(beenAttackedHero)} and attack Him/Her.`
                    });

                break;
            case 1:
                //Lance Dash
                beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.LeastCorruption, true);
                bossAction = new MobSkill({
                    name: 'Time Ticking',
                    description:
                        `Place The Reaper in the <b>Hourglass Zone</b> withe the least ${this.timeTokenHtml} and remove 1 ${this.timeTokenHtml} from <b>The OtherHourglass Zone</b>.`
                });
                break;
            case 2:
                //Dark Blessing
                bossAction = new MobSkill({
                    name: 'Death Is Coming',
                    description:
                        `Place The Reaper in the central Zone.<br>` +
                        `Roll 1 ${this.md2Service.iconHtml(MD2Icon.YellowDice)}. Remove ${this.timeTokenHtml} equal to ${this.md2Service.iconHtml(MD2Icon.Melee)} rolled from both <b>Hourglass Zone</b>.<br>` +
                        `Each Hero discards ${this.md2Service.iconHtml(MD2Icon.MP)} equal to ${this.md2Service.iconHtml(MD2Icon.Melee)} rolled.`
                });
                break;

            default:
                break;
        }
        return this.md2Service.dlgService.open(BossActivationComponent, { context: { boss: this, bossAction: bossAction, currentAction: this.activatedTimes, allActions: this.actions } }).onClose;
    }
    prepareForBossFight(): void {
        this.md2Service.msgBoxService.show('Prepare Boss Fight', {
            text: `<h6>Place 2 ${this.timeTokenHtml} in each Hourglass Zone.</h6>`
        })
    }
    darkBlessingCorruptionAmt: number = 1;
    darknessPhase(): void {
        this.rounds++;
        if (this.rounds > 4) {
            this.actions = 3;
        } else if (this.rounds > 1) {
            this.actions = 2;
        }
    }
}