import { ABI_STAGE, ABI_STAGE_TO_TYPE, ABI_TYPE, ABI_TYPE_MAIN, LINEUP_NUM, SEID_TYPE, TALENT_RELATION_TYPE } from "../../consts";
import { ArtifactModelType } from "../../db/Artifact";
import { Connect, EPlace, HeroSkin, HeroType, HeroUpdate, Stone, Talent } from "../../db/Hero";
import { JewelType } from "../../db/Jewel";
import { RoleUpdate, Teraph } from "../../db/Role";
import { AttrCell, Attribute, EquipAttr, HeroAttr, RoleCeType, SchoolAttr, ScrollAttr } from "../../db/RoleCe";
import { TopHero } from "../../domain/dbGeneral";
import { AttributeCal } from "../../domain/roleField/attribute";
import { gameData, getDicArtifactLvByPlanId, getEquipQualityIdByEquipIdAndPoint, getEquipStarAttrByStage, getEquipStrenthenAttr, getEquipSuitByHero, getFriendShipByIdAndLv, getHeroStarByQuality, getHeroWakeByQuality, getJewelConditionByLvAndSeId, getJobByGradeAndClass, getSchoolRateByStar, getScollByStar, getTeraph } from "../../pubUtils/data";
import { DicRandomEffectPool } from "../../pubUtils/dictionary/DicRandomEffectPool";
import { DicSe } from "../../pubUtils/dictionary/DicSe";
import { addToMap, deepCopy } from "../../pubUtils/util";
export class CalCe {
    roleId: string;
    originCes: Map<number, number> = new Map(); // hid => ce
    resultCes: Map<number, number> = new Map(); // hid => ce
    data: CalCeData;
    attrsByHid: Map<number, { id: number, val: number, ceVal: number, str: string, ceStr: string }[]> = new Map();
    originTopHeroCe: number;

    constructor(roleId: string) {
        this.roleId = roleId;
    }

    public setRoleCe(roleCe: RoleCeType) {
        this.data = new CalCeData(roleCe)
        this.originCes = this.calHeroCe();
        this.originTopHeroCe = this.getTopLineup().topLineupCe;
    }
    
    public clearRoleCe() {
        this.data = new CalCeData(null);
    }

    public getRoleCeTable() {
        let attributes: Attribute[] = [];
        for(let [hid, attrs] of this.attrsByHid) {
            attributes.push({ hid, attrs })
        }
        return this.data.getRoleCeTable(this.roleId, attributes);
    }

    public calHeroCe() {
        let attrs = new Map<number, { id: number, val: number, ceVal: number, str: string, ceStr: string }[]>(); // hid => [{attrId, val}]
        for(let [hid ] of this.data.heroAttrsByHid) {
            let lv = this.data.heroLv.get(hid)||1;
            for(let attrId = ABI_TYPE.ABI_HP; attrId < ABI_TYPE.ABI_MAX; attrId++) {
                if(!this.data.heroAttrs.has(`${hid}_${attrId}`) && !this.data.globalAttrs.has(attrId)) continue;
                let { mainBase = 0, mainBaseUp = 0, subBase = 0, job = 0, starUp = 0, connect = 0, talent = 0, equipQuality = 0, equipStrength = 0, equipStar = 0, equipSuit = 0, jewel = 0, stone = 0, artifactLv = 0, artifactQuality = 0, artifactSeid = 0 } = this.data.heroAttrs.get(`${hid}_${attrId}`)||{};
                let { school = 0, teraph = 0, title = 0, scroll = 0, skin = 0 } = this.data.getGlobalAttrById(attrId)||{};
                let val = 0, ceVal = 0, str = '', ceStr = '';
                if(ABI_TYPE_MAIN.indexOf(attrId) != -1) {
                    //     {[ hp1               + lv * hp2                     ] * ( 1 + hp5           ) + [( hp6          + hp7             ) * ( 1 + hp8                              )]} * ( 1 + hp9                                                               )   + hp10   + hp11                       + hp14
                    val =  (( mainBase + job    + lv * ( starUp + mainBaseUp )  ) * ( 1 + connect/100   ) + (( equipQuality + equipStrength   ) * ( 1 + ( equipStar/100 + equipSuit/100 )))) * ( 1 + jewel/100 + school/100 + talent/100 + skin/100  + artifactSeid/100)  + stone  + teraph + title + scroll    + artifactLv + artifactQuality;
                    ceVal = (( mainBase + job    + lv * ( starUp + mainBaseUp )  ) * ( 1 + connect/100   ) + (( equipQuality + equipStrength   ) * ( 1 + ( equipStar/100 + equipSuit/100 )))) * ( 1 + jewel/100 + school/100 + talent/100 + skin/100  + artifactSeid/100)  + stone  + teraph + title + scroll    + artifactLv + artifactQuality;
                    str += `{[${mainBase}+${job}+${lv}*(${starUp}+${mainBaseUp})]* ( 1 + ${connect}/100) + [(${equipQuality}+${equipStrength}) * ( 1 + ${equipStar}/100+${equipSuit}/100)]} * (1+${jewel}/100+${school}/100+${talent}/100+${skin}/100+${artifactSeid}/100)+${stone}+${teraph}+${title}+${scroll}+${artifactLv}+${artifactQuality}`;
                    ceStr += `{[${mainBase}+${job}+${lv}*(${starUp}+${mainBaseUp})]* ( 1 + ${connect}/100) + [(${equipQuality}+${equipStrength}) * ( 1 + ${equipStar}/100+${equipSuit}/100)]} * (1+${jewel}/100+${school}/100+${talent}/100+${skin}/100+${artifactSeid}/100)+${stone}+${teraph}+${title}+${scroll}+${artifactLv}+${artifactQuality}`;
                } else {
                    //    attr1    + attr2 + attr4  + attr5  + attr6 + attr7 + attr9
                    val = subBase  + job   + teraph + school + title + jewel + equipStar;
                    ceVal = job   + teraph + school + title + jewel + equipStar;
                    str += `${subBase}+${job}+${teraph}+${school}+${title}+${jewel}+${equipStar}`;
                    ceStr += `${job}+${teraph}+${school}+${title}+${jewel}+${equipStar}`;
                }
                if(!attrs.has(hid)) attrs.set(hid, []);
                attrs.get(hid).push({ id: attrId, val, ceVal, str, ceStr });
            }
        }
        let result = new Map<number, number>();
        for(let [hid, arr] of attrs) {
            let lv = this.data.heroLv.get(hid)||1;
            let obj = new AttributeCal();
            obj.setLv(lv);
            obj.setByCeArr(arr);
            let ce = obj.calCe();
            this.resultCes.set(hid, ce);
            result.set(hid, ce);
            this.attrsByHid.set(hid, arr);
        }
        return result;
    }

    public calSingleGlobalPartCe(hid: number, field: 'teraph'|'scroll') {
        let arr: { id: number, val: number }[] = [];
        for(let [attrId, globalAttr] of this.data.globalAttrs) {
            arr.push({ id: attrId, val: globalAttr[field]||0 });
        }
        let lv = this.data.heroLv.get(hid)||1;
        let obj = new AttributeCal();
        obj.setLv(lv);
        obj.setByWarJson(hid, arr);
        return obj.calCe();
    }

    public getCeInc() {
        let ceResult = this.calHeroCe();
        // console.log('##### ceResult', ceResult, 'originCes', this.originCes)
        let heroCe = new Map<number, { inc: number, origin: number, ce: number }>();
        let roleInc = 0;
        for(let [hid, ce] of ceResult) {
            let originCe = this.originCes.get(hid)||0;
            if(ce != originCe) {
                heroCe.set(hid, { inc: ce - originCe, origin: originCe, ce });
                roleInc += ce - originCe;
                this.data.setHistoryCe(hid, ce);
            }
        }
        return { heroCe, roleInc }
    }

    public setResultHero(hero: HeroType) {
        this.data.setResultHero(hero);
    }

    public getResultCeArr() {
        let arr: { hid: number, ce: number }[] = [];
        for(let [ hid, ce ] of this.resultCes) {
            arr.push({ hid, ce });
        }
        arr.sort((a, b) => b.ce - a.ce);
        return arr;
    }

    public getTopLineup() {
        let topLineup: TopHero[] = [], topLineupCe = 0;
        let arr = this.getResultCeArr();
        for(let i = 0; i < LINEUP_NUM; i++) {
            if(arr[i]) {
                let { hid, ce } = arr[i];
                topLineup.push({
                    hid, ce, hero: this.data.heroObjectId.get(hid)
                });
                topLineupCe += ce;
            }
        }
        return { topLineup, topLineupCe, hasTopCeChange: topLineupCe == this.originTopHeroCe };
    }

    // 玩家等级
    public setRoleLv(lv: number) {
        this.data.roleLv = lv;
    }

    // 武将基础&成长
    public setHeroBase(hid: number, skinId: number) {
        this.data.clearHeroAttrByHid(hid, 'mainBase');
        this.data.clearHeroAttrByHid(hid, 'mainBaseUp');
        let dicHero = gameData.hero.get(skinId);

        for(let [attrId, value] of dicHero.baseAbilityArr) {
            let heroAttr = this.data.getHeroAttrByHidAndId(hid, attrId);
            heroAttr.mainBase = value;
        }
        for(let [attrId, value] of dicHero.baseAbilityUpArr) {
            let heroAttr = this.data.getHeroAttrByHidAndId(hid, attrId);
            heroAttr.mainBaseUp = value;
        }
    }

    // 武将等级
    public setHeroLv(hid: number, lv: number) {
        this.data.heroLv.set(hid, lv);
    }

    // 星级相关
    public setHeroStar(hid: number, job: number, quality: number, star: number, starStage: number, colorStar: number, colorStarStage: number) {
        this.data.clearHeroAttrByHid(hid, 'starUp');
        let dicHero = gameData.hero.get(hid);
        let dicJob = gameData.job.get(job);
        let jobClass = dicJob.job_class;
        const isWake = colorStar > 0; // 是否觉醒，只要激活了觉醒，彩星就会 > 1    
        const dicStar = isWake ? getHeroWakeByQuality(jobClass, dicHero.quality, colorStar) : getHeroStarByQuality(jobClass, quality, star); // 星级表
        const dicPreStar = isWake? getHeroWakeByQuality(jobClass, dicHero.quality, colorStar - 1): getHeroStarByQuality(jobClass, quality, star - 1);
        let curStage = isWake? colorStarStage: starStage;

        for (let stage = ABI_STAGE.START + 1; stage <= ABI_STAGE.END; stage++) {
            let attrId = ABI_STAGE_TO_TYPE.get(stage);
            let value = 0; // 星级成长
            if(curStage >= stage) {
                value = dicStar?.ceAttr.get(stage)||0;
            } else {
                value = dicPreStar?.ceAttr.get(stage)||0;
            }
            let heroAttr = this.data.getHeroAttrByHidAndId(hid, attrId);
            heroAttr.starUp = value;
        };
    }

    // 职业基础
    public setJob(hid: number, job: number, jobStage: number) {
        this.data.clearHeroAttrByHid(hid, 'job');
        const dicJob = gameData.job.get(job);
        let ceAttr = dicJob.ceAttr.get(jobStage)||[];
        for(let { id, attr } of ceAttr) {
            let heroAttr = this.data.getHeroAttrByHidAndId(hid, id);
            heroAttr.job = attr;
        }
        for(let { id, val } of dicJob.baseSubAttr) {
            let heroAttr = this.data.getHeroAttrByHidAndId(hid, id);
            heroAttr.subBase = val;
        }
    }

    // 羁绊
    public setConnection(hid: number, connections: Connect[]) {
        this.data.clearHeroAttrByHid(hid, 'connect');
        let map = new Map<number, number>(); // attrId => val;
        for(let { shipId, level = 0 } of connections) {
            let currentShip = getFriendShipByIdAndLv(hid, shipId, level);
            if (currentShip) {
                for (let { id, number: val } of currentShip.attributes) {
                    if(!map.has(id)) {
                        map.set(id, val);
                    } else {
                        map.set(id, map.get(id) + val);
                    }
                }
            }
        }    
        for(let [id, val] of map) {
            let heroAttr = this.data.getHeroAttrByHidAndId(hid, id);
            heroAttr.connect = val;
        }
    }

    // 第一次获得皮肤
    public setAddSkin(skinId: number) {
        let addSkin = gameData.fashion.get(skinId);
        for (let { id, number: val } of addSkin.globalAttr) {
            let globalAttr = this.data.getGlobalAttrById(id);
            globalAttr.skin += val;
        }
    }

    // 天赋
    public setTalent(hid: number, skins: HeroSkin[]) {
        this.data.clearHeroAttrByHid(hid, 'talent');
        let skin = skins.find(cur => cur.enable);
        let seids = this.getTalentSeid(skin.talent);
        let { ratioUp } = this.addSeidEffect(seids);
        for(let [attrId, val] of ratioUp) {
            let heroAttr = this.data.getHeroAttrByHidAndId(hid, attrId);
            heroAttr.talent = val;
        }
    }

    public getTalentSeid(talent: Talent[]) {
        let seids = new Map<number, number[]>(); // id, seids
        for(let { id, level } of talent) {
            let dicHeroTalent = gameData.heroTalent.get(id);
            for(let { type, ids} of dicHeroTalent.relation) {
                if(type == TALENT_RELATION_TYPE.REPLACE) {
                    for(let id of ids) {
                        seids.delete(id);
                    }
                }
            }
            for(let { lv, seid } of dicHeroTalent.level) {
                if(level == lv) {
                    if(!seids.has(id)) seids.set(id, []);
                    seids.get(id).push(seid, 0);
                }
            }
        }
        let result: number[] = [];
        for(let [_, ids] of seids) {
            result.push(...ids);
        }
        return result;
    }

    // 装备升品
    public setEquipQuality(hid: number, eplaceId: number, equipId: number, quality: number, qualityStage: number) {
        this.data.clearHeroAttrByHid(hid, 'equipQuality');
        this.data.clearEquipAttrByHidAndEplace(hid, eplaceId, 'equipQuality');
        let dicEquipQuality = getEquipQualityIdByEquipIdAndPoint(equipId, quality, qualityStage);
        let map = new Map<number, number>(); // attrId => val
        for(let { id, num } of dicEquipQuality.attribute) {
            if(!map.has(id)) {
                map.set(id, num);
            } else {
                map.set(id, map.get(id) + num);
            }
        }
        for(let [id, val] of map) {
            let equipAttr = this.data.getEquipAttrByHidAndId(hid, eplaceId, id);
            equipAttr.equipQuality = val;
        }
        let equips = this.data.getEquipAttrsByHid(hid);
        for(let { hid, attrId, equipQuality } of equips) {
            let heroAttr = this.data.getHeroAttrByHidAndId(hid, attrId);
            heroAttr.equipQuality += equipQuality;
        }

    }

    public clearEquip(hid: number) {
        this.data.clearHeroAttrByHid(hid, 'equipQuality');
        this.data.clearEquipAttrByHid(hid, 'equipQuality');
        this.data.clearHeroAttrByHid(hid, 'equipStrength');
        this.data.clearEquipAttrByHid(hid, 'equipStrength');
        this.data.clearHeroAttrByHid(hid, 'equipStar');
        this.data.clearEquipAttrByHid(hid, 'equipStar');
    }
    
    // 装备强化
    public setEquipStrength(hid: number, eplaceId: number, equipId: number, lv: number) {
        this.data.clearHeroAttrByHid(hid, 'equipStrength');
        this.data.clearEquipAttrByHidAndEplace(hid, eplaceId, 'equipStrength');
        let dicEquipStrenth = getEquipStrenthenAttr(equipId, lv);
        let map = new Map<number, number>(); // attrId => val
        for(let { id, num } of dicEquipStrenth.attr) {
            if(!map.has(id)) {
                map.set(id, num);
            } else {
                map.set(id, map.get(id) + num);
            }
        }
        for(let [id, val] of map) {
            let equipAttr = this.data.getEquipAttrByHidAndId(hid, eplaceId, id);
            equipAttr.equipStrength = val;
        }
        let equips = this.data.getEquipAttrsByHid(hid);
        for(let { hid, attrId, equipStrength } of equips) {
            let heroAttr = this.data.getHeroAttrByHidAndId(hid, attrId);
            heroAttr.equipStrength += equipStrength;
        }
    }

    // 装备精炼（升星）
    public setEquipStar(hid: number, eplaceId: number, equipId: number, star: number, starStage: number) {
        this.data.clearHeroAttrByHid(hid, 'equipStar');
        this.data.clearEquipAttrByHidAndEplace(hid, eplaceId, 'equipStar');
        let dicEquipStar = getEquipStarAttrByStage(equipId, star, starStage);
        if(dicEquipStar) {
            let { mainAttr, subAttr } = dicEquipStar;
            let map = new Map<number, number>(); // attrId => val
            for(let { id, num } of [...mainAttr, ...subAttr]) {
                if(!map.has(id)) {
                    map.set(id, num);
                } else {
                    map.set(id, map.get(id) + num);
                }
            }
            for(let [id, val] of map) {
                let equipAttr = this.data.getEquipAttrByHidAndId(hid, eplaceId, id);
                equipAttr.equipStar = val;
            }
            let equips = this.data.getEquipAttrsByHid(hid);
            for(let { hid, attrId, equipStar } of equips) {
                let heroAttr = this.data.getHeroAttrByHidAndId(hid, attrId);
                heroAttr.equipStar += equipStar;
            }
        }
    }

    // 装备套装
    public setEquipSuit(hid: number, skinId: number, ePlace: EPlace[]) {
        this.data.clearHeroAttrByHid(hid, 'equipSuit');
        let dicEquipSuit = getEquipSuitByHero(skinId);
        let suitStars: number[] = [];
        for(let equipId of dicEquipSuit.equips) {
            let equip = ePlace.find(cur => cur.equipId == equipId);
            suitStars.push(equip? equip.star: 0);
        }
        let minStar = Math.min(...suitStars);

        for(let { star, id, val } of dicEquipSuit.effect) {
            if(minStar >= star) {
                let heroAttr = this.data.getHeroAttrByHidAndId(hid, id);
                heroAttr.equipSuit = val;
            }
        }
    }

    // 天晶
    public setJewel(hid: number, eplaceId: number, stones: Stone[], jewel: JewelType) {
        this.data.clearHeroAttrByHid(hid, 'jewel');
        this.data.clearEquipAttrByHidAndEplace(hid, eplaceId, 'jewel');
        let seids: number[] = [];
        if(jewel) {
            for(let { id, seid, rand } of jewel.randSe) {
                if(this.isRandSeUnLock(jewel.id, id, stones)) {
                    seids.push(seid, rand);
                }
            }
            for(let { seid, rand } of jewel.rareSe) {
                seids.push(seid, rand);
            }
        }
        let { ratioUp } = this.addSeidEffect(seids);
        let map = new Map<number, number>(); // attrId => val
        for(let [id, num] of ratioUp) {
            if(!map.has(id)) {
                map.set(id, num);
            } else {
                map.set(id, map.get(id) + num);
            }
        }
        for(let [id, val] of map) {
            let equipAttr = this.data.getEquipAttrByHidAndId(hid, eplaceId, id);
            equipAttr.jewel = val;
        }
        let equips = this.data.getEquipAttrsByHid(hid);
        for(let { hid, attrId, jewel } of equips) {
            let heroAttr = this.data.getHeroAttrByHidAndId(hid, attrId);
            heroAttr.jewel += jewel;
        }
    }

    public isRandSeUnLock(jewelId: number, randSeId: number, stones: Stone[]) {
        let dicJewel = gameData.jewel.get(jewelId);
        let dicJewelCondition = getJewelConditionByLvAndSeId(dicJewel.lv, randSeId);
        let stoneCnt = 0, stoneLv = 0;
        for(let { stone } of stones) {
            let dicStone = gameData.stone.get(stone);
            if(dicStone) {
                stoneCnt++;
                stoneLv += dicStone.lv;
            }
        }
        return stoneCnt >= dicJewelCondition.stoneCnt && stoneLv >= dicJewelCondition.stoneLv;
    }
    
    // 地玉
    public setStone(hid: number, eplaceId: number, stones: Stone[]) {
        this.data.clearHeroAttrByHid(hid, 'stone');
        this.data.clearEquipAttrByHidAndEplace(hid, eplaceId, 'stone');
        let map = new Map<number, number>(); // attrId => val
        for(let { stone } of stones) {
            let dicStone = gameData.stone.get(stone);
            if(dicStone) {
                for(let { id, num } of dicStone.attribute) {
                    if(!map.has(id)) {
                        map.set(id, num);
                    } else {
                        map.set(id, map.get(id) + num);
                    }
                }
            }
        }
        for(let [id, val] of map) {
            let equipAttr = this.data.getEquipAttrByHidAndId(hid, eplaceId, id);
            equipAttr.stone = val;
        }
        let equips = this.data.getEquipAttrsByHid(hid);
        for(let { hid, attrId, stone } of equips) {
            let heroAttr = this.data.getHeroAttrByHidAndId(hid, attrId);
            heroAttr.stone += stone;
        }
    }


    // 爵位
    public setTitle(title: number) {
        this.data.clearRoleAttr('title');
        let dicTitle = gameData.title.get(title)||{ mainAttrValue: new Map(), assiAttrValue: new Map() };
        for(let [attrId, value] of dicTitle.mainAttrValue) {
            let globalAttr = this.data.getGlobalAttrById(attrId);
            globalAttr.title = value;
        }
        for(let [attrId, value] of dicTitle.assiAttrValue) {
            let globalAttr = this.data.getGlobalAttrById(attrId);
            globalAttr.title = value;
        }
    }

    // 神像相关
    public setTeraph(teraphs: Teraph[]) {

        this.data.clearRoleAttr('teraph');
        let teraphOfAttrId = new Map<number, number>();
        for(let teraph of teraphs) {
            let dicTeraph = getTeraph(teraph.id, teraph.grade);
            for(let [attrId, value] of teraph.attr) { // 主属性
                let basic = dicTeraph.basicAttrValue.get(attrId)||0;
                addToMap(teraphOfAttrId, attrId, value + basic);
            }
            for(let [attrId, value] of dicTeraph.assiAttrValue) { // 次属性
                addToMap(teraphOfAttrId, attrId, value);
            }
        }
        for(let [attrId, value] of teraphOfAttrId) {
            let globalAttr = this.data.getGlobalAttrById(attrId);
            globalAttr.teraph = value;
        }
    }

    // 名将谱
    public setScroll(hid: number, scrollStar: number, scrollQuality: number, scrollColorStar: number) {
        this.data.clearScrollAttr(hid);
        let dicHero =gameData.hero.get(hid);
        let dicHeroScroll = getScollByStar(dicHero.quality, scrollStar, scrollQuality, scrollColorStar);

        if(dicHeroScroll) {
            for(let [attrId, value] of dicHeroScroll.ceAttr) {
                this.data.setScrollAttrs(hid, attrId, value);
                this.data.calScrollAttrsToGlobal(attrId);
            }
        }
    }

    // 百家学宫
    public setSchool(isPutOn: boolean, hid: number, schoolId: number, star: number, colorStar: number, quality: number) {
        this.data.clearSchoolAttr(hid);
        let dicSchool = gameData.school.get(schoolId);
        let dicSchoolRate = getSchoolRateByStar(star, colorStar, quality);

        for (let attrId of dicSchool.upAttribute) {
            if(ABI_TYPE_MAIN.includes(attrId)) { // 主属性
                this.data.setSchoolAttrs(hid, attrId, isPutOn?dicSchoolRate.mainAttrAPerent: 0);
            } else {
                this.data.setSchoolAttrs(hid, attrId, isPutOn?dicSchoolRate.assiAttrAddValue: 0);
            }
            this.data.calSchoolAttrsToGlobal(attrId);
        }
    }

    // 卸下宝物
    public setPutOffArtifact(hid: number) {
        this.data.clearHeroAttrByHid(hid, 'artifactLv');
        this.data.clearHeroAttrByHid(hid, 'artifactQuality');
        this.data.clearHeroAttrByHid(hid, 'artifactSeid');
    }

    // 装备宝物
    public setPutArtifact(hid: number, skinId: number, job: number, artifact: ArtifactModelType) {
        this.data.clearHeroAttrByHid(hid, 'artifactLv');
        this.data.clearHeroAttrByHid(hid, 'artifactQuality');
        this.data.clearHeroAttrByHid(hid, 'artifactSeid');

        if(artifact) {
            this.setArtifactLv(hid, artifact.artifactId, artifact.lv);
            this.setArtifactQuality(hid, artifact.artifactId);
            this.setArtifactSeid(hid, skinId, job, artifact.artifactId);
        }
    }

    // 宝物等级
    public setArtifactLv(hid: number, artifactId: number, lv: number) {
        this.data.clearHeroAttrByHid(hid, 'artifactLv');
        let dicArtifact = gameData.artifact.get(artifactId);
        if(!dicArtifact) return;

        let dicArtifactLv = getDicArtifactLvByPlanId(dicArtifact.lvAttrPlan, lv);
        if(!dicArtifactLv) return;

        let ceAttr = dicArtifactLv.attr||[];
        for(let { id, attr } of ceAttr) {
            let heroAttr = this.data.getHeroAttrByHidAndId(hid, id);
            heroAttr.artifactLv = attr;
        }
    }

    // 宝物品质
    public setArtifactQuality(hid: number, artifactId: number) {
        this.data.clearHeroAttrByHid(hid, 'artifactQuality');

        let dicArtifact = gameData.artifact.get(artifactId);
        if(!dicArtifact) return;

        // 基础属性
        let dicArtifactQualityPlan = gameData.artifactQualityPlan.get(dicArtifact.qualityAttrPlan);
        if(!dicArtifactQualityPlan) return;

        let ceAttr = dicArtifactQualityPlan.attr||[];
        for(let { id, attr } of ceAttr) {
            let heroAttr = this.data.getHeroAttrByHidAndId(hid, id);
            heroAttr.artifactQuality = attr;
        }
    }

    // 宝物词条
    public setArtifactSeid(hid: number, skinId: number, job: number, artifactId: number) {
        this.data.clearHeroAttrByHid(hid, 'artifactSeid');

        let dicArtifact = gameData.artifact.get(artifactId);
        if(!dicArtifact) return;
        let dicJob = gameData.job.get(job);
        if(!dicJob) return;
        
        let seids: number[] = []; // id, seids
        for(let seid of dicArtifact.seids) {
            let dicArtifactSeid = gameData.artifactSeid.get(seid);
            if(!dicArtifactSeid) continue;
            if(dicArtifactSeid.jobClass != 0 && dicArtifactSeid.jobClass != dicJob.job_class) continue;
            if(dicArtifactSeid.hids.length > 0 && dicArtifactSeid.hids.indexOf(skinId) == -1) continue;
            if(dicArtifactSeid.quality > dicArtifact.quality) continue;
            for(let seid of dicArtifactSeid.seids) {
                seids.push(seid, 0);
            }
        }

        let { ratioUp } = this.addSeidEffect(seids);
        for(let [attrId, val] of ratioUp) {
            let heroAttr = this.data.getHeroAttrByHidAndId(hid, attrId);
            heroAttr.artifactSeid = val;
        }
    }

    // 添加技能增加的被动属性
    private addSeidEffect(seidList: number[]) {
        let fixUp = new Map<number, number>(), ratioUp = new Map<number, number>();
        let effectList: DicSe[] = []; // any: dic_zyz_se表内容

        for (let ii = 0; ii < seidList.length; ii += 2) {
            let seid = seidList[ii];
            let rand = seidList[ii + 1] || 0;
            let dicSeid: DicSe | DicRandomEffectPool = gameData.se.get(seid);
            if (!dicSeid) dicSeid = gameData.randomEffectPool.get(seid);
            if (dicSeid && dicSeid.id > 0) {
                this.addSeid(effectList, dicSeid.id, rand, dicSeid.gainValueArr)
            }
        }

        // console.log('effectList', JSON.stringify(effectList));
        for (let { type, id, gainValueArr: [ability, value] } of effectList) {
            // console.log('##### addToMap type', type, 'id', id, 'ability', ability, 'value', value)
            if (type == SEID_TYPE.FIX) { // 加值
                addToMap(fixUp, ability, value);
            } else if (type == SEID_TYPE.MAIN_RATIO||type == SEID_TYPE.MAIN_RATIO_2) { // 主属性加百分比
                if(ABI_TYPE_MAIN.includes(ability)) {
                    addToMap(ratioUp, ability, value);
                }
            } else if (type == SEID_TYPE.SUB_RATIO||type == SEID_TYPE.SUB_RATIO_2) { // 次级属性加百分比
                if(!ABI_TYPE_MAIN.includes(ability)) {
                    addToMap(ratioUp, ability, value);
                }
            }

        }
        return { fixUp, ratioUp };
    }

    // 获取dic_zyz_se内容
    private addSeid(effectList: (DicSe | DicRandomEffectPool)[], seidId: number, rand: number, seidValue: number[] = []) {
        let curSeid: DicSe | DicRandomEffectPool = gameData.se.get(seidId);
        if (!curSeid) curSeid = gameData.randomEffectPool.get(seidId);
        if (!curSeid) { console.log("seidId not found:" + seidId); return; }
        if (!seidValue) seidValue = curSeid.gainValueArr;

        if (curSeid.type === SEID_TYPE.MIX) {
            for (let i = 0; i < seidValue.length; i++) {
                this.addSeid(effectList, seidValue[i], rand);
            }
            return;
        }
        let seid: DicSe | DicRandomEffectPool = deepCopy(curSeid);
        if (curSeid.index > 0) {
            seid.gainValueArr[curSeid.index - 1] = rand;
        }
        effectList.push(seid);
    }
}

class CalCeData {
    roleLv: number = 1;  // 玩家等级
    // 全局
    globalAttrs: Map<number, GlobalMainAttr|GlobalSubAttr> = new Map(); // attrId => GlobalAttr
    // 武将
    heroAttrs: Map<string, HeroMainAttr|HeroSubAttr> = new Map(); // hid+attrId => HeroAttr
    heroAttrsByHid: Map<number, string[]> = new Map(); // hid => [hid+attrId]
    heroLv: Map<number, number> = new Map();
    heroHistoryCe: Map<number, number> = new Map();
    heroObjectId: Map<number, string> = new Map();    
    // 装备
    equipAttrs: Map<string, EquipMainAttr|EquipSubAttr> = new Map(); // hid+eplaceId+attrId => EquipAttr
    equipAttrsByHid: Map<number, string[]> = new Map(); // hid => [hid+eplaceId+attrId]
    equipAttrsByHidAndEplace: Map<string, { hid: number, eplaceId: number, keys: string[]}> = new Map(); // hid+eplaceId =>[hid+eplaceId+attrId]
    equipAttrsByHidAndAttrId: Map<string, { hid: number, attrId: number, keys: string[]}> = new Map(); // hid+attrId =>[hid+eplaceId+attrId]
    equipLv: Map<string, number> = new Map();  // hid+eplaceId => lv
    // 百家学宫
    schoolAttrs: Map<string, { hid: number, attrId: number, ce: number }> = new Map(); // hid + attr => ratio
    schoolAttrsByAttrId: Map<number, string[]> = new Map();  // attrId => hid + attr
    schoolAttrsByHid: Map<number, string[]> = new Map();  // hid => hid + attr
    // 名将谱
    scrollAttrs: Map<string, { hid: number, attrId: number, ce: number }> = new Map(); // hid + attr => ratio
    scrollAttrsByAttrId: Map<number, string[]> = new Map();  // attrId => hid + attr
    scrollAttrsByHid: Map<number, string[]> = new Map();  // hid => hid + attr

    constructor(roleCe: RoleCeType) {
        if(roleCe) {
            this.roleLv = roleCe.roleLv;
            for(let globalAttr of roleCe.globalAttrs) {
                let obj = this.getGlobalAttrById(globalAttr.attrId);
                obj.setByRoleCe(globalAttr);
            } 
            for(let {hid, lv, attrs, historyCe, objectId } of roleCe.heroAttrs) {
                this.heroLv.set(hid, lv);
                this.heroHistoryCe.set(hid, historyCe);
                this.heroObjectId.set(hid, objectId);
                for(let cell of attrs) {
                    let obj = this.getHeroAttrByHidAndId(hid, cell.attrId);
                    obj.setByRoleCe(cell);
                }
            }
            for(let { hid, eplaceId, lv, attrs } of roleCe.equipAttrs) {
                this.equipLv.set(`${hid}_${eplaceId}`, lv);
                for(let cell of attrs) {
                    let obj = this.getEquipAttrByHidAndId(hid, eplaceId, cell.attrId);
                    obj.setByRoleCe(cell);
                }
            }
            for(let { hid, attrId, value } of roleCe.schoolAttrs) {
                this.setSchoolAttrs(hid, attrId, value);
            }
            for(let { hid, attrId, value } of roleCe.scrollAttrs) {
                this.setScrollAttrs(hid, attrId, value);
            }
        }
    }

    public getGlobalAttrById(attrId: number) {
        if(!this.globalAttrs.has(attrId)) {
            if(ABI_TYPE_MAIN.indexOf(attrId) != -1) {
                let obj = new GlobalMainAttr(attrId);
                this.globalAttrs.set(attrId, obj);
            } else {
                let obj = new GlobalSubAttr(attrId);
                this.globalAttrs.set(attrId, obj);
            }
        }
        return this.globalAttrs.get(attrId);
    }

    public clearRoleAttr(field: string) {
        for(let globalAttr of this.globalAttrs) {
            globalAttr[field] = 0;
        }
    }

    public getHeroAttrsByHid(hid: number) {
        let keys = this.heroAttrsByHid.get(hid)||[];
        return keys.map(key => this.heroAttrs.get(key));
    }

    public getHeroAttrByHidAndId(hid: number, attrId: number) {
        let key = `${hid}_${attrId}`;
        if(!this.heroAttrs.has(key)) {
            if(ABI_TYPE_MAIN.indexOf(attrId) != -1) {
                let obj = new HeroMainAttr(hid, attrId);
                this.heroAttrs.set(key, obj);
            } else {
                let obj = new HeroSubAttr(hid, attrId);
                this.heroAttrs.set(key, obj);
            }

            if(!this.heroAttrsByHid.has(hid)) {
                this.heroAttrsByHid.set(hid, []);
            }
            this.heroAttrsByHid.get(hid).push(key);
        }
        return this.heroAttrs.get(key);
    }

    public clearHeroAttrByHid(hid: number, field: string) {
        let heroAttrs = this.getHeroAttrsByHid(hid)||[];
        for(let heroAttr of heroAttrs) {
            heroAttr[field] = 0;
        }
    }

    public getEquipAttrByHidAndId(hid: number, eplaceId: number, attrId: number) {
        let key = `${hid}_${eplaceId}_${attrId}`;
        if(!this.equipAttrs.has(key)) {
            if(ABI_TYPE_MAIN.indexOf(attrId) != -1) {
                let obj = new EquipMainAttr(hid, attrId);
                this.equipAttrs.set(key, obj);
            } else {
                let obj = new EquipSubAttr(hid, attrId);
                this.equipAttrs.set(key, obj);
            }

            if(!this.equipAttrsByHid.has(hid)) {
                this.equipAttrsByHid.set(hid, []);
            }
            this.equipAttrsByHid.get(hid).push(key);
            let key2 = `${hid}_${eplaceId}`;
            if(!this.equipAttrsByHidAndEplace.has(key2)) {
                this.equipAttrsByHidAndEplace.set(key2, { hid, eplaceId, keys: []});
            }
            this.equipAttrsByHidAndEplace.get(key2).keys.push(key);
        }
        return this.equipAttrs.get(key);
    }

    public getEquipAttrsByHid(hid: number) {
        let keys = this.equipAttrsByHid.get(hid)||[];
        return keys.map(key => this.equipAttrs.get(key));
    }

    public getEquipAttrsByHidAndEplace(hid: number, eplaceId: number) {
        let key2 = `${hid}_${eplaceId}`;
        if(!this.equipAttrsByHidAndEplace.has(key2)) return []
        let { keys } = this.equipAttrsByHidAndEplace.get(key2);
        return keys.map(key => this.equipAttrs.get(key));
    }

    public clearEquipAttrByHidAndEplace(hid: number, eplaceId: number, field: string) {
        let equipAttrs = this.getEquipAttrsByHidAndEplace(hid, eplaceId);
        for(let equipAttr of equipAttrs) {
            equipAttr[field] = 0;
        }
    }

    public clearEquipAttrByHid(hid: number, field: string) {
        let equipAttrs = this.getEquipAttrsByHid(hid);
        for(let equipAttr of equipAttrs) {
            equipAttr[field] = 0;
        }
    }

    public setSchoolAttrs(hid: number, attrId: number, value: number) {
        let key = `${hid}_${attrId}`;
        if(!this.schoolAttrs.has(key)) {
            this.schoolAttrs.set(key, { hid, attrId, ce: value });
            if(!this.schoolAttrsByAttrId.has(attrId)) {
                this.schoolAttrsByAttrId.set(attrId, []);
            }
            this.schoolAttrsByAttrId.get(attrId).push(key);
            if(!this.schoolAttrsByHid.has(hid)) {
                this.schoolAttrsByHid.set(hid, []);
            }
            this.schoolAttrsByHid.get(hid).push(key);
        } else {
            this.schoolAttrs.get(key).ce = value;
        }
    }

    public getSchoolAttrsByHid(hid: number) {
        let keys = this.schoolAttrsByHid.get(hid)||[];
        return keys.map(key => this.schoolAttrs.get(key));
    }

    public calSchoolAttrsToGlobal(attrId: number) {
        let keys = this.schoolAttrsByAttrId.get(attrId)||[];
        let schoolResult = 0;
        for(let key of keys) {
            let { ce } = this.schoolAttrs.get(key);
            schoolResult += ce;
        }
        let globalAttr = this.getGlobalAttrById(attrId);
        globalAttr.school = schoolResult;
    }

    public clearSchoolAttr(hid: number) {
        let schoolAttrs = this.getSchoolAttrsByHid(hid)||[];
        for(let schoolAttr of schoolAttrs) {
            schoolAttr.ce = 0;        
            this.calSchoolAttrsToGlobal(schoolAttr.attrId);
        }
    }

    public setScrollAttrs(hid: number, attrId: number, value: number) {
        let key = `${hid}_${attrId}`;
        if(!this.scrollAttrs.has(key)) {
            this.scrollAttrs.set(key, { hid, attrId, ce: value });
            if(!this.scrollAttrsByAttrId.has(attrId)) {
                this.scrollAttrsByAttrId.set(attrId, []);
            }
            this.scrollAttrsByAttrId.get(attrId).push(key);
            if(!this.scrollAttrsByHid.has(hid)) {
                this.scrollAttrsByHid.set(hid, []);
            }
            this.scrollAttrsByHid.get(hid).push(key);
        } else {
            this.scrollAttrs.get(key).ce = value;
        }
    }

    public calScrollAttrsToGlobal(attrId: number) {
        let keys = this.scrollAttrsByAttrId.get(attrId)||[];
        let result = 0;
        for(let key of keys) {
            let { ce } = this.scrollAttrs.get(key);
            result += ce;
        }
        let globalAttr = this.getGlobalAttrById(attrId);
        globalAttr.scroll = result;
    }

    public clearScrollAttr(hid: number) {
        let scrollAttrs = this.getScrollAttrsByHid(hid);
        for(let scrollAttr of scrollAttrs) {
            scrollAttr.ce = 0;        
            this.calScrollAttrsToGlobal(scrollAttr.attrId);
        }
    }

    public getScrollAttrsByHid(hid: number) {
        let keys = this.scrollAttrsByHid.get(hid)||[];
        return keys.map(key => this.scrollAttrs.get(key));
    }

    public setHistoryCe(hid: number, ce: number) {
        let historyCe = this.heroHistoryCe.get(hid)||0;
        if(ce > historyCe) {
            this.heroHistoryCe.set(hid, ce);
        }
    }

    public setResultHero(hero: HeroType) {
        this.heroObjectId.set(hero.hid, hero._id);
    }

    public getRoleCeTable(roleId: string, attributes: Attribute[]) {
        let globalAttrs: AttrCell[] = [];
        for(let [_, globalAttr] of this.globalAttrs) {
            globalAttrs.push(globalAttr.getGlobalAttr());
        }
        let heroAttrs: HeroAttr[] = [];
        for(let [hid, keys] of this.heroAttrsByHid) {
            let lv = this.heroLv.get(hid);
            let historyCe = this.heroHistoryCe.get(hid)||0;
            let objectId = this.heroObjectId.get(hid);
            let attrs: AttrCell[] = [];
            for(let key of keys) {
                let heroAttr = this.heroAttrs.get(key);
                attrs.push(heroAttr.getHeroAttrCell());
            }
            heroAttrs.push({
                hid, lv, attrs, historyCe, objectId
            });
        }
        let equipAttrs: EquipAttr[] = [];
        for(let [key2, { hid, eplaceId, keys }] of this.equipAttrsByHidAndEplace) {
            let lv = this.equipLv.get(key2);
            let attrs: AttrCell[] = [];
            for(let key of keys) {
                let equipAttr = this.equipAttrs.get(key);
                attrs.push(equipAttr.getEquipAttrCell());
            }
            equipAttrs.push({
                hid, eplaceId, lv, attrs
            });
        }
        let schoolAttrs: SchoolAttr[] = [];
        for(let [_, { hid, attrId, ce }] of this.schoolAttrs) {
            schoolAttrs.push({ hid, attrId, value: ce });
        }
        let scrollAttrs: ScrollAttr[] = [];
        for(let [_, { hid, attrId, ce }] of this.scrollAttrs) {
            scrollAttrs.push({ hid, attrId, value: ce });
        }
        return {
            roleLv: this.roleLv, roleId, globalAttrs, heroAttrs, equipAttrs, schoolAttrs, scrollAttrs, attributes
        }
    }
}

abstract class GlobalAllAttr {
    attrId: number;
    school: number = 0;
    teraph: number = 0;
    title: number = 0;
    scroll: number = 0;
    skin: number = 0;
    
    constructor(attrId: number) {
        this.attrId = attrId;
    }

    abstract setByRoleCe(data: AttrCell): void;
    abstract getValues(): number[];

    public getGlobalAttr() {
        let values = this.getValues().map(cur => (cur||0));
        return {
            attrId: this.attrId,
            values
        }
    }
}

class GlobalMainAttr extends GlobalAllAttr {

    public setByRoleCe(globalAttr: AttrCell) {
        for(let i = 0; i < globalAttr.values.length; i++) {
            let value = globalAttr.values[i];
            if(value != undefined) {
                switch(i) {
                    case GLOBAL_MAIN_ATTR_INDEX.SCHOOL:
                        this.school = value; break;
                    case GLOBAL_MAIN_ATTR_INDEX.TERAPH:
                        this.teraph = value; break;
                    case GLOBAL_MAIN_ATTR_INDEX.TITLE:
                        this.title = value; break;
                    case GLOBAL_MAIN_ATTR_INDEX.SCROLL:
                        this.scroll = value; break;
                    case GLOBAL_MAIN_ATTR_INDEX.SKIN:
                        this.skin = value; break;
                }
            }
        }
    }

    public getValues() {
        let values: number[] = [];
        for(let i = GLOBAL_MAIN_ATTR_INDEX.START; i < GLOBAL_MAIN_ATTR_INDEX.END; i++) {
            switch(i) {
                case GLOBAL_MAIN_ATTR_INDEX.SCHOOL:
                    values.push(this.school); 
                    break;
                case GLOBAL_MAIN_ATTR_INDEX.TERAPH:
                    values.push(this.teraph); 
                    break;
                case GLOBAL_MAIN_ATTR_INDEX.TITLE:
                    values.push(this.title); 
                    break;
                case GLOBAL_MAIN_ATTR_INDEX.SCROLL:
                    values.push(this.scroll); 
                    break;
                case GLOBAL_MAIN_ATTR_INDEX.SKIN:
                    values.push(this.skin); 
                    break;
            }
        }
        return values;
    }
}

class GlobalSubAttr extends GlobalAllAttr {

    public setByRoleCe(globalAttr: AttrCell) {
        for(let i = 0; i < globalAttr.values.length; i++) {
            let value = globalAttr.values[i];
            if(value != undefined) {
                switch(i) {
                    case GLOBAL_SUB_ATTR_INDEX.SCHOOL:
                        this.school = value; break;
                    case GLOBAL_MAIN_ATTR_INDEX.TERAPH:
                        this.teraph = value; break;
                    case GLOBAL_MAIN_ATTR_INDEX.TITLE:
                        this.title = value; break;
                    case GLOBAL_MAIN_ATTR_INDEX.SKIN:
                        this.skin = value; break;
                }
            }
        }
    }

    public getValues() {
        let values: number[] = [];
        for(let i = GLOBAL_MAIN_ATTR_INDEX.START; i < GLOBAL_MAIN_ATTR_INDEX.END; i++) {
            switch(i) {
                case GLOBAL_MAIN_ATTR_INDEX.SCHOOL:
                    values.push(this.school); 
                    break;
                case GLOBAL_MAIN_ATTR_INDEX.TERAPH:
                    values.push(this.teraph); 
                    break;
                case GLOBAL_MAIN_ATTR_INDEX.TITLE:
                    values.push(this.title); 
                    break;
                case GLOBAL_MAIN_ATTR_INDEX.SKIN:
                    values.push(this.skin); 
                    break;
            }
        }
        return values;
    }
}

abstract class HeroAllAttr {
    hid: number;
    attrId: number;
    mainBase: number = 0;      // hp1，武将基础属性（dic_zyz_hero的hp） 
    subBase: number = 0;       // attr1，职业基础（dic_zyz_job的baseSubAttr字段）
    job: number = 0;           // hp1 & attr2, 职业属性(dic_zyz_job的attr)
    starUp: number = 0;        // hp2, 角色升星成长(dic_zyz_hero_star的hp或dic_zyz_hero_wake的hp)
    connect: number = 0;       // hp3, 角色羁绊固定值(dic_zyz_friend_ship的attribute)
    favour: number = 0;        // 删除-hp4, 声望加成(dic_zyz_friend_ship_level的add)
    talent: number = 0;        // hp5 & attr3, 天赋树百分比加成(dic_zyz_hero_talent的levelSeid，然后对应到dic_zyz_se)
    equipQuality: number = 0;  // hp6, 装备品质基础值(dic_zyz_equipQuality的attribute)
    equipStrength: number = 0; // hp7, 装备强化值(dic_zyz_equipStrenthAttr的attr)
    equipStar: number = 0;     // hp8 & attr9，装备升星(dic_zyz_equipStar的mainAttr和subAttr)
    equipSuit: number = 0;     // hp8，套装(dic_zyz_equipSuit的effect，然后对应dic_zyz_se)
    jewel: number = 0;         // hp9 & attr7，天晶随机属性值(jewel的ranSe，对应dic_zyz_randomEffectPool)
    stone: number = 0;         // hp10, 地玉增加的固定值(dic_zyz_stone的attribute)
    mainBaseUp: number = 0;    // hp2, 基础成长(dic_zyz_hero的hp_up)
    artifactLv: number = 0;    // hp14 宝物等级(dic_zyz_artifactLvPlan的attr)
    artifactQuality: number = 0; // hp14 宝物品质(dic_zyz_artifactQualityPlan的attr)
    artifactSeid: number = 0;  // hp9 宝物词条(dic_zyz_artifactSeid算出来的)

    constructor(hid: number, attrId: number, ) {
        this.hid = hid;
        this.attrId = attrId;

    }

    abstract setByRoleCe(data: AttrCell): void;
    abstract getValues(): number[];

    public getHeroAttrCell() {
        return {
            attrId: this.attrId,
            values: this.getValues()
        }
    }
}

class HeroMainAttr extends HeroAllAttr {
    public setByRoleCe(heroAttr: AttrCell) {
        for(let i = 0; i < heroAttr.values.length; i++) {
            let value = heroAttr.values[i];
            if(value != undefined) {
                switch(i) {
                    case HERO_MAIN_ATTR_INDEX.BASE:
                        this.mainBase = value; break;
                    case HERO_MAIN_ATTR_INDEX.JOB:
                        this.job = value; break;
                    case HERO_MAIN_ATTR_INDEX.STAR_UP:
                        this.starUp = value; break;
                    case HERO_MAIN_ATTR_INDEX.CONNECT:
                        this.connect = value; break;
                    case HERO_MAIN_ATTR_INDEX.FAVOUR:
                        this.favour = value; break;
                    case HERO_MAIN_ATTR_INDEX.TALENT:
                        this.talent = value; break;
                    case HERO_MAIN_ATTR_INDEX.EQUIP_QUALITY:
                        this.equipQuality = value; break;
                    case HERO_MAIN_ATTR_INDEX.EQUIP_STRENGTH:
                        this.equipStrength = value; break;
                    case HERO_MAIN_ATTR_INDEX.EQUIP_STAR:
                        this.equipStar = value; break;
                    case HERO_MAIN_ATTR_INDEX.EQUIP_SUIT:
                        this.equipSuit = value; break;
                    case HERO_MAIN_ATTR_INDEX.JEWEL:
                        this.jewel = value; break;
                    case HERO_MAIN_ATTR_INDEX.STONE:
                        this.stone = value; break;
                    case HERO_MAIN_ATTR_INDEX.BASE_UP:
                        this.mainBaseUp = value; break;
                    case HERO_MAIN_ATTR_INDEX.ARTIFACT_LV:
                        this.artifactLv = value; break;
                    case HERO_MAIN_ATTR_INDEX.ARTIFACT_QUALITY:
                        this.artifactQuality = value; break;
                    case HERO_MAIN_ATTR_INDEX.ARTIFACT_SEID:
                        this.artifactSeid = value; break;
                }
            }
        }
    }

    public getValues() {
        let values: number[] = [];
        for(let i = HERO_MAIN_ATTR_INDEX.START; i < HERO_MAIN_ATTR_INDEX.END; i++) {
            switch(i) {
                case HERO_MAIN_ATTR_INDEX.BASE:
                    values.push(this.mainBase); 
                    break;
                case HERO_MAIN_ATTR_INDEX.JOB:
                    values.push(this.job);
                    break;
                case HERO_MAIN_ATTR_INDEX.STAR_UP:
                    values.push(this.starUp); 
                    break;
                case HERO_MAIN_ATTR_INDEX.CONNECT:
                    values.push(this.connect); 
                    break;
                case HERO_MAIN_ATTR_INDEX.FAVOUR:
                    values.push(this.favour); 
                    break;
                case HERO_MAIN_ATTR_INDEX.TALENT:
                    values.push(this.talent); 
                    break;
                case HERO_MAIN_ATTR_INDEX.EQUIP_QUALITY:
                    values.push(this.equipQuality); 
                    break;
                case HERO_MAIN_ATTR_INDEX.EQUIP_STRENGTH:
                    values.push(this.equipStrength); 
                    break;
                case HERO_MAIN_ATTR_INDEX.EQUIP_STAR:
                    values.push(this.equipStar); 
                    break;
                case HERO_MAIN_ATTR_INDEX.EQUIP_SUIT:
                    values.push(this.equipSuit); 
                    break;
                case HERO_MAIN_ATTR_INDEX.JEWEL:
                    values.push(this.jewel);
                    break;
                case HERO_MAIN_ATTR_INDEX.STONE:
                    values.push(this.stone);
                    break;
                case HERO_MAIN_ATTR_INDEX.BASE_UP:
                    values.push(this.mainBaseUp);
                    break;
                case HERO_MAIN_ATTR_INDEX.ARTIFACT_LV:
                    values.push(this.artifactLv);
                    break;
                case HERO_MAIN_ATTR_INDEX.ARTIFACT_QUALITY:
                    values.push(this.artifactQuality);
                    break;
                case HERO_MAIN_ATTR_INDEX.ARTIFACT_SEID:
                    values.push(this.artifactSeid);
                    break;
            }
        }
        return values;
    }
}

class HeroSubAttr extends HeroAllAttr {
    public setByRoleCe(heroAttr: AttrCell) {
        for(let i = 0; i < heroAttr.values.length; i++) {
            let value = heroAttr.values[i];
            if(value != undefined) {
                switch(i) {
                    case HERO_SUB_ATTR_INDEX.BASE:
                        this.subBase = value; break;
                    case HERO_SUB_ATTR_INDEX.JOB:
                        this.job = value; break;
                    case HERO_SUB_ATTR_INDEX.TALENT:
                        this.talent = value; break;
                    case HERO_SUB_ATTR_INDEX.JEWEL:
                        this.jewel = value; break;
                    case HERO_SUB_ATTR_INDEX.EQUIP_STAR:
                        this.equipStar = value; break;
                }
            }
        }
    }

    public getValues() {
        let values: number[] = [];
        for(let i = HERO_SUB_ATTR_INDEX.START; i < HERO_SUB_ATTR_INDEX.END; i++) {
            switch(i) {
                case HERO_SUB_ATTR_INDEX.BASE:
                    values.push(this.subBase); 
                    break;
                case HERO_SUB_ATTR_INDEX.JOB:
                    values.push(this.job); 
                    break;
                case HERO_SUB_ATTR_INDEX.TALENT:
                    values.push(this.talent); 
                    break;
                case HERO_SUB_ATTR_INDEX.JEWEL:
                    values.push(this.jewel); 
                    break;
                case HERO_SUB_ATTR_INDEX.EQUIP_STAR:
                    values.push(this.equipStar); 
                    break;
            }
        }
        return values;
    }
}

abstract class EquipAllAttr {
    hid: number;
    eplaceId: number;
    attrId: number;
    equipQuality: number = 0;
    equipStrength: number = 0;
    equipStar: number = 0;
    jewel: number = 0;
    stone: number = 0;

    constructor(hid: number, attrId: number) {
        this.hid = hid;
        this.attrId = attrId;
    }

    abstract setByRoleCe(data: AttrCell): void;
    abstract getValues(): number[];

    public getEquipAttrCell() {
        return {
            attrId: this.attrId,
            values: this.getValues()
        }
    }
}

class EquipMainAttr extends EquipAllAttr {
    public setByRoleCe(equipAttr: AttrCell) {
        for(let i = 0; i < equipAttr.values.length; i++) {
            let value = equipAttr.values[i];
            if(value != undefined) {
                switch(i) {
                    case EQUIP_MAIN_ATTR_INDEX.EQUIP_QUALITY:
                        this.equipQuality = value; break;
                    case EQUIP_MAIN_ATTR_INDEX.EQUIP_STRENGTH:
                        this.equipStrength = value; break;
                    case EQUIP_MAIN_ATTR_INDEX.EQUIP_STAR:
                        this.equipStar = value; break;
                    case EQUIP_MAIN_ATTR_INDEX.JEWEL:
                        this.jewel = value; break;
                    case EQUIP_MAIN_ATTR_INDEX.STONE:
                        this.stone = value; break;
                }
            }
        }
    }

    public getValues() {
        let values: number[] = [];
        for(let i = EQUIP_MAIN_ATTR_INDEX.START; i < EQUIP_MAIN_ATTR_INDEX.END; i++) {
            switch(i) {
                case EQUIP_MAIN_ATTR_INDEX.EQUIP_QUALITY:
                    values.push(this.equipQuality); 
                    break;
                case EQUIP_MAIN_ATTR_INDEX.EQUIP_STRENGTH:
                    values.push(this.equipStrength); 
                    break;
                case EQUIP_MAIN_ATTR_INDEX.EQUIP_STAR:
                    values.push(this.equipStar); 
                    break;
                case EQUIP_MAIN_ATTR_INDEX.JEWEL:
                    values.push(this.jewel); 
                    break;
                case EQUIP_MAIN_ATTR_INDEX.STONE:
                    values.push(this.stone); 
                    break;
            }
        }
        return values;
    }
}

class EquipSubAttr extends EquipAllAttr {
    public setByRoleCe(equipAttr: AttrCell) {
        for(let i = 0; i < equipAttr.values.length; i++) {
            let value = equipAttr.values[i];
            if(value != undefined) {
                switch(i) {
                    case EQUIP_SUB_ATTR_INDEX.EQUIP_STAR:
                        this.equipStar = value; break;
                    case EQUIP_SUB_ATTR_INDEX.JEWEL:
                        this.jewel = value; break;
                }
            }
        }
    }

    public getValues() {
        let values: number[] = [];
        for(let i = EQUIP_SUB_ATTR_INDEX.START; i < EQUIP_SUB_ATTR_INDEX.END; i++) {
            switch(i) {
                case EQUIP_SUB_ATTR_INDEX.EQUIP_STAR:
                    values.push(this.equipStar); 
                    break;
                case EQUIP_SUB_ATTR_INDEX.JEWEL:
                    values.push(this.jewel); 
                    break;
            }
        }
        return values;
    }
}

enum GLOBAL_MAIN_ATTR_INDEX {
    START,
    SCHOOL = 0,    // hp5，百家学宫百分比加成（dic_zyz_schoolRate的mainAttrApercent）
    TERAPH = 1,     // hp11, 神像加成(直接读数据库teraph字段的值)
    TITLE = 2,      // hp11, 爵位加成(dic_zyz_title的hp)
    SCROLL = 3,     // hp11，名将谱加成(dic_zyz_heroScroll的hp)
    SKIN = 4,       // hp12, 皮肤加成(dic_zyz_fashion的actorAttr)
    END
}

enum GLOBAL_SUB_ATTR_INDEX {
    START,
    SCHOOL = 0,     // attr5, 百家学宫(根据dic_zyz_school中的upAttribute决定书院加的是那些次级属性，具体值读取dic_zyz_schoolRate中的assiAttrAddValue一列)
    TERAPH = 1,     // attr4, 神像加成(dic_zyz_teraph中的assistAttrValue)
    TITLE = 2,      // attr6, 爵位（dic_zyz_title中的pdi、mdi）
    SKIN = 3,       // attr8, 皮肤
    END
}

enum HERO_MAIN_ATTR_INDEX {
    START,
    BASE = 0,     // hp1, 角色基础属性(dic_zyz_hero的hp)
    JOB = 1,      // hp1, 职业属性(dic_zyz_job的attr)
    STAR_UP = 2,  // hp2, 角色升星成长(dic_zyz_hero_star的hp或dic_zyz_hero_wake的hp)
    CONNECT = 3,  // hp3, 角色羁绊固定值(dic_zyz_friend_ship的attribute)
    FAVOUR = 4,   // hp4, 声望加成(dic_zyz_friend_ship_level的add)
    TALENT = 5,   // hp5, 天赋树百分比加成(dic_zyz_hero_talent的levelSeid，然后对应到dic_zyz_se)
    EQUIP_QUALITY = 6, // hp6, 装备品质基础值(dic_zyz_equipQuality的attribute)
    EQUIP_STRENGTH = 7, // hp7, 装备强化值(dic_zyz_equipStrenthAttr的attr)
    EQUIP_STAR = 8,   // hp8，装备升星(dic_zyz_equipStar的mainAttr)
    EQUIP_SUIT = 9,   // hp8，套装(dic_zyz_equipSuit的effect，然后对应dic_zyz_se)
    JEWEL = 10,       // hp9，天晶随机属性值(jewel的ranSe，对应dic_zyz_randomEffectPool)
    STONE = 11,    // hp10, 地玉增加的固定值(dic_zyz_stone的attribute)
    BASE_UP = 12,   // hp2, 角色基础属性成长(dic_zyz_hero的hp_up)
    ARTIFACT_LV = 13, // hp14, 宝物等级
    ARTIFACT_QUALITY = 14, // hp14, 宝物品质
    ARTIFACT_SEID = 15,    // hp9，宝物词条
    END
}

enum HERO_SUB_ATTR_INDEX {
    START,
    BASE = 0,      // attr1，系统参数表中所有人
    JOB = 1,       // attr2, 职业的attr
    TALENT = 2,    // attr3, 天赋树百分比加成 (dic_zyz_hero_talent的levelSeid，然后对应到dic_zyz_se)
    JEWEL = 3,     // attr7, 天晶随机属性(随机出来的值在dic_zyz_randomEffectPool中读最终值)
    EQUIP_STAR = 4, // attr9, 精炼次级属性(dic_zyz_equipStar的subAttr)
    END
}

enum EQUIP_MAIN_ATTR_INDEX {
    START,
    EQUIP_QUALITY = 0, // hp6, 装备升品基础值加成(dic_zyz_equipQuality)
    EQUIP_STRENGTH = 1,// hp7, 装备强化值(需改表，改为键值对)
    EQUIP_STAR = 2,    // hp8, 装备(dic_zyz_equipStar)
    JEWEL = 3,         // hp9, 天晶洗练出的主属性百分比加成，equipAttr加起来
    STONE = 4,         // hp10，地玉石增加的固定值，equipAttr加起来
    END
}

enum EQUIP_SUB_ATTR_INDEX {
    START,
    JEWEL = 0,        // attr7, 天晶随机属性(随机出来的值在dic_zyz_randomEffectPool中读最终值)
    EQUIP_STAR = 1,   // attr9, 精炼次级属性(dic_zyz_equipStar的subAttr)
    END
}