import { Client as ColyseusClient, Room } from "colyseus.js";
class CSClient {
    constructor(roomName, serverUrl,messageCB,connectedCB,closeCB) {
        this.roomName=roomName;
        this.serverUrl=serverUrl;
        this.messageCB=messageCB;
        this.connectedCB=connectedCB;
        this.closeCB=closeCB;
        this.client=new ColyseusClient(serverUrl);
        this.callbacks=[];
        this.retryTimer=null;
        this.retryTimeout=1000;
        this.maxRetry=5;
        this.currRetry=0;
        this.isKicked=false;
        //ping
        this.pingTimer=null;
        this.pingInterval=10000;
        this.pingTimeout=0;
        this.pingTimeoutMax=3;
        this.unsetRoom();
    }
    clearTimer(){
        if (this.retryTimer!=null){
            clearTimeout(this.retryTimer);
            this.retryTimer = null;
        }
        this.pingTimeout=0;
    }
    clearPingTimer(){
        if (this.pingTimer){
            clearInterval(this.pingTimer);
            this.pingTimer=null;
        }
    }
    startRoomPing(){
        this.clearPingTimer();
        let self=this;
        this.pingTimer=setInterval(()=>{
            if (self.pingTimeout>self.pingTimeoutMax){
                self.clearPingTimer();
                console.log("connecting timeout");
                self.reconnect();
                return;
            }
            self.pingTimeout++;
            self.send("PING","");
        },this.pingInterval);
    }
    unsetRoom(){
        console.log("unsetRoomunsetRoom");
        if(this.currentRoom){
            this.currentRoom.removeAllListeners();
            this.currentRoom=null;
        }
        this.clearPingTimer();
    }
    setRoom(room){
        this.currentRoom=room;
        this.currRetry=0;
        let self=this;
        room.connection.transport.ws.addEventListener("close",()=>{
            console.log("connection closed");
            if (!self.isKicked)self.reconnect();
        });
        room.onStateChange((newState) => {
            // console.log(self.roomName, "has new state:", newState.mySynchronizedProperty);
            // skip unregistered (null) callbacks
            self.callbacks.filter((_) => _).forEach((callback) => callback(newState));
        });
        room.onMessage("*",(type,message)=>{
            //被踢了
            if (message!=null&&message.cmd==="kick"){
                self.isKicked=true;
            }
            if (type==="PONG"){
                self.pingTimeout--;
                return;
            }
            let host=window.location.host;
            if (host.startsWith("localhost")){
                console.log("Room:"+self.roomName+" onMessage:"+" type:"+type+" message:"+JSON.stringify(message))
            }
            if (self.messageCB)self.messageCB(type,message);
        })
        room.onError((code, message) => {
            console.log("couldn't join", room.name,code,message);
        });

        // when leaving, reset the connection state indicator
        room.onLeave((e)=>{
            self.unsetRoom();
        });
        if (this.connectedCB)this.connectedCB(true);
        this.startRoomPing();
    }
    async reconnect(resolve){
        let during=this.retryTimeout+this.currRetry*2000;
        this.clearTimer();
        this.clearPingTimer();
        if (this.currRetry>=this.maxRetry){
            if (resolve)resolve(false);
            this.currRetry=0;
            if (this.closeCB)this.closeCB();
            return;
        }
        let self=this;
        this.retryTimer=setTimeout(()=>{
            self.currRetry++;
            self.connect({}).then((data)=>{
                if (resolve)resolve(data);
            })
        },during);
    }
    async connect(options={}){
        let self=this;
        this.clearPingTimer();
        if(this.isConnected())await this.disconnect();
        return new Promise((resolve, reject)=>{
            self.client.joinOrCreate(this.roomName,options).then((room)=>{
                self.setRoom(room);
                resolve(true);
            }).catch((e)=>{
                // resolve(false);
                self.reconnect(resolve);
            })
        });
    }
    async disconnect(){
        let self=this;
        return new Promise((resolve, reject)=>{
            if (self.currentRoom!=null){
                // self.currentRoom.onLeave(()=>{
                //     self.unsetRoom();
                //     resolve(true);
                // });
                self.currentRoom.leave();
                self.unsetRoom();
                resolve(true);
            }
            else{
                self.unsetRoom();
                resolve(true);
            }
        })
    }
    send(type,message){
        if (this.currentRoom){
            // console.log("send:",type,message)
            this.currentRoom.send(type,message);
        }
    }
    isConnected(){
        return this.currentRoom!=null;
    }
}
export default CSClient;
