import { HERO_CE_RATIO, getAtrrNameById, ABI_TYPE_MAIN } from '../../consts';
import { CeAttrDataRole } from '../../db/Role';
import { CeAttrData } from '../../db/Hero';
import { gameData } from '../../pubUtils/data';
import { decodeArrayListStr, reduceCe } from '../../pubUtils/util';
import { ATTRIBUTE } from '../../pubUtils/dicParam';

export class AttributeCal {
    attrs: Map<number, number> = new Map<number, number>();
    ce?: number = 0;
    lv: number = 1;

    public setLv(lv: number) {
        this.lv = lv;
    }

    public setByDbData(roleAttrs: CeAttrDataRole[], heroAttrs: CeAttrData[]) {
        for(let { id, fixUp: roleFix, ratioUp: roleRatio } of roleAttrs) {
            let heroAttr = heroAttrs.find(heroAttr => heroAttr.id == id);
            if(heroAttr) {
                let { fixUp: heroFix, base: heroBase, ratioUp: heroRatio, equipUp: heroEquip } = heroAttr;
                let value = this.calAttrValue(roleFix, roleRatio, heroBase, heroFix, heroRatio, heroEquip);
                this.attrs.set(id, value);
            } else {
                let value = this.calAttrValue(roleFix, roleRatio, 0, 0, 0, 0);
                this.attrs.set(id, value);
            }
        }

        for(let { id, base: heroBase, fixUp: heroFix, ratioUp: heroRatio, equipUp: heroEquip } of heroAttrs) {
            let roleAttr = roleAttrs.find(roleAttr => roleAttr.id == id);
            if(!roleAttr) {
                let value = this.calAttrValue(0, 0, heroBase, heroFix, heroRatio, heroEquip);
                this.attrs.set(id, value);
            }
        }
    }

    public setByWarJson(attributes: {id: number, val: number}[], ratio: number = 1) {
        for(let {id, val} of attributes) {
            if(ABI_TYPE_MAIN.includes(id)) {
                this.attrs.set(id, Math.floor(val * ratio * HERO_CE_RATIO * HERO_CE_RATIO));
            } else {
                this.attrs.set(id, Math.floor(val * HERO_CE_RATIO * HERO_CE_RATIO));
            }
        }
    }

    public setByMap( attributes: Map<number, number>, ratio: number = 1 ) {
        for(let [id, val] of attributes) {
            if(ABI_TYPE_MAIN.includes(id)) {
                this.attrs.set(id, Math.floor(val * ratio * HERO_CE_RATIO * HERO_CE_RATIO));
            } else {
                this.attrs.set(id, Math.floor(val * HERO_CE_RATIO * HERO_CE_RATIO));
            }
        }
    }

    private calAttrValue(roleFix: number = 0, roleRatio: number = 0, heroBase: number = 0, heroFix: number = 0, heroRatio: number = 0, heroEquip: number = 0) {
        return (heroFix + heroEquip + roleFix) * HERO_CE_RATIO + heroBase * ( HERO_CE_RATIO + heroRatio + roleRatio );
    }

    // private getRealAttrToMap() {
    //     let newMap = new Map<number, number>();
    //     for(let [id, val] of this.attrs) {
    //         if(ABI_TYPE_MAIN.includes(id)) { // 主属性
    //             newMap.set(id, val / HERO_CE_RATIO / HERO_CE_RATIO);
    //         } else {
    //             newMap.set(id, val / HERO_SUB_ATTR_RATIO / HERO_CE_RATIO / HERO_CE_RATIO);
    //         }
    //     }
    //     return newMap;
    // }

    private getReduceAttrMap() {
        let newMap = new Map<number, number>();
        for(let [id, val] of this.attrs) {
            newMap.set(id, reduceCe(val));
        }
        return newMap
    }

    public getReduceAttributes() {
        let attrs = new Attribute();
        attrs.setByMap(this.getReduceAttrMap());
        return attrs;
    }

    public getReduceAttributesToArr() {
        let arr: {id: number, val: number}[] = [];
        for(let [id, val] of this.getReduceAttrMap()) {
            arr.push({ id, val });
        }
        return arr;
    }

    public getReduceAttributesToString() {
        let arr = this.getReduceAttributesToArr();
        return arr.map(cur => `${cur.id}&${cur.val}`).join('|');
    }

    public getReduceMainAttributes() {
        let attribute = this.getReduceAttributes();
        return attribute.getMainAttr();
    }

    // 战力计算
    // calType 1-全部 2-只算主属性 3-只算次属性
    public calCe(calType: number = 1) {
        let ce = gameData.ceRatio.reduce((pre, cur) => {
            let { type, val } = cur;
            if(ABI_TYPE_MAIN.includes(type)) {
                if(calType == 3) {
                    return 0
                } else {
                    return pre + val * (this.attrs.get(type)|| 0)
                }
            } else {
                if(calType == 2) {
                    return 0
                } else {
                    return pre + val * (this.attrs.get(type)|| 0) * this.lv / ATTRIBUTE.ATTRIBUTE_POWER_DIVISOR;
                }
            }
        }, 0);
        return Math.floor(ce);
    }

    public calSubAttrCeAndReduce() {
        return Math.floor(this.calCe(3) / HERO_CE_RATIO / HERO_CE_RATIO);
    }

    public calCelAndReduce() {
        return Math.floor(this.calCe() / HERO_CE_RATIO / HERO_CE_RATIO);
    }
}

export class Attribute {
    hp: number = 0;
    atk: number = 0;
    def: number = 0;
    mdef: number = 0;
    speed: number = 0;
    hit: number = 0;
    cri: number = 0;
    flee: number = 0;
    antCri: number = 0;
    damageIncrease: number = 0;
    damageDecrease: number = 0;
    defIngnore: number = 0;
    bloodSuck: number = 0;
    ap: number = 0;
    damageCri: number = 0;
    physicaldamageInc: number = 0;
    magicdamageInc: number = 0;
    physicaldamageDec: number = 0;
    magicdamageDec: number = 0;
    treatmentInc: number = 0;
    treatmentDec: number = 0;
    acceptTreatmentInc: number = 0;
    acceptTreatmentDec: number = 0;
    bloodRebound: number = 0;
    strikeBack: number = 0;

    setByMap( attributes: Map<number, number> ) {
        for(let [id, val] of attributes) {
            let attrName = getAtrrNameById(id);
            if(attrName in this) this[attrName] = val;
        }
    }

    setByStr(str: string) { // id&val|...
        let arr = decodeArrayListStr(str);
        for(let [id, val] of arr) {
            let attrName = getAtrrNameById(parseInt(id));
            if(attrName in this) this[attrName] = parseInt(val);
        }
    }

    getMainAttr() {
        return {
            hp: this.hp,
            atk: this.atk,
            def: this.def,
            mdef: this.mdef
        }
    }
}