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

import { Member, GateMembersRec, WoodenHorse, Event, WoodenHorseMember, RaceActivityRankParam } from "../../domain/battleField/guildActivity";
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, PUSH_ROUTE, GUILD_ACTIVITY_STATUS, DEBUG_MAGIC_WORD } from "../../consts";
import { getRandEelm, sortArrRandom, resResult, getRandResultByMember } from "../../pubUtils/util";
import { sendMessageToGuildWithSuc, sendMessageToServerWithSuc, sendMessageToUserWithSuc } from "../pushService";

class MemberInfo extends Member {
    isQuit: boolean = false;

    constructor(roleId: string, job: number, code: string) {
        super();
        this.roleId = roleId;
        this.job = job;
        this.code = code;
    }

    public quit() {
        this.isQuit = true;
    }
}

class GuildInfo {
    serverId: number;
    guildCode: string;
    isDissmiss: boolean = false;

    constructor(serverId: number, guildCode: string) {
        this.serverId = serverId;
        this.guildCode = guildCode;
    }

    dismiss() {
        this.isDissmiss = true;
    }
}

// 粮草先行
export class RaceActivityObject {
    public status = GUILD_ACTIVITY_STATUS.WAITING; // 活动状态 0-未开始 1-已开始 2-已结束 
    private allStartTime = 0; // 开启时间，每天晚上8点
    private guildList: GuildInfo[] = []; // 所有军团
    private members: Map<string, MemberInfo[]> = 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}]
    private hasSentGuild: string[] = [];

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

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

    public async updateMemberSid(guildCode: string, serverId: number, roleId: string, sid: string) {
        let member = this.getMember(guildCode, roleId);
        if(!member) return member;
        let wh = await this.getWoodenHorse(guildCode, serverId);
        wh.updateMemberSid(roleId, sid);
        return member
    }

    public async memberLeave(guildCode: string, serverId: number, roleId: string) {
        let member = this.getMember(guildCode, roleId);
        if(!member) return member;
        let wh = await this.getWoodenHorse(guildCode, serverId);
        wh.leaveMemberSid(roleId);

        return member
    }

    public async memberQuit(guildCode: string, serverId: number, roleId: string) {
        let member = this.getMember(guildCode, roleId);
        if(!member) return;
        member.quit();
        let wh = await this.getWoodenHorse(guildCode, serverId);
        wh.leaveMemberSid(roleId, true);
    }

    public dismiss(guildCode: string) {
        let guild = this.guildList.find(guild => guild.guildCode == guildCode);
        if(!guild) return;
        guild.dismiss();
        this.woodenHorses.delete(guildCode);
    }

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

    public getGuilds() {
        return this.guildList.filter(cur => !cur.isDissmiss);
    }

    public getMembersOfGuild(guildCode: string) {
        let members = this.members.get(guildCode)||[];
        return members.filter(cur => !cur.isQuit);
    }
    
    public getAllWoodenHorses() {
        return this.woodenHorses;
    }

    public async getRanks(serverId: number, guildCode?: string) {
        let myRank: RaceActivityRankParam = null;
        let ranks: RaceActivityRankParam[] = [];
        let myIndex = -1;
        for(let [_, _wh] of this.woodenHorses) {
            if(_wh.serverId == serverId && _wh.status != GUILD_ACTIVITY_STATUS.WAITING) {
                let wh = await this.getWoodenHorse(_wh.guildCode, _wh.serverId);
                if(wh) {
                    ranks.push(new RaceActivityRankParam(wh));
                }
            }
        }
        ranks.sort((a, b) => {
            if(a.sortDistance == b.sortDistance) {
                return a.sortTime - b.sortTime;
            } else {
                return b.sortDistance - a.sortDistance;
            }
        }).map((wh, index) => {
            wh.setRank(index + 1);
            if(guildCode && wh.code == guildCode) {
                myRank = wh;
                myIndex = index;
            }
            return wh
        });

        if(!myRank && guildCode) {
            let myWoodenHorse = await this.getWoodenHorse(guildCode, serverId);
            if(myWoodenHorse) myRank = new RaceActivityRankParam(myWoodenHorse);
        }

        return { guildRank: ranks, myGuildRank: myRank, myIndex }
    }

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

        if(this.status == GUILD_ACTIVITY_STATUS.START) {
            // this.sendRandItemsToMembers([member], woodenHorse, woodenHorse.remainItems);
            this.woodenHorseStartRace(woodenHorse);
        } 
        return await this.getWoodenHorse(guildCode, serverId);
    } 

    // 获取某个军团的木马状态
    public async getWoodenHorse(guildCode: string, serverId: number, isDebug: boolean = false) {
        if(!this.woodenHorses.has(guildCode)) {
            this.initEvents(guildCode);
            if(isDebug) {
                this.woodenHorses.set(guildCode, new WoodenHorse(guildCode, guildCode, 0, serverId, this.allStartTime, false));
                this.guildList.push(new GuildInfo(serverId, guildCode));
            } else {
                let guild = await GuildModel.findByCode(guildCode, serverId);
                if(!guild) return undefined;
                let { name: guildName, guildCe } = guild;
                this.woodenHorses.set(guildCode, new WoodenHorse(guildCode, guildName, guildCe, serverId, this.allStartTime, false));
                this.guildList.push(new GuildInfo(serverId, guildCode));
            }
        }
        let woodenHorse = this.woodenHorses.get(guildCode);
        let events = this.events.get(guildCode)||[];
        let needSendEnd = woodenHorse.calCurWoodenHorse(events);
        if (needSendEnd) { // 抵达后发送奖励，发送消息，结算
            await sendSingleRaceActEndMsg(guildCode, woodenHorse);
        }
            
        this.handleItemsByWoodenHorse(woodenHorse);
        return woodenHorse;
    }

    public async useItem(serverId: number, fromRoleId: string, sid: string, fromGuild: string, toGuild: string, id: number, count: number, debug = false) {
        if(!debug) {
            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 { guildRank, myGuildRank, myIndex } = await this.getRanks(serverId, toGuild);
        if(myGuildRank) {
            let ranks = this.getScreenHorseRanks(guildRank, myIndex);
            for(let rank of ranks||[]) {
                if(rank) await sendMessageToGuildWithSuc(rank.code, PUSH_ROUTE.GUILD_RACE_EVENT, { timestamp: Date.now(), events: [event] });
            }
            let timeout = setTimeout(async () => {
                await this.getScreenHorses(serverId, guildRank, myIndex, true);
                clearTimeout(timeout);
            }, event.startTime - Date.now())
        } else {
            await sendMessageToGuildWithSuc(toGuild, PUSH_ROUTE.GUILD_RACE_EVENT, { timestamp: Date.now(), events: [event] });
        }
        return event;
    }

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

    public stopRace() {
        this.status = GUILD_ACTIVITY_STATUS.END;
        for(let [_, woodenHorse] of this.woodenHorses) {
            woodenHorse.stopRace();
        }
    }

    private woodenHorseStartRace(woodenHorse: WoodenHorse) {
        woodenHorse.startRace(this.allStartTime);
        let members = woodenHorse.members;
        let normalItems = getRaceEventItems();
        this.sendRandItemsToMembers(members, woodenHorse, normalItems);
    }

    private sendRandItemsToMembers(members: WoodenHorseMember[], woodenHorse: WoodenHorse, items: Map<number, { total: number, max: number}>) {
        let notReceiveMembers = members.filter(member => !member.isReceived() && member.isOnline);
        let memberCnt = notReceiveMembers.length;
        console.log('##### SEND_START', memberCnt, notReceiveMembers)
        let addItems: Map<string, { member: WoodenHorseMember, items: {id: number, count: number}[] }> = new Map();
        for(let [id, {total, max}] of items) {
            let randResult = getRandResultByMember(total, max, memberCnt);
            for(let i = 0; i < memberCnt; i++) {
                let member = notReceiveMembers[i];
                let count = randResult.arr[i]||0;
                if(count > 0) {
                    if(!addItems.has(member.roleId)) {
                        addItems.set(member.roleId, { member: member, items: [] });
                    }
                    addItems.get(member.roleId).items.push({ id, count });
                }
            }
            woodenHorse.setRemainItem(id, randResult.remain, max);
        }
        for(let [_roleId, {member, items}] of addItems) {
            member.setReceived(true);
            this.handleItems(member.roleId, member.sid, items);
        }
    }
    
    // 初始进入就随机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(!event.useDistance() && endTime) {
                if(Date.now() > endTime) continue;
            }
            if(event.useDistance() && 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);
        sendMessageToUserWithSuc(roleId, PUSH_ROUTE.RACE_ITEM_UPDATE, { items: sendItems }, sid);
        return items;
    }

    public send(guildCode: string) {
        this.hasSentGuild.push(guildCode);
    }

    public hasSend(guildCode: string) {
        return this.hasSentGuild.indexOf(guildCode) != -1;
    }

    public async calServerHorses(serverId: number) {
        let { guildRank: ranks } = await this.getRanks(serverId);
        // 找到往前和往后2艘船
        for (let i = 0; i < ranks.length; i++) {  
            await this.getScreenHorses(serverId, ranks, i, true); 
        }
    
    }

    private getScreenHorseRanks(ranks: RaceActivityRankParam[], i: number) {
        let curRank = ranks[i];
        if(!curRank || curRank.durability == 0) return;

        let beforeHorses: RaceActivityRankParam[] = [], afterHorses: RaceActivityRankParam[] = [], beforeCnt = 0, afterCnt = 0;
        for(let j = i - 1; j >= 0; j--) {
            if(ranks[j]) {
                beforeHorses.push(ranks[j]);
                if(ranks[j].durability > 0) beforeCnt ++
            }
            if(beforeCnt >= 2) break;
        }
        for(let j = i + 1; j < ranks.length; j++) {
            if(ranks[j]) {
                afterHorses.push(ranks[j]);
                if(ranks[j].durability > 0) afterCnt++;
            }
            if(afterCnt >= 2) break;
        }
        // 发送
        // console.log('send', woodenHorseList.length); 
        return [...beforeHorses.reverse(), curRank, ...afterHorses]
    }

    private async getScreenHorses(serverId: number, ranks: RaceActivityRankParam[], i: number, needSend: boolean) {
        let curRank = ranks[i];
        if(!curRank) return;
        // 发送
        // console.log('send', woodenHorseList.length);
        let wh = await this.getWoodenHorse(curRank.code, serverId);
        let screenRank = this.getScreenHorseRanks(ranks, i);
        let woodenHorseList: WoodenHorse[] = [];
        if(curRank.durability != 0) {
            for(let rank of screenRank) {
                if(rank.code == curRank.code) {
                    woodenHorseList.push(wh);
                } else {
                    let wh = await this.getWoodenHorse(rank.code, serverId);
                    woodenHorseList.push(wh);
                }
            }
        }
 
        if(needSend) {
            let events = this.getEvents(curRank.code, wh ? wh.distance : 0);
            await sendMessageToGuildWithSuc(curRank.code, PUSH_ROUTE.GUILD_RACE_UPDATE, { timestamp: Date.now(), woodenHorseList, guildRank: ranks, myGuildRank: curRank, events });    
        }
        return woodenHorseList
    }
}

