import { prop } from '@typegoose/typegoose';
import { HERO_CE_RATIO, getAtrrNameById, CE_CONST, HERO_SUB_ATTR_RATIO, ABI_TYPE_MAIN } from '../../consts';
import { reduceCe } from '../../pubUtils/util';

// hero表内属性基础格式
export class CeAttrData {
    @prop({ required: true })
    id: number = 0;
    @prop({ required: true })
    base: number = 0;
    @prop({ required: true })
    ratioUp: number = 0;
    @prop({ required: true })
    fixUp: number = 0;
    @prop({ required: true })
    equipUp: number = 0;

    constructor(id: number) {
        this.id = id;
    }
}

// role表属性格式
export class CeAttrDataRole {
    @prop({ required: true })
    id: number = 0;
    @prop({ required: true })
    ratioUp: number = 0;
    @prop({ required: true })
    fixUp: number = 0;
    
    constructor(id: number) {
        this.id = id;
    }
}

// 主属性
export class MainAttrNumber {
    hp: number = 0;
    atk: number = 0;
    def: number = 0;
    mdef: number = 0;

    constructor(attr: Attribute) {
        if(attr.hp) this.hp = attr.hp;
        if(attr.atk) this.atk = attr.atk;
        if(attr.def) this.def = attr.def;
        if(attr.mdef) this.mdef = attr.mdef;
    }
}

export class Attribute {
    @prop({ required: false })
    hp: number = 0;
    @prop({ required: false })
    atk: number = 0;
    @prop({ required: false })
    def: number = 0;
    @prop({ required: false })
    mdef: number = 0;
    @prop({ required: false })
    speed: number = 0;
    @prop({ required: false })
    hit: number = 0;
    @prop({ required: false })
    cri: number = 0;
    @prop({ required: false })
    flee: number = 0;
    @prop({ required: false })
    antCri: number = 0;
    @prop({ required: false })
    damageIncrease: number = 0;
    @prop({ required: false })
    damageDecrease: number = 0;
    @prop({ required: false })
    defIngnore: number = 0;
    @prop({ required: false })
    bloodSuck: number = 0;
    @prop({ required: false })
    ap: number = 0;
    @prop({ required: false })
    damageCri: number = 0;
    @prop({ required: false })
    ce?: number = 0;

    setByDbData(roleAttrs: CeAttrDataRole[], heroAttrs: CeAttrData[]) {
        for(let { id, fixUp: roleFix, ratioUp: roleRatio } of roleAttrs) {
            let attrName = getAtrrNameById(id);
            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);
                if(attrName in this) this[attrName] = value;
            } else {
                let value = this.calAttrValue(roleFix, roleRatio, 0, 0, 0, 0);
                if(attrName in this) this[attrName] = value;
            }
        }

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

    setByWarJson( attributes: {id: number, val: number}[], ratio: number = 1) {
        for(let {id, val} of attributes) {
            let attrName = getAtrrNameById(id);
            if(ABI_TYPE_MAIN.includes(id)) {
                if(attrName in this) this[attrName] = Math.floor(val * ratio * HERO_CE_RATIO * HERO_CE_RATIO);
            } else {
                if(attrName in this) this[attrName] = 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 getRealSubAttr() {
        return {
            hit: this.hit / HERO_SUB_ATTR_RATIO / HERO_CE_RATIO / HERO_CE_RATIO,
            cri: this.cri / HERO_SUB_ATTR_RATIO / HERO_CE_RATIO / HERO_CE_RATIO,
            flee: this.flee / HERO_SUB_ATTR_RATIO / HERO_CE_RATIO / HERO_CE_RATIO,
            antCri: this.antCri / HERO_SUB_ATTR_RATIO / HERO_CE_RATIO / HERO_CE_RATIO,
            damageIncrease: this.damageIncrease / HERO_SUB_ATTR_RATIO / HERO_CE_RATIO / HERO_CE_RATIO,
            damageDecrease: this.damageDecrease / HERO_SUB_ATTR_RATIO / HERO_CE_RATIO / HERO_CE_RATIO,
            defIngnore: this.defIngnore / HERO_SUB_ATTR_RATIO / HERO_CE_RATIO / HERO_CE_RATIO,
            damageCri: this.damageCri / HERO_SUB_ATTR_RATIO / HERO_CE_RATIO / HERO_CE_RATIO
        }
    }

    private getRealMainAttr() {
        return {
            hp: this.hp / HERO_CE_RATIO / HERO_CE_RATIO,
            atk: this.atk / HERO_CE_RATIO / HERO_CE_RATIO,
            def: this.def / HERO_CE_RATIO / HERO_CE_RATIO,
            mdef: this.mdef / HERO_CE_RATIO / HERO_CE_RATIO
        }
    }

    public getReduceAttributes() {
        let newAttr = new Attribute();
        newAttr.hp = reduceCe(this.hp);
        newAttr.atk = reduceCe(this.atk);
        newAttr.def = reduceCe(this.def);
        newAttr.mdef = reduceCe(this.mdef);
        newAttr.speed = reduceCe(this.speed);
        newAttr.hit = reduceCe(this.hit);
        newAttr.cri = reduceCe(this.cri);
        newAttr.flee = reduceCe(this.flee);
        newAttr.antCri = reduceCe(this.antCri);
        newAttr.damageIncrease = reduceCe(this.damageIncrease);
        newAttr.damageDecrease = reduceCe(this.damageDecrease);
        newAttr.defIngnore = reduceCe(this.defIngnore);
        newAttr.bloodSuck = reduceCe(this.bloodSuck);
        newAttr.ap = reduceCe(this.ap);
        newAttr.damageCri = reduceCe(this.damageCri);
        return newAttr;
    }

    // 战力计算
    public calCe() {
        let { hp, atk, def, mdef } = this.getRealMainAttr();
        let { cri, flee, damageIncrease, damageDecrease, damageCri } = this.getRealSubAttr();

        let putHit = CE_CONST.PUT_HIT / HERO_SUB_ATTR_RATIO;
        let putAntCri = CE_CONST.PUT_ANT_CRI / HERO_SUB_ATTR_RATIO;

        let hitRate = CE_CONST.HIT_RATE_BASE + putHit/2 - flee; // 命中率
        if(hitRate > CE_CONST.HIT_RATE_MAX) hitRate = CE_CONST.HIT_RATE_MAX;
        if(hitRate < CE_CONST.HIT_RATE_MIN) hitRate = CE_CONST.HIT_RATE_MIN;
        let fleeRate = 1 - hitRate; // 格挡率

        let criRate = CE_CONST.CRI_RATE_BASE + cri - putAntCri/2;
        if(criRate > CE_CONST.CRI_RATE_MAX) criRate = CE_CONST.CRI_RATE_MAX;
        if(criRate < CE_CONST.CRI_RATE_MIN) criRate = CE_CONST.CRI_RATE_MIN;

        let criValue = CE_CONST.CRI_VALUE_BASE + damageCri;

        let validHp = hp + (def + mdef) * 0.5 / ((1 - fleeRate * CE_CONST.FLEE_VALUE) * (1 - damageDecrease)); // 有效生命
        let validAtk = atk * hitRate * ( 1 + criRate * criValue) * ( 1 + damageIncrease); // 有效输出
        let ce = Math.floor(validHp * validAtk * HERO_CE_RATIO * HERO_CE_RATIO / 1000);
        return ce > 0? ce: 1;
    }

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