/**
 * 体力系统
 */

import { HERO_SYSTEM_TYPE, ABI_TYPE, HERO_CE_RATIO, LINEUP_NUM } from '../consts';

import { cal, deepCopy, getAllAttrStage, reduceCe } from './util';
import { HeroModel, HeroType, HeroUpdate, CeAttrData } from '../db/Hero';
import { RoleModel, RoleType, RoleUpdate, CeAttrDataRole } from '../db/Role';
import { AttributeCal } from '../domain/roleField/attribute';
import { ABI_STAGE, SEID_TYPE } from '../consts';
import { gameData, getJobByGradeAndClass, getHeroWakeByQuality, getHeroStarByQuality, getFriendShipById, getSchoolRateByStar, getScollByStar, getTeraph, getDicSuitByTypeAndLv } from './data';
import { DicSe } from './dictionary/DicSe';
import { EquipType } from '../db/Equip';
import { DicRandomEffectPool } from './dictionary/DicRandomEffectPool';
import { SchoolModel } from '../db/School';
import { ABI_TYPE_MAIN, ABI_JOB_STAGE, ABI_STAGE_TO_TYPE } from '../consts/constModules/abilityConst'
import { PvpDefenseModel } from '../db/PvpDefense';
import { findIndex } from 'underscore';
import { GuildModel } from '../db/Guild';
import { DicJob } from './dictionary/DicJob';
import { DicSuit } from './dictionary/DicSuit';

// 修改并下发战力
export async function calPlayerCeAndSave(type: number, roleId: string, originHero: HeroType, update: HeroUpdate, args?: Array<number>) {
    let role = await RoleModel.findByRoleId(roleId);

    let { attr: roleAttrs = [], serverId } = role;

    let heroAttrs = calPlayerCe(originHero, update, type, args); // 根据操作计算attr的增加

    let newAttr = new AttributeCal();
    newAttr.setLv(update.lv||originHero.lv);
    newAttr.setByDbData(roleAttrs, heroAttrs);
    let heroCe = newAttr.calCe(); // 计算最终战力
    let incCe = heroCe - originHero.ce;

    // 更新到武将
    update.attr = heroAttrs;
    update.ce = heroCe;
    if (originHero.historyCe < heroCe) update.historyCe = heroCe;
    let hero = await HeroModel.updateHeroInfo(roleId, originHero.hid, update, null, true); // 更新武将 返回是战力缩过的
    
    // 更新到角色
    let { topLineup, topLineupCe } = await calculatetopLineup(role, originHero.hid, heroCe, originHero._id); // 计算更新最强五人战力
    role = await RoleModel.updateRoleInfo(roleId, { ce: role.ce + incCe, topLineup, topLineupCe });

    await PvpDefenseModel.updateCe(roleId, originHero.hid, incCe); // 更新pvp防守阵战力
    let guild = await GuildModel.updateCe(roleId, incCe); // 公会更新战力

    let pushHeros = new Array<{ hid: number, ce: number, incHeroCe: number }>();
    pushHeros.push({
        hid: originHero.hid,
        ce: reduceCe(heroCe),
        incHeroCe: reduceCe(incCe),
    });
    return { pushHeros, role, topLineupCe, hero, guild, serverId }
}

//全局属性加成
export async function reCalAllHeroCe(type: number, roleId: string, update: RoleUpdate, args?: Array<number>) {
    let role = await RoleModel.findByRoleId(roleId);
    let heros = await HeroModel.findByRole(roleId);

    let roleAttrs = await reCalRoleAttr(type, heros, role, update, args);
    if(!roleAttrs) return {role, pushHeros: [], ce: role.ce, topLineupCe: role.topLineupCe, serverId: role.serverId, heros }; // 无加成

    let pushHeros = new Array<{ hid: number, ce: number, incHeroCe: number }>();

    let roleCe = 0; // 总战力加成
    let allIncCe = 0;
    for (let hero of heros) {
        let { attr: heroAttrs, lv } = hero;
        let newAttr = new AttributeCal();
        newAttr.setLv(lv);
        newAttr.setByDbData(roleAttrs, heroAttrs);
        let heroCe = newAttr.calCe(); // 计算最终战力
        if(heroCe != hero.ce) {
            let incHeroCe = heroCe - hero.ce;
            allIncCe += incHeroCe;
            roleCe += heroCe;
            pushHeros.push({ hid: hero.hid, ce: reduceCe(heroCe), incHeroCe: reduceCe(incHeroCe) });
            await HeroModel.updateHeroInfo(roleId, hero.hid, { ce: heroCe });   
            await PvpDefenseModel.updateCe(roleId, hero.hid, incHeroCe); // 更新pvp防守阵战力
        }

    }
    let { topLineup, topLineupCe } = await calculatetopLineup(role); // 计算更新最强五人战力

    if(allIncCe != 0) {
        update.attr = roleAttrs;
        update.ce = roleCe;
        update.topLineup = topLineup;
        update.topLineupCe = topLineupCe;
    }
    role = await RoleModel.updateRoleInfo(roleId, update);
    
    let guild = await GuildModel.updateCe(roleId, allIncCe); // 公会更新战力
    return { role, pushHeros, ce: role.ce, topLineupCe: role.topLineupCe, guild, serverId: role.serverId, heros }

}

// 计算武将全局战力
async function reCalRoleAttr(type: number, heros: Array<HeroType>, role: RoleType, update: RoleUpdate, args: Array<number>) {
    let { attr: roleAttrs } = role;
    switch (type) {
        case HERO_SYSTEM_TYPE.ADD_SKIN:
            roleAttrs = calHeroAddSkin(role, args);
            break;
        case HERO_SYSTEM_TYPE.SCHOOL:
            roleAttrs = calSchoolAddAttr(role, heros, args[0], args[1], args[2]);
            break;
        case HERO_SYSTEM_TYPE.SCROLL:
            roleAttrs = calScrollAddAttr(role, heros, args[0]);
            break;
        case HERO_SYSTEM_TYPE.STAR:
        case HERO_SYSTEM_TYPE.COLORSTAR:
        case HERO_SYSTEM_TYPE.QUALITY:
            roleAttrs = await calSchoolStarIncAttr(role, heros, type, args[0], args[1]);
            break;
        case HERO_SYSTEM_TYPE.TITLE:
            roleAttrs = calTitle(role, update);
            break;
        case HERO_SYSTEM_TYPE.TERAPH:
            roleAttrs = calTeraphMainAttr(role, update, args[0]);
            break;
        case HERO_SYSTEM_TYPE.TERAPH_UP:
            roleAttrs = calTeraphAssistAttr(role, update, args[0]);
            break;
    }
    
    return roleAttrs;
}

// 计算单个武将战力
export function calPlayerCe(hero: HeroType, update: HeroUpdate, type: number, args: Array<number> = []) {
    let heroAttrs: CeAttrData[] = []; // {"hp": {"base": number, "fixUp": number, "ratioUp": number}}

    let addSeidList = new Array<number>();
    let removeSeidList = new Array<number>();

    switch (type) {
        case HERO_SYSTEM_TYPE.STAR:
        case HERO_SYSTEM_TYPE.LVUP:
        case HERO_SYSTEM_TYPE.COLORSTAR:
        case HERO_SYSTEM_TYPE.QUALITY:
            heroAttrs = calHeroStarIncAttr(hero, update, type, addSeidList, removeSeidList); // args: 升的星盘
            break;
        case HERO_SYSTEM_TYPE.TRAIN:
            heroAttrs = calHeroTrainIncAttr(hero, update);
            break;
        case HERO_SYSTEM_TYPE.STAGEUP:
            heroAttrs = calHeroJobStageUpIncAttr(hero, update, addSeidList, removeSeidList);
            break;
        case HERO_SYSTEM_TYPE.SKIN:
            heroAttrs = calHeroWearSkinIncAttr(hero, update, addSeidList, removeSeidList);
            break;
        case HERO_SYSTEM_TYPE.FAVOUR:
            heroAttrs = calHeroFavourUpIncAttr(hero, update);
            break;
        case HERO_SYSTEM_TYPE.CONNECT:
            heroAttrs = calHeroConectIncAttr(hero, update, args[0]);
            break;
        case HERO_SYSTEM_TYPE.EQUIP:
            heroAttrs = calEquipPutOnOffIncAttr(hero, args, addSeidList, removeSeidList);
            break;
        case HERO_SYSTEM_TYPE.EQUIP_BASE:
            heroAttrs = calHeroEquipIncAttr(hero);
            break;
        case HERO_SYSTEM_TYPE.RESTRENGTHEN:
            heroAttrs = calRestrengthenIncAttr(hero, args.shift(), args, addSeidList, removeSeidList);
            break;
        case HERO_SYSTEM_TYPE.JEWEL_ON:
            heroAttrs = calHeroCeWhenJewelOnOrOff(hero, args[0], args[1]);
            break;
        case HERO_SYSTEM_TYPE.JEWEL_OFF:
            heroAttrs = calHeroCeWhenJewelOnOrOff(hero, 0, args[0]);
            break;
        case HERO_SYSTEM_TYPE.SCROLL:
            heroAttrs = calHeroCeScrollIncAttr(hero, update);
            break;
        default:
            break;
    }
    addSeidEffect(heroAttrs, addSeidList, removeSeidList); // 处理加值
    
    return heroAttrs;
}

/**
 * 计算最强阵容战力
 * @param role 
 * @param hid 
 * @param ce 
 * @param heroId 
 */
export async function calculatetopLineup(role: RoleType, hid?: number, ce?: number, heroId?: string) {
    let topLineup = role?.topLineup || new Array();
    if(!hid) { // 直接重新排
        let heroes = await HeroModel.getTopHero(role.roleId, LINEUP_NUM);
        topLineup = heroes.map(cur => { return { hid: cur.hid, ce: cur.ce, hero: cur._id } });
    } else {
        topLineup.sort((a, b) => { return b.ce - a.ce }); // 0-6，最大-最小
        let index = topLineup.findIndex(cur => cur.hid == hid);
        if(index != -1 && !heroId) {
            let heroes = await HeroModel.getTopHero(role.roleId, LINEUP_NUM);
            topLineup = heroes.map(cur => { return { hid: cur.hid, ce: cur.ce, hero: cur._id } });
        } else {
            if (index == -1) { // 不在最强列表
                if (topLineup.length < LINEUP_NUM) { // 不满6人
                    topLineup.push({ hid, ce, hero: heroId });
                } else if (topLineup.length == LINEUP_NUM) {
                    if (ce > topLineup[topLineup.length - 1].ce) { // 跻身最强6人
                        topLineup.pop();
                        topLineup.push({ hid, ce, hero: heroId });
                    }
                } else {
                    topLineup.splice(LINEUP_NUM, topLineup.length - LINEUP_NUM);
                }
            } else { // 原来就是最强6人
                if (ce < topLineup[topLineup.length - 1].ce) { // 滑出最强
                    let heroes = await HeroModel.getTopHero(role.roleId, LINEUP_NUM);
                    topLineup = heroes.map(cur => { return { hid: cur.hid, ce: cur.ce, hero: cur._id } });
                } else {
                    topLineup[index].ce = ce;
                }
            }
        }
    }

    let topLineupCe = topLineup.reduce((pre, cur) => {
        return pre + cur.ce
    }, 0);

    return { topLineup, topLineupCe };
}

/**
 * 添加皮肤全局加成
 * @param args 
 * @param ceAttr 
 */
function calHeroAddSkin(role: RoleType, skinIds: Array<number>) {
    let { attr: roleAttrs } = role;

    for (let id of skinIds) {
        let addSkin = gameData.fashion.get(id);
        for (let attr of addSkin.globalAttr) {
            let fixUp = attr.number * HERO_CE_RATIO;
            updateRoleAttr(roleAttrs, attr.id, { inc: { fixUp } });
        }
    }
    role.attr = roleAttrs;
    return roleAttrs;
}

/**
 * 升星觉醒升品等
 * @param {HeroType} hero  武将更新前的值
 * @param {HeroUpdate} update 更新的值
 * @param {number} type  养成类型
 * @param {number[]} addSeidList 用于更新被动
 * @param {number[]} removeSeidList 用于更新被动
 */
export function calHeroStarIncAttr(originHero: HeroType, update: HeroUpdate, type: number, addSeidList: Array<number>, removeSeidList: Array<number>) {
    let { star: originStar, starStage: originStarStage, quality: originQuality, colorStar: originColorStar, colorStarStage: originColorStarStage, attr: heroAttrs, skinId: originSkinId, lv: oldLv } = originHero;
    let { star = originStar, starStage = originStarStage, quality = originQuality, colorStar = originColorStar, colorStarStage = originColorStarStage, skinId = originSkinId, lv = oldLv } = update;

    const dicHero = gameData.hero.get(skinId);

    const isWake = colorStar > 0; // 是否觉醒，只要激活了觉醒，彩星就会 > 1
    const isFirstWake = colorStar > 0 && originColorStar <= 0; // 是否是初次觉醒
    const isUpStar = originStar != star || originColorStar != colorStar; // 是否有升星
    // console.log('*isUpstar', isUpStar, originStar, star, originColorStar, colorStar)
    if(starStage == ABI_STAGE.START) star--;
    if(colorStarStage == ABI_STAGE.START) colorStar--;

    const dicStar = isWake ? getHeroWakeByQuality(dicHero.jobClass, dicHero.quality, colorStar) : getHeroStarByQuality(dicHero.jobClass, quality, star); // 星级表


    let stages = new Array<number>(); // 需要升级的阶
    if (type == HERO_SYSTEM_TYPE.LVUP) {
        stages = getAllAttrStage();
    } else if (type == HERO_SYSTEM_TYPE.STAR) {
        let end = isUpStar? ABI_STAGE.END: starStage;
        if(end < originStarStage) {
            stages = getAllAttrStage();
        } else {
            for(let i = originStarStage; i < end; i++) {
                stages.push(i + 1);
            }
        }
    } else if (type == HERO_SYSTEM_TYPE.QUALITY) {
        stages = getAllAttrStage();
    } else if (type = HERO_SYSTEM_TYPE.COLORSTAR) {
        if (isFirstWake) { // 首次觉醒
            stages = getAllAttrStage();
        } else {
            let end = isUpStar? ABI_STAGE.END: colorStarStage;
            if(end < originColorStarStage) {
                stages = getAllAttrStage();
            } else {
                for(let i = originColorStarStage; i < end; i++) {
                    stages.push(i + 1);
                }
            }
        }
    } else if (type == HERO_SYSTEM_TYPE.SKIN) {
        stages = getAllAttrStage();
    }
    for (let stage of stages) {

        let targetAttrId = ABI_STAGE_TO_TYPE.get(stage); // 转换为18维的属性id 
        let heroAttr = dicHero.baseAbilityArr.get(targetAttrId); // 武将表hp等
        let heroUpAttr = dicHero.baseAbilityUpArr.get(targetAttrId); // 武将表hp_up等
        let starUp = 0; // 星级成长
        if (!!dicStar && !!dicStar.ceAttr) {
            starUp = dicStar.ceAttr.get(stage);
        }
        let newBase = (heroAttr + lv * (heroUpAttr + starUp)) * HERO_CE_RATIO;
        updateHeroAttr(heroAttrs, targetAttrId, { set: { base: newBase } });
    }

    let curSeidList = getSeidListOfFashion(skinId, star, colorStar);
    let preSeidList = getSeidListOfFashion(originSkinId, originStar, originColorStar);

    curSeidList.forEach((seid, type) => {
        if (!preSeidList.has(type)) {
            addSeidList.push(seid, 0);
        }
    });
    preSeidList.forEach((seid, type) => {
        if (!curSeidList.has(type)) {
            removeSeidList.push(seid, 0);
        }
    });

    originHero.attr = heroAttrs;
    return heroAttrs;//属性增量可以是多个
}

type updateCeAttr = Partial<CeAttrData>;
function updateHeroAttr(heroAttrs: CeAttrData[], id: number, update: { inc?: updateCeAttr, set?: updateCeAttr }) {
    let index = heroAttrs.findIndex(cur => cur.id == id);
    let curAttr = heroAttrs[index];
    if(!curAttr) {
        curAttr = new CeAttrData(id);
        heroAttrs.push(curAttr);
        index = heroAttrs.length - 1;
    }
    if(update.inc) {
        let { base, equipUp, fixUp, ratioUp } = update.inc;
        if(base != undefined) curAttr.base += base;
        if(equipUp != undefined) curAttr.equipUp += equipUp;
        if(fixUp != undefined) curAttr.fixUp += fixUp;
        if(ratioUp != undefined) curAttr.ratioUp += ratioUp;
    }
    if(update.set) {
        let { base, equipUp, fixUp, ratioUp } = update.set;
        if(base != undefined) curAttr.base = base;
        if(equipUp != undefined) curAttr.equipUp = equipUp;
        if(fixUp != undefined) curAttr.fixUp = fixUp;
        if(ratioUp != undefined) curAttr.ratioUp = ratioUp;
    }
    if(curAttr.base <= 0 && curAttr.equipUp <=0 && curAttr.fixUp <=0 && curAttr.ratioUp <= 0) {
        heroAttrs.splice(index, 1);
    }
}

type updateCeAttrRole = Partial<CeAttrDataRole>;
function updateRoleAttr(roleAttrs: CeAttrDataRole[], id: number, update: { inc?: updateCeAttrRole, set?: updateCeAttrRole }) {
    let curAttr = roleAttrs.find(cur => cur.id == id);
    if(!curAttr) {
        curAttr = new CeAttrDataRole(id);
        roleAttrs.push(curAttr);
    }
    if(update.inc) {
        let { fixUp, ratioUp } = update.inc;
        if(fixUp) curAttr.fixUp = cal.add(curAttr.fixUp, fixUp);
        if(ratioUp) curAttr.ratioUp = cal.add(curAttr.ratioUp, ratioUp);
    }
    if(update.set) {
        let { fixUp, ratioUp } = update.set;
        if(fixUp) curAttr.fixUp = fixUp;
        if(ratioUp) curAttr.ratioUp = ratioUp;
    }
}
/**
 * 获取皮肤上的seid
 * @param skinId 
 * @param originStar 
 * @param originColorStar 
 */
function getSeidListOfFashion(skinId: number, originStar: number, originColorStar: number) {
    let seidList = new Map<number, number>(); // type => seid
    let dicHero = gameData.hero.get(skinId);

    let { starSeidArr, colorStarSeidArr } = gameData.heroSkill.get(dicHero.skill);
    for (let { star, value, type } of starSeidArr) {
        if (originStar >= star) {
            seidList.set(type, value);
        }
    }
    for (let { star, value, type } of colorStarSeidArr) {
        if (originColorStar >= star) {
            seidList.set(type, value);
        }
    }
    return seidList
}

/**
 * 兵种训练
 * @param {HeroType} originHero 原始武将
 * @param {HeroUpdate} update 更新的数据
 */
export function calHeroTrainIncAttr(originHero: HeroType, update: HeroUpdate) {
    let { attr: heroAttrs, job: oldJob, jobStage: oldJobStage } = originHero;
    let { job = oldJob, jobStage = oldJobStage } = update;

    let dicJob = gameData.job.get(job);
    let lastJob = getJobByGradeAndClass(dicJob.job_class, dicJob.grade - 1);
    let dicLastJob = lastJob? gameData.job.get(lastJob.jobid): null;
    
    for(let i = 1; i <= dicJob.maxStage; i++) {
        if(oldJobStage < i && jobStage >= i) {
            let lastAttr = dicLastJob? dicLastJob.ceAttr.get(i).attr: 0;
            let targetAttrId = dicJob.ceAttr.get(i).id;
            let targetAttrValue = dicJob.ceAttr.get(i).attr;
            let inc = (targetAttrValue - lastAttr) * HERO_CE_RATIO;
            // console.log('*******', targetAttrId, targetAttrValue, lastAttr, inc )
            updateHeroAttr(heroAttrs, targetAttrId, { inc: { fixUp: inc } });
        }
    }
    return heroAttrs;
}

/**
 * 兵种进阶
 * @param {HeroType} originHero 原始武将
 * @param {HeroUpdate} update 更新内容
 * @param {number[]} addSeidList 用于更新被动
 * @param {number[]} removeSeidList 用于更新被动
 */
export function calHeroJobStageUpIncAttr(originHero: HeroType, update: HeroUpdate, addSeidList: Array<number>, removeSeidList: Array<number>) {
    let { job: oldJob, attr: heroAttrs } = originHero;
    let { job = oldJob } = update;

    let lastJob = gameData.job.get(oldJob);
    let currentJob = gameData.job.get(job);
    let lastSeids = lastJob?.seid||new Array<number>();
    let curSeids = currentJob?.seid||new Array<number>();

    for (let seid of curSeids) {
        let index = findIndex(lastSeids, seid);
        if (index < 0) {
            addSeidList.push(seid, 0);
        }
    }
    for (let seid of lastSeids) {
        let index = findIndex(curSeids, seid);
        if (index < 0) {
            removeSeidList.push(seid, 0);
        }
    }
    return heroAttrs;
}

/**
 * 初始计算兵种属性
 * @param {HeroType} originHero 原始武将
 * @param {HeroUpdate} hero 更新后的武将
 * @param {number[]} addSeidList 用于更新被动
 * @param {number[]} removeSeidList 用于更新被动
 */
export function calHeroJobAttr(originHero: HeroType, hero: HeroUpdate, addSeidList: Array<number>, removeSeidList: Array<number>) {
    let { attr: heroAttrs } = originHero;
    let currentJob = gameData.job.get(hero.job);
    let jobGradeAndClass = getJobByGradeAndClass(currentJob.job_class, currentJob.grade - 1);
    let lastJob: DicJob;
    if (!!jobGradeAndClass) {
        lastJob = gameData.job.get(jobGradeAndClass.jobid);
    }

    for (let stage = ABI_JOB_STAGE.START; stage <= ABI_JOB_STAGE.END; stage++) {
        if(hero.jobStage >= stage) {
            let targetAttrId = currentJob.ceAttr.get(stage).id;
            let fixUp = currentJob.ceAttr.get(stage).attr * HERO_CE_RATIO;
            updateHeroAttr(heroAttrs, targetAttrId, { inc: { fixUp }})
        } else {
            if(lastJob) {
                let targetAttrId = lastJob.ceAttr.get(stage).id;
                let fixUp = lastJob.ceAttr.get(stage).attr * HERO_CE_RATIO;
                updateHeroAttr(heroAttrs, targetAttrId, { inc: { fixUp } });
            }
        }
    };

    originHero.attr = heroAttrs;
    heroAttrs = calHeroJobStageUpIncAttr(originHero, hero, addSeidList, removeSeidList);
    return heroAttrs;
}

/**
 * 穿戴时装的单武将加成
 * @param {HeroType} originHero 原始武将
 * @param {number} wearSkinId 穿戴的皮肤
 * @param {number} pullSkinId 脱下的皮肤
 * @param {number[]} addSeidList 用于更新被动
 * @param {number[]} removeSeidList 用于更新被动
 * @param {boolean} isInit 是否是计算初始战力
 */
export function calHeroWearSkinIncAttr(originHero: HeroType, update: HeroUpdate, addSeidList: number[], removeSeidList: number[]) {
    let { attr: heroAttrs, skinId: originSkinId } = originHero;
    let { skinId = originSkinId } = update;
    calHeroStarIncAttr(originHero, update, HERO_SYSTEM_TYPE.SKIN, addSeidList, removeSeidList);

    let addSkin = gameData.fashionBySkinId.get(skinId);
    let delSkin = gameData.fashionBySkinId.get(originSkinId);

    if (delSkin) {
        for (let attr of delSkin.actorAttr) {
            let fixUp = -1 * attr.number * HERO_CE_RATIO;
            updateHeroAttr(heroAttrs, attr.id, { inc: { fixUp } });
        }
    }
    for (let attr of addSkin.actorAttr) {
        let fixUp = attr.number * HERO_CE_RATIO;
        updateHeroAttr(heroAttrs, attr.id, { inc: { fixUp } });
    }
    originHero.attr = heroAttrs;
    return heroAttrs;
}

/**
 * 羁绊解锁
 * @param {HeroType} originHero 原始武将 
 * @param {HeroUpdate} update 更新数据
 * @param {number} shipId 解锁的那个id
 */
export function calHeroConectIncAttr(originHero: HeroType, update: HeroUpdate, shipId: number) {
    let { connections: oldConnections = [], favourLv, attr: heroAttrs } = originHero;
    let { connections = oldConnections } = update;

    let oldConnect = oldConnections.find(cur => cur.shipId == shipId);
    let connect = connections.find(cur => cur.shipId == shipId);

    let oldLevel = oldConnect?.level||0;
    let level = connect?.level||0;

    let fiendShipLevel = gameData.friendShipLevelMap.get(favourLv);
    let currentShip = getFriendShipById(shipId, level);
    if (currentShip) {
        for (let attr of currentShip.attributes) {
            let fixUp = attr.number * (HERO_CE_RATIO + fiendShipLevel.add);
            updateHeroAttr(heroAttrs, attr.id, { inc: { fixUp } });
        }
    }
    if (level > 1) {
        let lastShip = getFriendShipById(shipId, oldLevel);
        if (lastShip) {
            for (let attr of lastShip.attributes) {
                let fixUp = -1 * attr.number * (HERO_CE_RATIO + fiendShipLevel.add);
                updateHeroAttr(heroAttrs, attr.id, { inc: { fixUp } })
            }
        }
    }
    originHero.attr = heroAttrs;
    return heroAttrs;
}

/**
 * 好感度升级，影响羁绊加成
 * 
 * @param {HeroType} originHero 原始武将数据
 * @param {HeroUpdate} update 更新的数据
 */
export function calHeroFavourUpIncAttr(originHero: HeroType, update: HeroUpdate) {
    let { favourLv: oldFavourLv, attr: heroAttrs } = originHero;
    let { favourLv = oldFavourLv } = update;

   
    let currentFiendShipLevel = gameData.friendShipLevelMap.get(favourLv);
    let difAdd = currentFiendShipLevel.add;
    if (oldFavourLv && oldFavourLv > 0) { // 减上一级好感
        let lastFiendShipLevel = gameData.friendShipLevelMap.get(oldFavourLv);
        difAdd -= lastFiendShipLevel.add;
    }

    for (let {shipId, level} of originHero.connections) {
        let dicHeroFriendShip = getFriendShipById(shipId, level);
        for (let attr of dicHeroFriendShip.attributes) {
            let fixUp = (attr.number + difAdd ) * HERO_CE_RATIO;
            updateHeroAttr(heroAttrs, attr.id, { inc: { fixUp } });
        }
    }
    originHero.attr = heroAttrs;
    return heroAttrs;
}

/**
 * 穿脱, removeSeidList原来身上穿着的所有装备的seid，包括套装的
 * @param {HeroType} hero 武将
 * @param {number[]} seids args 原来穿着的装备的seid
 * @param {number[]} addSeidList 用于更新被动
 * @param {number[]} removeSeidList 用于更新被动
 */
export function calEquipPutOnOffIncAttr(hero: HeroType, seids: Array<number>, addSeidList: Array<number>, removeSeidList: Array<number>) {
    // 计算身上所有装备的战力值（特技相关以外）
    let heroAttrs = calHeroEquipIncAttr(hero);

    // 计算被动技能
    let resultSeid = calEquipSeids(hero);
    for(let seid of resultSeid) {
        addSeidList.push(seid);
    }

    for (let seid of seids) {
        removeSeidList.push(seid)
    }

    return heroAttrs
}

/**
 * 计算一个武将身上的所有被动seid
 * @param {HeroType} hero 武将，equip需要populate
 */
export function calEquipSeids(hero: HeroType) {
    let seids: number[] = [];
    // 计算被动技能
    let { ePlace } = hero;
    let suits = new Map<number, Map<number, { dic: DicSuit, count: number }>>(); // suitType => starLevel => DicSuit

    for (let { equip } of ePlace) {
        if (equip) {
            let e = <EquipType>equip;
            if (!!e.randSe) {
                for (let { seid, rand } of e.randSe) {
                    seids.push(seid, rand);
                }
            }
            if (e.suitId > 0) {
                let { suitType, starLevel } = gameData.suit.get(e.suitId);
                if (!suits.has(suitType)) {
                    suits.set(suitType, new Map<number, { dic: DicSuit, count: number }>());
                }
                for(let lv = 1; lv <= starLevel; lv++) {
                    let dicSuit = getDicSuitByTypeAndLv(suitType, lv); // 计算同type的低阶套装
                    if(dicSuit) {
                        if(!suits.get(suitType).has(lv)) {
                            suits.get(suitType).set(lv, { dic: dicSuit, count: 0 });
                        }
                        suits.get(suitType).get(lv).count ++;
                    }
                }

            }
        }
    }
    
    for(let [ _suitType, map ] of suits) {
        let effectSeid = new Map<number, { starLevel: number, seid: number }>();  // count => { starLevel, seid }
        for(let [ starLevel, { dic: { effect }, count } ] of map) {
            for(let { count: effectCount, seid} of effect) {
                if(count >= effectCount ) { // 生效
                    if(!effectSeid.has(effectCount) || effectSeid.get(effectCount).starLevel < starLevel) { // 没有同数量效果
                        effectSeid.set(effectCount, { starLevel, seid });
                    }
                }
            }
        }
        for(let [_count, { seid }] of effectSeid) {
            seids.push(seid, 0);
        }
    }
    return seids;
}

/**
 * 装备，装备栏升级，装备精炼等涉及到值的
 * @param {HeroType} hero 装备更新过的武将
 */
export function calHeroEquipIncAttr(hero: HeroType) {
    let { ePlace, attr: heroAttrs } = hero;

    let setMap = new Map<number, number>();
    for (let { equip, lv, refineLv } of ePlace) {
        if (equip) {
            let e = <EquipType>equip;
            let dicGoods = gameData.goods.get(e.id);
            let { goodsAbility, goodsAbilityUp } = dicGoods;
            let dicRefine = gameData.refine.get(refineLv);

            let jewel = new Map<number, number>();
            for (let { jewel: jewelId } of e.holes) {
                if (jewelId > 0) {
                    let g = gameData.goods.get(jewelId);
                    if (g) {
                        let jGoods = g.goodsAbility;
                        jGoods.forEach((value, key) => {
                            if (!jewel.has(key)) {
                                jewel.set(key, value);
                            } else {
                                jewel.set(key, jewel.get(key) + value);
                            }
                        })
                    }
                }
            }

            let randMainMap = new Map<number, number>();
            for(let {id, rand} of (e.randMain||[])) {
                randMainMap.set(id, rand);
            }
            for (let i = ABI_TYPE.ABI_HP; i < ABI_TYPE.ABI_MAX; i++) {
                // console.log('***', i);
                let value1 = goodsAbility.get(i) || 0 * (HERO_CE_RATIO + e.randRange);
                // console.log('基础值', value1);
                let valueup = goodsAbilityUp.get(i) || 0;
                // console.log('成长', lv, valueup);
                let valueRefine = dicRefine ? dicRefine.upPercent : 0;
                // console.log('精炼', dicRefine?dicRefine.upPercent:0 );
                let valueJewel = jewel.get(i) || 0;
                // console.log('宝石', valueJewel);
                let valueGrade = randMainMap.get(i)||0;
                // console.log('品相', valueGrade)
                let attr = (value1 + lv * valueup) * valueGrade * (HERO_CE_RATIO + valueRefine)  + valueJewel * HERO_CE_RATIO * HERO_CE_RATIO;

                if(attr >= 0) {
                    // console.log('装备战力：', i, attr);
                    if(setMap.has(i)) {
                        setMap.set(i, setMap.get(i) + Math.floor(attr / HERO_CE_RATIO));
                    } else {
                        setMap.set(i, Math.floor(attr / HERO_CE_RATIO));
                    }
                }
            }
        }
        
    }

    for (let i = ABI_TYPE.ABI_HP; i < ABI_TYPE.ABI_MAX; i++) {
        let attr = setMap.get(i)||0;
        updateHeroAttr(heroAttrs, i, { set: { equipUp: attr } })
    }

    hero.attr = heroAttrs;
    return heroAttrs;
}

/**
 * 洗炼
 * @param {HeroType} hero 更新过的武将
 * @param {number} ePaceId 更新的栏位id
 * @param {number[]} seids 移除的seid
 * @param {number[]} addSeidList 用于更新被动
 * @param {number[]} removeSeidList 用于更新被动
 */
export function calRestrengthenIncAttr(hero: HeroType, ePaceId: number, seids: Array<number>, addSeidList: Array<number>, removeSeidList: Array<number>) {

    let { attr: heroAttrs } = hero;
    let { ePlace } = hero;

    let curPlace = ePlace.find(cur => cur.id == ePaceId);
    if (curPlace && curPlace.equip) {
        let e = <EquipType>curPlace.equip;
        for (let { seid, rand } of e.randSe) {
            addSeidList.push(seid, rand);
        }
    }

    for (let seid of seids) { 
        removeSeidList.push(seid) 
    }

    return heroAttrs
}

// 添加技能增加的被动属性
function addSeidEffect(heroAttrs: CeAttrData[], addSeidList: Array<number>, removeSeidList: Array<number>) {

    // console.log('addSeidList', addSeidList.join())
    // console.log('removeSeidList', removeSeidList.join())
    let otiginalSeidList = [
        { list: addSeidList, multi: 1 },
        { list: removeSeidList, multi: -1 }
    ];
    for (let { list, multi } of otiginalSeidList) {
        let effectList = new Array<DicSe>(); // any: dic_zyz_se表内容

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

        // console.log('effectList', JSON.stringify(effectList));
        for (let { type, gainValueArr: [ability, value] } of effectList) {
            if (type == SEID_TYPE.TYPE101) { // 加值
                updateHeroAttr(heroAttrs, ability, { inc: {fixUp: value * multi * HERO_CE_RATIO} });
            } else if (type == SEID_TYPE.TYPE103) { // 主属性加百分比
                if(ABI_TYPE_MAIN.includes(ability)) {
                    updateHeroAttr(heroAttrs, ability, { inc: {ratioUp: value / 1000 * multi} });
                }
            } else if (type == SEID_TYPE.TYPE104) { // 次级属性加百分比
                if(!ABI_TYPE_MAIN.includes(ability)) {
                    updateHeroAttr(heroAttrs, ability, { inc: {fixUp: value * 100 * multi * HERO_CE_RATIO } });
                }
            }
        }
    }

}

// 获取dic_zyz_se内容
function addSeid(effectList: Array<any>, seidId: number, rand: number, seidValue = new Array<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.TYPE999) {
        for (let i = 0; i < seidValue.length; i++) {
            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);
}

/**
 * 带上宝石
 * @param {HeroType} hero 武将数据
 * @param {number} id 带上的宝石
 * @param {number} oldId 脱下的宝石
 */
function calHeroCeWhenJewelOnOrOff(hero: HeroType, id: number, oldId: number) {
    let { attr: heroAttrs } = hero;
    let { goodsAbility } = gameData.goods.get(id)||{ goodsAbility: new Map<number, number>() };
    let { goodsAbility: oldGoodsAbility } = gameData.goods.get(oldId)||{ goodsAbility: new Map<number, number>() };
    
    let allIds: number[] = [];
    for(let [ id ] of goodsAbility) {
        allIds.push(id);
    }
    for(let [ id ] of oldGoodsAbility) {
        if(allIds.indexOf(id) == -1) allIds.push(id);
    }
    for(let id of allIds) {
        let value = goodsAbility.get(id)||0;
        let oldValue = oldGoodsAbility.get(id)||0;
        updateHeroAttr(heroAttrs, id, { inc: { equipUp: (value - oldValue) * HERO_CE_RATIO } })
    }

    hero.attr = heroAttrs;
    return heroAttrs;
}

/**
 * 全局加成，百家学宫
 * @param role 角色
 * @param heros 所有武将
 * @param schoolId 学宫学派
 * @param hid 换上的武将
 * @param preHid 撤下的武将
 */
function calSchoolAddAttr(role: RoleType, heros: HeroType[], schoolId: number, hid: number, preHid: number) {
    let { attr: roleAttrs } = role;
    let school = gameData.school.get(schoolId);
    if (!school) return null;

    let preHero = heros.find(cur => cur.hid == preHid);
    let curHero = heros.find(cur => cur.hid == hid);

    let defaultPercent = { mainAttrAPerent: 0, assiAttrAddValue: 0 };
    let preRate = preHero ? getSchoolRateByStar(preHero.star, preHero.colorStar, preHero.quality) : defaultPercent;
    let curRate = curHero ? getSchoolRateByStar(curHero.star, curHero.colorStar, curHero.quality) : defaultPercent;

    for (let attrId of school.upAttribute) {
        if(ABI_TYPE_MAIN.includes(attrId)) { // 主属性
            let ratioUp = curRate.mainAttrAPerent - preRate.mainAttrAPerent;
            updateRoleAttr(roleAttrs, attrId, { inc: { ratioUp } });
        } else {
            let fixUp = (curRate.assiAttrAddValue - preRate.assiAttrAddValue) * HERO_CE_RATIO;
            updateRoleAttr(roleAttrs, attrId, { inc: { fixUp } });
        }
    }

    return roleAttrs;
}

/**
 * 升星，觉醒，升品时，百家学宫配置武将会相应修改全局战力
 * @param {HeroType[]} heros 全部武将
 * @param {number} type 类型 HERO_SYSTEM_TYPE
 * @param {number} hid 更新的那个武将 
 * @param {number} isStarUp 是否升星 
 */
async function calSchoolStarIncAttr(role: RoleType, heros: HeroType[], type: number, hid: number, isStarUp: number) {
    let { roleId, attr: roleAttrs } = role;

    if ((type == HERO_SYSTEM_TYPE.STAR || type == HERO_SYSTEM_TYPE.COLORSTAR) && !isStarUp) {
        return null;
    }

    let hero = heros.find(cur => cur.hid == hid);
    if(!hero) return null;

    let curHeroInSchool = await SchoolModel.findByHid(roleId, hid);
    if (!curHeroInSchool) return null;

    let preStar = hero.star, preColorStar = hero.colorStar, preQuality = hero.quality;
    if (type == HERO_SYSTEM_TYPE.STAR && isStarUp) {
        preStar--;
    } else if (type == HERO_SYSTEM_TYPE.QUALITY) {
        preQuality--;
    } else if (type == HERO_SYSTEM_TYPE.COLORSTAR) {
        preColorStar--;
        if(preColorStar <= 0) preQuality--;
    } else {
        return null;
    }
    let school = gameData.school.get(curHeroInSchool.schoolId);
    let preRate = getSchoolRateByStar(preStar, preColorStar, preQuality);
    let curRate = getSchoolRateByStar(hero.star, hero.colorStar, hero.quality);

    for (let attrId of school.upAttribute) {
        if(ABI_TYPE_MAIN.includes(attrId)) { // 主属性
            let ratioUp = curRate.mainAttrAPerent - preRate.mainAttrAPerent;
            updateRoleAttr(roleAttrs, attrId, { inc: { ratioUp } });
        } else {
            let fixUp = (curRate.assiAttrAddValue - preRate.assiAttrAddValue) * HERO_CE_RATIO;
            updateRoleAttr(roleAttrs, attrId, { inc: { fixUp } });
        }
    }

    return roleAttrs;
}

/**
 * 全局加成， 名将谱
 * @param heros 所有武将
 * @param hid 激活的武将
 * @param ceAttr 
 */
function calScrollAddAttr(role: RoleType,heros: HeroType[], hid: number) {
    let { attr: roleAttrs } = role;

    let curHero = heros.find(cur => cur.hid == hid);
    let dicHero = gameData.hero.get(hid);
    if (!curHero || !dicHero) return roleAttrs;
    let { quality } = dicHero;
    let { star, quality: curQuality, colorStar } = curHero;
    let heroScroll = getScollByStar(quality, star, curQuality, colorStar);
    if (!heroScroll) return roleAttrs;

    let preScroll = gameData.preHeroScroll.get(heroScroll.id);
    heroScroll.ceAttr.forEach((add, attrId) => {

        let preAdd = preScroll ? preScroll.ceAttr.get(attrId) : 0;
        let fixUp = (add - preAdd) * HERO_CE_RATIO;
        updateRoleAttr(roleAttrs, attrId, { inc: { fixUp } });
    });
    return roleAttrs;
}

/**
 * 名将谱激活增加单个武将好感
 * @param originHero 原始武将
 * @param update 更新数据
 */
function calHeroCeScrollIncAttr(originHero: HeroType, update: HeroUpdate) {
    let { attr: heroAttrs, favourLv: oldFavourLv } = originHero;
    let { favourLv = oldFavourLv } = update; 
    if (favourLv != oldFavourLv) {
        heroAttrs = calHeroFavourUpIncAttr(originHero, update);
    }
    return heroAttrs;
}

/**
 * 升爵位
 * @param role 角色数据
 * @param titleId 升到哪个爵位
 */
function calTitle(role: RoleType, update: RoleUpdate) {
    let { title: oldTitle, attr: roleAttrs } = role;
    let { title: newTitle = oldTitle } = update;

    let dicOldTitle = gameData.title.get(oldTitle)||{ mainAttrValue: new Map(), assiAttrValue: new Map() };
    let dicNewTitle = gameData.title.get(newTitle);

    for (let i = ABI_TYPE.ABI_HP; i < ABI_TYPE.ABI_MAX; i++) {
        if (dicNewTitle.mainAttrValue.has(i) || dicOldTitle.mainAttrValue.has(i)) {
            let fixUp = ((dicNewTitle.mainAttrValue.get(i) || 0) - (dicOldTitle.mainAttrValue.get(i) || 0)) * HERO_CE_RATIO;
            updateRoleAttr(roleAttrs, i, { inc: { fixUp } });
        }
        if (dicNewTitle.assiAttrValue.has(i) || dicOldTitle.mainAttrValue.has(i)) {
            let fixUp = ((dicNewTitle.assiAttrValue.get(i) || 0) - (dicOldTitle.assiAttrValue.get(i) || 0)) * HERO_CE_RATIO;
            updateRoleAttr(roleAttrs, i, { inc: { fixUp } });
        }
    }
    return roleAttrs;
}


/**
 * 神像强化，更新主属性加成
 * @param role 角色数据
 * @param update 更新数据
 * @param id 更新哪座神像
 */
function calTeraphMainAttr(role: RoleType, update: RoleUpdate, id: number) {
    let { attr: roleAttrs, teraphs: oldTeraphs = [] } = role;
    let { teraphs = oldTeraphs } = update;

    let oldTeraph = oldTeraphs.find(cur => cur.id == id);
    let teraph = teraphs.find(cur => cur.id == id);
    if(teraph && teraph.attr) {
        for(let [attrId, val] of teraph.attr) {
            let oldVal = 0;
            if(oldTeraph && oldTeraph.attr && oldTeraph.attr.has(attrId)) {
                oldVal = oldTeraph.attr.get(attrId);
            }

            let fixUp = (val - oldVal) * HERO_CE_RATIO;
            updateRoleAttr(roleAttrs, attrId, { inc: {fixUp} })
        }
    }
    return roleAttrs;
}


/**
 * 神像进阶，更新次级属性加成
 * @param role 角色数据
 * @param update 更新数据
 * @param id 更新哪座神像
 */
function calTeraphAssistAttr(role: RoleType, update: RoleUpdate, id: number) {
    let { attr: roleAttrs, teraphs: oldTeraphs = [] } = role;
    let { teraphs = oldTeraphs } = update;

    let oldTeraph = oldTeraphs.find(cur => cur.id == id);
    let teraph = teraphs.find(cur => cur.id == id);

    let dicOldTeraph = getTeraph(id, oldTeraph?.grade);
    let dicTeraph = getTeraph(id, teraph?.grade);
    if(!dicTeraph) return null;

    dicTeraph.assiAttrValue.forEach((val, attrId) => {
        let oldVal = 0;
        if(dicOldTeraph && dicOldTeraph.assiAttrValue && dicOldTeraph.assiAttrValue.has(attrId)) {
            oldVal = dicOldTeraph.assiAttrValue.get(attrId);
        }

        let fixUp = (val - oldVal) * HERO_CE_RATIO;
        updateRoleAttr(roleAttrs, attrId, { inc: { fixUp } });
    });
    return roleAttrs;
}