import { GUILDACTIVITY } from "../../pubUtils/dicParam";
import { SimpleGuildRankParam, SimpleRoleRankParam } from '../rank'
import { prop } from "@typegoose/typegoose";
import { CITY_STATUS, RACE_ACTIVITY_STATUS, RACE_EVENT, RACE_EVENT_TYPE } from "../../consts";
import { gameData, getRaceEventItems } from "../../pubUtils/data";
import { getRandEelm, getRandResultByMember, } from "../../pubUtils/util";
import { RewardInter } from "../../pubUtils/interface";

export class GateMembersRec {
    roleId: string;
    round: number = 0;
    enemies: number[] = [];
    constructor(roleId: string) {
        this.roleId = roleId;
    }
}

export class WoodenHorseMember {
    roleId: string;
    roleName: string;
    sid: string;
    items: RewardInter[] = [];
    code?: string;
    hasReceiveInitItems: boolean = false;
    isOnline: boolean = false;

    constructor(roleId: string, roleName: string, sid: string, code: string) {
        this.roleId = roleId;
        this.roleName = roleName;
        this.sid = sid;
        this.code = code;
        if(sid) this.isOnline = true;
    }

    addTempItem(item: RewardInter) {
        this.items.push(item);
    }

    addTempItems(items: RewardInter[]) {
        this.items.push(...items);
    }

    getTempItems() {
        return this.items;
    }

    receiveTempItems() {
        let items = [...this.items];
        this.items = [];
        return items;
    }

    isReceived() {
        return this.hasReceiveInitItems;
    }

    setReceived(hasReceiveInitItems: boolean) {
        this.hasReceiveInitItems = hasReceiveInitItems;
    }

    updateSid(sid: string) {
        this.sid = sid;
        this.isOnline = true;
    }

    leave() {
        this.isOnline = false;
    }
}

// 木牛流马
export class WoodenHorse {
    @prop({required: true})
    serverId: number = 0;               // 服务器id
    @prop({required: true})
    guildCode: string = "";             // 军团code 木马的唯一标识
    @prop({required: true})
    guildName: string = "";             // 军团名
    @prop({required: true})
    guildCe: number = 0;                // 军团战力
    @prop({required: true})
    status: number = 0;                 // 状态 0-停止 1-开启 2-结束
    @prop({required: true})
    speed: number = GUILDACTIVITY.RACE_INIT_SPEED; // 速度
    @prop({required: true})
    durability: number = 100;           // 耐久度
    @prop({required: true})
    distance: number = 0;               // 距离
    @prop({required: true})
    allStartTime: number = 0;              // 全服开启世界
    @prop({required: true})
    startTime: number = 0;              // 开始时间
    @prop({required: true})
    time: number = 0;                   // 到达时间
    @prop({required: true})
    memberCnt: number = 0;              // 成员人数
    @prop({required: true})
    members: WoodenHorseMember[] = [];  // 成员
    @prop({required: true})
    shield: number = 0;                 // 护盾数量
    @prop({required: true})
    shieldTime: number = 0;             // 天师盾符

    remainItems: Map<number, { total: number, max: number }> = new Map();
    isRobot: boolean = false;           // debug接口生成

    constructor(guildCode: string, guildName: string, guildCe: number, serverId: number, allStartTime: number, isRobot: boolean) {
        this.guildCode = guildCode;
        this.guildName = guildName;
        this.guildCe = guildCe;
        this.serverId = serverId;
        this.allStartTime = allStartTime;
        this.isRobot = isRobot;
    }

    public startRace(allStartTime: number) {
        this.status = RACE_ACTIVITY_STATUS.START;
        this.time = Date.now();
        this.startTime = Date.now();
        this.allStartTime = allStartTime;
    }

    public stopRace() {
        this.status = RACE_ACTIVITY_STATUS.END;
    }

    /**
     * 根据时间计算当前木马速度距离耐久等
     * @param events 
     * @returns {boolean} needSendEnd 是否跑到终点发送结束新号 
     */
    public calCurWoodenHorse(events: Event[]): boolean {
        if(this.status == RACE_ACTIVITY_STATUS.END) return false;

        if(this.status == RACE_ACTIVITY_STATUS.START) {
            if(this.distance >= GUILDACTIVITY.RACEACTIVITY_LENGTH) {
                this.distance = GUILDACTIVITY.RACEACTIVITY_LENGTH;
                this.status = RACE_ACTIVITY_STATUS.END;
                this.speed = 0;
                return true;
            }

            this.distance = Math.floor((this.distance + (Date.now() - this.time)/1000 * this.speed ) * 1000)/1000; // 1位小数点
            this.time = Date.now();
        }
        let effectiveEvents = new Array<Event>();
        for(let i = 0; i < events.length; i++) {
            let event = events[i];
            if(event.useDistance() && event.startDistance && this.distance > event.startDistance) {
                let startTime = Date.now() - Math.floor((this.distance - event.startDistance) / this.speed);
                event.setStartTime(startTime); // 距离生效的事件的实际生效时间，主要用于速度叠加顺序
            }
            let isEffective = false;
            if(event.useTime()) {
                if(event.startTime <= Date.now() && event.endTime > Date.now()) {
                    isEffective = true;
                }
                if(event.endTime < Date.now()) {
                    if(event.endTime && event.startTime == event.endTime) {
                        isEffective = true;
                    }
                    let index = events.findIndex(cur => cur.id == event.id);
                    events.splice(index, 1);
                }
            }
            if(event.useDistance()) {
                if(event.startDistance <= this.distance && event.endDistance > this.distance) {
                    isEffective = true;
                }
                if(event.endDistance < this.distance) {
                    if(event.endDistance && event.startDistance == event.endDistance) {
                        isEffective = true;
                    }
                    let index = events.findIndex(cur => cur.id == event.id);
                    events.splice(index, 1);
                }
            }

            if(isEffective) {
                effectiveEvents.push(event);
            }
        }

        effectiveEvents.sort((a, b) => a.startTime - b.startTime);
        this.speed = GUILDACTIVITY.RACE_INIT_SPEED + this.memberCnt * GUILDACTIVITY.RACE_PER_SPEED;
        for(let { id, count, endTime } of effectiveEvents) {
            this.calEvent(id, count, endTime);
        }
        if(this.durability <= 0) {
            this.status = RACE_ACTIVITY_STATUS.BREAK;
            this.speed = 0;
        } 
        return false;
    }

    private calEvent(id: number, count: number = 1, endTime?: number) {
        let { effect } = gameData.raceActivityEvents.get(id);

        switch (id) {
            case RACE_EVENT.LIANNU:
                if (this.shieldTime < Date.now()) {
                    if (this.shield >= count) {
                        this.shield -= count;
                    } else {
                        this.shield = 0;
                        this.durability -= (count - this.shield) * effect[0];
                        if(this.durability > 100) this.durability = 100;
                        if(this.durability < 0) this.durability = 0;
                    }
                }
                break;
            case RACE_EVENT.GUISHOUYINFU:
                if (this.shieldTime < Date.now()) {
                    this.time = endTime;
                    this.speed = effect[1]||0.1;
                }
                break;
            case RACE_EVENT.FENGCHE:
            case RACE_EVENT.WUGUIBANYUNFU:
                this.speed *= Math.pow(1 + effect[0] / 100, count); break;
            case RACE_EVENT.LUDUN:
                this.shield += count * effect[0]; break;
            case RACE_EVENT.TIANSHIDUNFU:
                this.shieldTime = endTime; break;
            case RACE_EVENT.JIASU_1:
                this.speed *= Math.pow(1 + effect[0] / 100, count); break;
            case RACE_EVENT.JIASU_2:
                this.speed += effect[0]; break;
            case RACE_EVENT.HUIFU_1:
                this.durability += effect[0]; 
                if(this.durability > 100) this.durability = 100;
                if(this.durability < 0) this.durability = 0;
                break;
            case RACE_EVENT.JIANSU_1:
                this.speed *= Math.pow(1 - effect[0] / 100, count); break;
            case RACE_EVENT.JIANSU_2:
                this.speed -= effect[0]; 
                if(this.speed < 0) this.speed = 0;
                break;
            case RACE_EVENT.SHANGHAI_1:
                this.durability -= effect[0]; 
                if(this.durability > 100) this.durability = 100;
                if(this.durability < 0) this.durability = 0;
                break;
            case RACE_EVENT.ITEM:
                let onlineMembers = this.members.filter(member => member.isOnline);
                let memberCnt = onlineMembers.length;
                console.log('##### HANDLE_ITEM', memberCnt, onlineMembers)
        
                let ranMember: WoodenHorseMember[] = getRandEelm(onlineMembers, GUILDACTIVITY.RACEACTIVITY_EVENT_MEMBERCNT);
                if(ranMember.length <= 0) ranMember = onlineMembers;
                for(let member of ranMember) {
                    let item = gameData.raceEventItems;
                    member.addTempItems(item);
                }
                let normalItems = getRaceEventItems();
                
                for(let [id, {total, max}] of normalItems) {
                    let randResult = getRandResultByMember(total, max, memberCnt);
                    for(let i = 0; i < onlineMembers.length; i++) {
                        let count = randResult.arr[i]||0;
                        if(count > 0) {
                            onlineMembers[i].addTempItem({ id, count });
                            normalItems.get(id).total -= count;
                        }
                    }
                }
                break;
        }
    }


    public joinMember(roleId: string, roleName: string, sid: string, code: string) {
        let index = this.members.findIndex(cur => cur.roleId == roleId)
        if(index == -1) {
            let member = new WoodenHorseMember(roleId, roleName, sid, code);
            this.members.push(member);
            this.speed++;
            this.memberCnt++;
            return member;
        } else {
            return this.members[index];
        }
    }

    public updateMemberSid(roleId: string, sid: string) {
        let index = this.members.findIndex(cur => cur.roleId == roleId);
        if(index != -1) {
            this.members[index].updateSid(sid);
        }
    }

    public leaveMemberSid(roleId: string, isQuit = false) {
        let index = this.members.findIndex(cur => cur.roleId == roleId);
        if(index != -1) {
            this.members[index].leave();
        }
        if(isQuit) this.memberCnt--;
        if(this.memberCnt < 0) this.memberCnt = 0;
    }

    public setRemainItem(id: number, remain: number, max: number) {
        this.remainItems.set(id, { total: remain, max });
    }

    public getTreatTime() {
        return {...this, time: this.time - this.allStartTime }
    }
}

export class Event {
    @prop({required: true})
    id: number;
    @prop({required: true})
    fromGuild: string;
    @prop({required: true})
    toGuild: string;
    @prop({required: true})
    startTime?: number;
    @prop({required: true})
    startDistance?: number;
    @prop({required: true})
    endTime?: number;
    @prop({required: true})
    endDistance?: number;
    @prop({required: true})
    count: number = 1;
    @prop({required: true})
    type: number = 0;      // 1-时间开启 2-距离开启


    constructor(id: number, fromGuild: string, toGuild: string, count: number = 0, distance: number = 0) {
        let dicEvent = gameData.raceActivityEvents.get(id);
        this.id = id;
        this.fromGuild = fromGuild;
        this.toGuild = toGuild;
        
        this.startDistance = distance;
        this.endDistance = this.startDistance + dicEvent.continueDistance;
        this.startTime = Date.now() + dicEvent.effectTime * 1000;
        this.endTime = this.startTime + dicEvent.continueTime * 1000;
        if(count) {
            this.count = count;
        }
        this.type = dicEvent.type;
    }

    setStartTime(time: number) {
        this.startTime = time;
    }

    needReCalHorse() {
        let importantEvents = [
            RACE_EVENT.LIANNU, // 连弩，可能durability突然变0
            RACE_EVENT.GUISHOUYINFU,  // 鬼手阴符
            RACE_EVENT.FENGCHE, // 风车
            RACE_EVENT.WUGUIBANYUNFU, // 五鬼搬运符
        ];
        return importantEvents.indexOf(this.id) != -1
    }

    useDistance() {
        return this.type == RACE_EVENT_TYPE.EVENT||this.type == RACE_EVENT_TYPE.ITEM;
    }

    useTime() {
        return this.type == RACE_EVENT_TYPE.DEFAULT;
    }
}

export interface GuildRankParams {
    guildRank: SimpleGuildRankParam[],
    myGuildRank: SimpleGuildRankParam,
    memberRank?: SimpleRoleRankParam[],
    myMemberRank?: SimpleRoleRankParam
}

export class Member {
    @prop({required: true})
    roleId: string;            // 玩家id
    @prop({required: true})
    job: number;             // 在军团的职位
    @prop({required: false})
    code?: string;             // 玩家的当前记录的code
}

export class JoinMember extends Member{
    @prop({required: true})
    roleName: string;            // 玩家名
}

export class CityParam {
    cityId: number = 0;   // 城池id
    guardGuildCode: string = ''; // 占领军团id
    guardGuildName: string = ''; // 占领军团名
    declareCount: number = 0; // 宣战数
    status: number = CITY_STATUS.NOT_OPEN;   // 城池对于我军状态

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

export class RaceActivityRankParam {
    rank: number = 0;
    code: string;
    name: string;
    time: number;
    sortTime: number;
    sortDistance: number;
    num: number;
    durability: number;

    constructor(woodenHorse: WoodenHorse) {
        this.code = woodenHorse.guildCode;
        this.name = woodenHorse.guildName;
        this.durability = woodenHorse.durability;
        let distance = woodenHorse.distance > GUILDACTIVITY.RACEACTIVITY_LENGTH? GUILDACTIVITY.RACEACTIVITY_LENGTH: woodenHorse.distance;
        this.sortDistance = distance;
        this.sortTime = woodenHorse.time - woodenHorse.allStartTime;
        this.num = Math.floor(distance);
        this.time = Math.floor((woodenHorse.time - woodenHorse.allStartTime)/1000)
    }

    setRank(rank: number) {
        this.rank = rank;
    }
}