
// 军团活动蛮夷入侵城门血量等数据存储

import { Member, GateMembersRec, WoodenHorse, Event } from "../domain/battleField/guildActivity";
import { GUILDACTIVITY } from "../pubUtils/dicParam";
import { RewardInter } from "../pubUtils/interface";
import { getRaceEventItems, gameData } from "../pubUtils/data";
import { GuildModel } from "../db/Guild";
import { sendSingleRaceActEndMsg } from "./guildActivityService";
import { REDIS_KEY, RACE_EVENT_TYPE, RACE_EVENT_EFFECT_TYPE, RACE_EVENT, STATUS, RACE_ACTIVITY_STATUS } from "../consts";
import { getGuildChannelSid } from "./chatService";
import { pinus } from "pinus";
import { getRandEelm, sortArrRandom, resResult } from "../pubUtils/util";
import { Rank } from "./rankService";
import { getTimeFun } from "../pubUtils/timeUtil";

export class GateActivityObject {
    private gateHp: Map<string, number> = new Map(); // 城门血条，每个军团有一条血条 guildCode => hp
    private members: Map<string, Array<Member>> = new Map(); // 每个军团参与的成员 guildCode => [{roleId, job}]
    private membersRecord: Map<string, GateMembersRec> = new Map(); // 每个成员的回合数和敌军数，防刷 recCode => GateMembersRec
    private guilds: Map<number, string[]> = new Map(); // 参加的所有军团 server => [guildCode]
    
    public getObj(guildCode: string, serverId: number) {
        // console.log(guildCode, serverId)
        return {
            gateHp: this.getGateHpAndInc(guildCode),
            members: this.members.get(guildCode)||[],
            memberesRecord: [...this.membersRecord],
            guilds: this.guilds.get(serverId)||[]
        }
    }

    public getGateHpAndInc(guildCode: string, inc: number = 0) {
        let gateHp = this.gateHp.get(guildCode);
        if(!this.gateHp.has(guildCode)) {
            gateHp = GUILDACTIVITY.GATEACTIVITY_GATEHP;
        }
        if(inc != 0) {
            gateHp += inc;
            this.gateHp.set(guildCode, gateHp);
        }
        if(gateHp <= 0) gateHp = 0;
        return gateHp
    }

    public getGuilds() {
        return this.guilds;
    }

    public pushMembers(guildCode: string, serverId: number, roleId: string, job: number) {

        if(this.members.has(guildCode)) {
            let members = this.members.get(guildCode);
            if(members.findIndex(cur => cur.roleId == roleId) == -1) {
                members.push({ roleId, job });
            }
        } else {
            let arr = new Array<string>();
            arr.push(roleId);
            this.members.set(guildCode, [{ roleId, job }]);
        }
        if(this.guilds.has(serverId)) {
            let arr = this.guilds.get(serverId);
            if(!arr.includes(guildCode)) arr.push(guildCode);
            this.guilds.set(serverId, arr);
        } else {
            this.guilds.set(serverId, [guildCode]);
        }

    }

    public getMemberRecord(recCode: string, roleId: string) {
        return this.membersRecord.get(recCode)||new GateMembersRec(roleId)
    }

    public setMemberRecord(recCode: string, newMemberRecord: GateMembersRec) {
        this.membersRecord.set(recCode, newMemberRecord);
    }

    public delMemberRecord(recCode: string) {
        this.membersRecord.delete(recCode);
    }

    public delGuildRecord(guildCode: string, serverId: number) {
        let guildList = this.guilds.get(serverId);
        if(guildList) {
            let index = guildList.indexOf(guildCode);
            guildList.splice(index, 1);
        }
        this.members.delete(guildCode);
    }
}

// 军团诸侯混战等数据
export class CityActivityObject {
    private gateHp: Map<string, number> = new Map(); // 城门血条，每个cityId有一条血条 serverId&cityId => gateHp
    private members: Map<string, Array<Member>> = new Map(); // 每个军团参与的成员 guildCode => [{roleId, job}]
    private cities: Map<string, string[]> = new Map(); // 各个城池参与的军团 serverId&cityId => [guildCode]
    private guilds: Map<string, number> = new Map();  // 军团所在的城池 guildCode => cityId
    private historyCity: Map<string, number> = new Map();  // 获取自己打开过的城池的页面 roleId => cityId

    private getKey(serverId: number, cityId: number) {
        return `${serverId}_${cityId}`;
    }

    private decodeKey(key: string) {
        let arr = key.split('_');
        return {
            serverId: parseInt(arr[0]),
            cityId: parseInt(arr[1])
        }
    }

    public getObj( serverId: number, cityId: number, guildCode: string) {
        let key = this.getKey(serverId, cityId);
        return {
            gateHp: this.gateHp.get(key),
            members: this.members.get(guildCode),
            city: this.cities.get(key),
            guild: this.guilds.get(guildCode)
        }
    }

    public getAllCities() {
        let allCities = new Array<{ serverId: number, cityId: number, guildCodes: string[]}>();
        let serverlists = new Array<number>();
        for(let [key, guildCodes] of this.cities) {
            let { serverId, cityId } = this.decodeKey(key);
            allCities.push({ serverId, cityId, guildCodes });
            if(!serverlists.includes(serverId)) serverlists.push(serverId);
        }
        return {cities: allCities, serverlists};
    }

    public getGuildsInCity(serverId: number, cityId: number) {
        let key = this.getKey(serverId, cityId);
        return this.cities.get(key)||[];
    }

    public getMembersOfGuild(guildCode: string) {
        return this.members.get(guildCode)||[];
    }

    public getGateHpAndInc(serverId: number, cityId: number, maxHp: number, inc: number = 0) {
        let key = this.getKey(serverId, cityId);
        let gateHp = this.gateHp.get(key);
        if(!this.gateHp.has(key)) {
            gateHp = maxHp;
        }
        if(inc != 0) {
            gateHp += inc;
            this.gateHp.set(key, gateHp);
        }
        if(gateHp <= 0) gateHp = 0;
        return gateHp
    }

    public getHistoryCity(roleId: string) {
        return this.historyCity.get(roleId)
    }
    
    public setHistoryCity(roleId: string, cityId: number) {
        return this.historyCity.set(roleId, cityId);
    }


    public pushMembers(guildCode: string, roleId: string, job: number) {

        if(this.members.has(guildCode)) {
            let members = this.members.get(guildCode);
            if(members.findIndex(cur => cur.roleId == roleId) == -1) {
                members.push({ roleId, job });
            }
        } else {
            let arr = new Array<string>();
            arr.push(roleId);
            this.members.set(guildCode, [{ roleId, job }]);
        }
    }

    public pushGuild(guildCode: string, serverId: number, cityId: number) {
        let key = this.getKey(serverId, cityId);
        if(!this.cities.has(key)) {
            this.cities.set(key, [guildCode]);
        } else {
            let arr = this.cities.get(key);
            if(!arr.includes(guildCode)) {
                arr.push(guildCode);
            }
        }
        this.guilds.set(guildCode, cityId);
    }

    public delCityRecord(cityId: number, serverId: number) {
        let key = this.getKey(serverId, cityId);
        let guildCodes = this.cities.get(key)||[];
        for(let guildCode of guildCodes) {
            this.members.delete(guildCode);
            this.guilds.delete(guildCode);
        }
        this.cities.delete(key);
    }
}


// 粮草先行
export class RaceActivityObject {
    private status = RACE_ACTIVITY_STATUS.WAITING; // 活动状态 0-未开始 1-已开始 2-已结束 
    public guildList: Array<{ serverId: number, guildCode: string }> = []; // 所有军团
    private members: Map<string, Array<Member>> = new Map(); // 每个军团参与的成员 guildCode => [{roleId, job}]
    private woodenHorses: Map<string, WoodenHorse> = new Map(); // 每个军团的木牛流马 guildCode => WoodenHorse
    private events: Map<string, Event[]> = new Map(); // 每个军团遇到的事件
    private items: Map<string, RewardInter[]> = new Map(); // 每个玩家的道具 roleId => [{id, count}]

    public getItem(roleId: string) {
        return this.items.get(roleId)||[]
    }

    // 是否加入过
    public hasJoin(guildCode: string, roleId: string) {
        let member = this.members.get(guildCode)||[];
        return member.findIndex(cur => cur.roleId == roleId) != -1;
    }

    // 加入member
    private pushMember(guildCode: string, roleId: string, job: number) {
        if(!this.members.has(guildCode)) {
            this.members.set(guildCode, []);
        }
        this.members.get(guildCode).push({roleId, job});
    }

    public getMembersOfGuild(guildCode: string) {
        return this.members.get(guildCode)||[];
    }
    
    public getAllWoodenHorses() {
        return this.woodenHorses;
    }

    public async joinWoodenHorse(guildCode: string, roleId: string, roleName: string, serverId: number, sid: string, job: number) {
        let woodenHorse = await this.getWoodenHorse(guildCode, serverId);
        if(!woodenHorse) return false;
        woodenHorse.joinMember(roleId, roleName, sid)
        this.pushMember(guildCode, roleId, job);

        if(this.status == RACE_ACTIVITY_STATUS.START) {
            let item = getRaceEventItems();
            this.handleItems(roleId, sid, item);
            this.woodenHorseStartRace(woodenHorse);
        } 
        return await this.getWoodenHorse(guildCode, serverId);
    } 

    // 获取某个军团的木马状态
    public async getWoodenHorse(guildCode: string, serverId: number) {
        if(!this.woodenHorses.has(guildCode)) {
            this.initEvents(guildCode);
            let guild = await GuildModel.findByCode(guildCode, serverId);
            if(!guild) return false;
            let { name: guildName, guildCe } = guild;
            this.woodenHorses.set(guildCode, new WoodenHorse(guildCode, guildName, guildCe, serverId));
            this.guildList.push({guildCode, serverId});
        }
        let woodenHorse = this.woodenHorses.get(guildCode);
        let events = this.events.get(guildCode)||[];
        let needSendEnd = woodenHorse.calCurWoodenHorse(events);

        if(woodenHorse.status == RACE_ACTIVITY_STATUS.START || woodenHorse.status == RACE_ACTIVITY_STATUS.END) { // 更新距离 
            let r = new Rank(REDIS_KEY.RACE_ACTIVITY, { serverId }, true, 5);
            await r.setRank({guildCode}, [Math.floor(woodenHorse.distance * 1000), woodenHorse.time - woodenHorse.startTime], false);
            let tomorrow = <number>getTimeFun().getAfterDayWithHour();
            await r.setExpire(tomorrow);

            if (needSendEnd) { // 抵达后发送奖励，发送消息，结算
                await sendSingleRaceActEndMsg(guildCode, woodenHorse);
            }
        }
        this.handleItemsByWoodenHorse(woodenHorse);
        return woodenHorse;
    }

    public async useItem(fromRoleId: string, sid: string, fromGuild: string, toGuild: string, id: number, count: number) {
        let rec = this.handleItems(fromRoleId, sid, [{id, count: -1 * count}]);
        if(!rec) return false;
        let event = new Event(id, fromGuild, toGuild, count);
        if(!this.events.has(toGuild)) {
            this.events.set(toGuild, [event]);
        } else {
            let events = this.events.get(toGuild)||[];
            events.push(event);
            this.events.set(toGuild, events);
        }
        let chatSid = await getGuildChannelSid(toGuild);
        pinus.app.rpc.chat.guildRemote.sendRaceEvent.toServer(chatSid, toGuild, [event]);

        return event;
    }

    // 定时任务到，开始比赛，设置开始赛道，发放初始道具
    public startRace() {
        this.status = RACE_ACTIVITY_STATUS.START;
        let guildCodes = new Array<string>();
        for(let [code, woodenHorse] of this.woodenHorses) {
            if(woodenHorse.memberCnt > 0) {
                this.woodenHorseStartRace(woodenHorse);
                guildCodes.push(code);
            }
        }
        return guildCodes;
    }

    private woodenHorseStartRace(woodenHorse: WoodenHorse) {
        woodenHorse.status = RACE_ACTIVITY_STATUS.START;
        woodenHorse.time = Date.now();
        woodenHorse.startTime = Date.now();
        let members = woodenHorse.members;
        for(let { roleId, sid } of members) {
            let item = getRaceEventItems();
            this.handleItems(roleId, sid, item);
        }
    }
    
    // 初始进入就随机9个灵球事件
    public initEvents(guildCode: string) {
        if(!this.events.get(guildCode)) {
            this.events.set(guildCode, []);
        }
        let dicEncounter = gameData.raceActivityEncounter; // 距离=>事件类型
        let goodEventNum = Math.floor(dicEncounter.eventNum/2);
        let badEventNum = dicEncounter.eventNum - goodEventNum;

        let dicRaceTypes = gameData.raceTypes.get(RACE_EVENT_TYPE.EVENT);
        let goodEvents = getRandEelm(dicRaceTypes.get(RACE_EVENT_EFFECT_TYPE.GOOD), goodEventNum);
        let badEvents = getRandEelm(dicRaceTypes.get(RACE_EVENT_EFFECT_TYPE.BAD), badEventNum);
        let events = sortArrRandom(goodEvents.concat(badEvents));
        let index = 0;
        
        for(let [distance, type] of dicEncounter.events) {
            if(type == RACE_EVENT_TYPE.ITEM) {
                let event = new Event(RACE_EVENT.ITEM, guildCode, guildCode, 1, distance);
                this.events.get(guildCode).push(event);
            } else if (type == RACE_EVENT_TYPE.EVENT) {
                let event = new Event(events[index], guildCode, guildCode, 1, distance);
                this.events.get(guildCode).push(event);
                index ++;
            }
        }
    }

    public getEvents(guildCode: string, distance: number) {
        let events = this.events.get(guildCode)||[];
        let result = new Array<Event>();
        for(let event of events) {
            let { endTime, endDistance } = event;
            if(endTime) {
                if(Date.now() > endTime) continue;
            }
            if(endDistance) {
                if(distance > endDistance) continue;
            }
            result.push(event);
        }
        return result;
    }

    private handleItemsByWoodenHorse(woodenHorse: WoodenHorse) {
        let { members } = woodenHorse;
        for(let curMember of members) {
            let { roleId, sid, items = [] } = curMember;
            if(items.length > 0) this.handleItems(roleId, sid, items);
            curMember.items = [];
        }
    }

    private handleItems(roleId: string, sid: string, incItems: {id: number, count: number}[]) {
        let items = this.items.get(roleId)||[];
        let sendItems = new Array<{id: number, count: number}>();
        for(let {id, count: inc} of incItems) {
            let curItem = items.find(cur => cur.id == id);
            if(!curItem) {
                curItem = { id, count: 0 };
                items.push(curItem);
            }
            if(curItem.count + inc < 0 ) return false;
            if(inc != 0) {
                curItem.count += inc;
                sendItems.push({ id, count: curItem.count });
            }
        }
        this.items.set(roleId, items);
        let uids = [{uid: roleId, sid}];
        pinus.app.get('channelService').pushMessageByUids('onRaceItemUpdate', resResult(STATUS.SUCCESS, { items: sendItems }), uids);

        return items;
    }

    public deleteFromList(guildCode: string) {
        let index = this.guildList.findIndex(cur => cur.guildCode == guildCode);
        this.guildList.splice(index, 1);
    }
}