import DataModelBase from '../framework/DataModelBase';
import Event from './Event';
import Common from '../common/Common';
import CCipher from '../common/CCipher';
import Packet from '../net/Packet';
import ConstMsgId from '../net/ConstMsgId';
import MsgTransfer from '../net/MsgTransfer';
import Define from '../common/Define';


//var protobuf = require("protobufjs");
//var PBTransfer = require("PBTransfer");
//var ByteBuffer = protobuf.ByteBuffer;


export default class Connect extends DataModelBase
{
    constructor(url,isShortConnect)
    {
        super('Connect');       
        this._url = url;
        Common.Print('Connect url:' + url);
        this._isShortConnect = isShortConnect;
        this._state = Common.SocketState.Closed;
        this._webSocket = null;
        this._protoLoaded = false;
        this._protoMsgClass = {};
        this._messageIndex = 0;
        this._bNeedClose = false; // 客户端需要被主动关闭
        this._reConnectInterval = 3000;
        this._bReconnect = false;
        this._closeTimeOut = null;
        this._errorTimeOut = null;
        this._timeOut = null;
        this._sendDataBuf = new Array(); 
        this._heatInterval = 15000;//15 second
        this._pingTimes = 0;
        this._socketType = Define.SOCKET_TYPE.PLAZA

    }

//     var protoFiles = [

//         ["resources/proto/authmsg.proto", "./authmsg.proto"],
  
//         ["resources/proto/commsg.proto", "./commsg.proto"],
  
//         ["resources/proto/gamemsg.proto", "./gamemsg.proto"]
  
//   ];

        //加载proto文件，在游戏初始时进行，推荐在资源加载时调用
         //需要等待proto全部加载完成之后，才能使用socket
    loadProto (protoFiles,callback) 
    {
        let con = this
        con._protoMsgClass = {};
        
        cc.loader.loadResDir("proto", function(err, array)
        {

            for (var i=0, dep, data; i<protoFiles.length; i++)
            {

                dep = protoFiles[i];
            
                var realUrl = cc.url.raw(dep[0]);
                var proto_msg = array[i].text;
                var msg_builder = protobuf.loadProto(proto_msg, dep[1]);
                con.analiseNameSpace(msg_builder, con._protoMsgClass);
            }

            con._protoLoaded = true;

            Common.Print('_protoLoaded '+ con._protoLoaded);

            PBTransfer.setProtoClassHolder(con._protoMsgClass);



            if(callback)
            {
                callback.call();
            }
        });
    }

        //解析一个proto ns，将其中所有的builder都缓存起来
    analiseNameSpace (builder, classHolder)
    {
        if(!builder)
            return;

        if(!classHolder)
            return;

        if(!builder.ns)
            return;
            
        var children = builder.ns.children;
        if(!children)
            return;

        var child = children[0];
        if(!child)
            return;

        var packageName = "";

        //has package;
        while(child.className == "Namespace")
        {
            if(packageName.length == 0)
                packageName += child.name;
            else
                packageName += "." + child.name;

            children = child.children;
            if(!children)
                return;

            child = children[0];
            if(!child)
                return;
        }

        for(var child of children)
        {
            var name = child.name;
            if(!name)
                continue;

            if(packageName.length == 0)
                classHolder[name] = builder.build(name);
            else
                classHolder[name] = builder.build(packageName + "." + name);
        }
    }

    getConnectState()
    {
        if(this._webSocket == null)
        {

            return this._state;
        }

        if(this._webSocket.readyState == WebSocket.CONNECTING)
        {
            this._state = Common.SocketState.Connecting;
        }
        else if(this._webSocket.readyState == WebSocket.OPEN)
        {
            this._state = Common.SocketState.OK;
        }
        else if(this._webSocket.readyState == WebSocket.CLOSING)
        {
            this._state = Common.SocketState.Closing;
        }
        else if(this._webSocket.readyState == WebSocket.CLOSED)
        {
            this._state = Common.SocketState.Closed;
        }        
        else
        {
           //noting to do
        }

        //Common.Print("this._state:" + this._state);
        return this._state;

    }

    open( bReconnect)
    {

        Common.Print(" connect open" + this._url)
        this._bNeedClose = false
        if(this._timeOut != null)
        {
            clearTimeout(this._timeOut);            
            this._timeOut = null; 
        } 

        if(!this._url || typeof(this._url) !== "string" || this._url.length == 0)
            return -1;

        if(this._state == Common.SocketState.Connecting)//正在连接中......
        {
           // return this._state;
           Common.Print(" connect open Common.SocketState.Connecting")
           return 0
        }   

        if(bReconnect == true)
        {
            this._bReconnect = true;
            this.emit(Event.SOCKET_MSG.RECONNECT_START);
        }    

        this.close()
        this._bNeedClose = false; // 客户端主动关闭



        this._webSocket = null;

        console.log('open:cc.sys.os: ' + cc.sys.os + ' CC_JSB:' + CC_JSB + ' cc.sys.isMobile:' + cc.sys.isMobile)
        if(CC_JSB && cc.sys.OS_ANDROID === cc.sys.os)
        {
            var pemUrl = cc.url.raw('resources/key/server.pem')
            if(cc.loader.md5Pipe)
            {
                pemUrl = cc.loader.md5Pipe.transformURL('resources/key/server.pem')
            }

            //this._webSocket = new WebSocket(this._url,'',pemUrl);
            this._webSocket = new WebSocket(this._url);

        }
        else
        {
            this._webSocket = new WebSocket(this._url);
        }
        
        this._webSocket.binaryType = "arraybuffer";
        this._webSocket.onopen = this._onOpen.bind(this);
        this._webSocket.onmessage = this._onMessage.bind(this);
        this._webSocket.onerror = this._onError.bind(this);
        this._webSocket.onclose = this._onClose.bind(this);
        this._state = Common.SocketState.Connecting;    
        return 0;


    }

        //主动关闭连接
    close() 
    {
        if(!this._webSocket || !(this._webSocket instanceof WebSocket)) 
            return -1;

        if(this.getConnectState() == Common.SocketState.Closed)
            return this._state;

        this._state = Common.SocketState.Closed;
        this._webSocket.close();
        this._messageIndex = 0; 
        this._bNeedClose = true; // 客户端主动关闭
        this._bReconnect = false;

    }


    sendSocketData(wMain, wSub, pData,wDataSize)
    {

    }

    sendMsg(msg)
    {
        
        CCipher.encryptBuffer(msg,msg.byteLength)
        this._sendData(msg)
    }
   
    getUrl()
    {
        return this._url;
    }


/////////////////////////////////////////私有方法和属性/////////////////////////////////////

    //连接监听
    _onOpen (evt) 
    {
        
        Common.Print('Connect _onOpen start')

        this._bNeedClose = false; // 客户端主动关闭

        this._state = Common.SocketState.OK;
        this._pingTimes = 0;
        if(this._bReconnect == true)
        {
            this.emit(Event.SOCKET_MSG.RECONNECT_OK);
            this._bReconnect = false;
        }
    
        this.emit(Event.SOCKET_MSG.OPEN)
                

        if(this._closeTimeOut != null)
        {
            clearTimeout(this._closeTimeOut);            
            this._closeTimeOut = null; 
        }

        if(this._errorTimeOut != null)
        {
            clearTimeout(this._errorTimeOut);            
            this._errorTimeOut = null; 
        }

        if(this._timeOut != null)
        {
            clearTimeout(this._timeOut);            
            this._timeOut = null; 
        } 

        this.checkHeartBeat();


        Common.Print('Connect _onOpen end')
    }


    sendBufData()
    {
        for (var i = 0; i < this._sendDataBuf.length ; i++)
        {
            this.sendData(this._sendDataBuf[i].msgid,this._sendDataBuf[i].msgdata);

        }

        this._sendDataBuf = [];
    }    

    //返回监听
    _onMessage (evt) 
    {        
        //解析返回的消息
        // 粘包、拆包
        // if (evt.data instanceof Blob) 
        // {
        //     console.log("Blob")
        // }
        if(typeof(evt.data)=="string")
        {
            console.log("string")
        }
        Common.Print('Connect _onMessage')
        console.log(evt.data)
        Common.Print(typeof(evt.data))
 
        var dataview = new DataView(evt.data)
        var dataviewLength = evt.data.byteLength
        if(dataviewLength < Packet.TCPInfoSize) //
        {
            console.log("dataviewLength< Packet.TCPInfoSize(4)")
            return
        }

        CCipher.decryptBuffer(evt.data,evt.data.byteLength)

        var readPos = 0
        var cbDataKind = dataview.getUint8(readPos,Common.littleEndian)
        readPos +=1
        var cbCheckCode = dataview.getUint8(readPos,Common.littleEndian)
        readPos +=1
        var wPacketSize = dataview.getUint16(readPos,Common.littleEndian)
        readPos +=2
        var wMainCmdID = dataview.getUint16(readPos,Common.littleEndian)
        readPos +=2
        var wSubCmdID = dataview.getUint16(readPos,Common.littleEndian)
        readPos +=2        
        if(wMainCmdID != ConstMsgId.MDM_KN_COMMAND && wSubCmdID != ConstMsgId.SUB_KN_DETECT_SOCKET )
        {
            
            Common.Print("_onMessage wMainCmdID:" + wMainCmdID + " wSubCmdID:" + wSubCmdID + " wPacketSize:" + wPacketSize)
        }
       
        if(wMainCmdID == ConstMsgId.MDM_KN_COMMAND && wSubCmdID == ConstMsgId.SUB_KN_DETECT_SOCKET)
        {
            this.pingTimes = this.pingTimes - 1
        }



        if(wMainCmdID > 0 || wSubCmdID != Packet.SUB_KN_DETECT_SOCKET)
        {
            var pack = null 
            if(dataviewLength > Packet.TCPInfoSize)
            {
               var  packBuf = evt.data.slice(Packet.TCPHeadSize,dataviewLength)
            }


            this.emit(Event.SOCKET_MSG.DATA, wMainCmdID,wSubCmdID,packBuf)

        }
     
    }

    //错误监听
    _onError(evt) {
        //GH.log("GameSocket _onError", evt);
        console.log(Common.GetDateString() + 'GameSocket _onError'+ JSON.stringify(evt))

        let self = this;

        this._state = Common.SocketState.Error;
        if(this._timeOut!=null)
        {
            clearTimeout(this._timeOut);            
            this._timeOut = null; 
        }

        if(this._bNeedClose !=  true)
        {
            this._errorTimeOut = setTimeout(function() {

                self.open(true);

            }, self._reConnectInterval);
        }
       
        this.emit(Event.SOCKET_MSG.ERROR);

    }

    //关闭监听
    _onClose(evt) 
    {
        console.log(Common.GetDateString() + 'GameSocket _onClose'+ JSON.stringify(evt))

       var state =  this.getConnectState()
       console.log(Common.GetDateString() + 'GameSocket _onClose state :'+ state)
       if( Common.SocketState.OK == state )
       {
           return
       }

        this._state = Common.SocketState.Closed;       
        var bClientClosed = this._bClientClosed;
        let self = this;

        if(this._timeOut!=null)
        {
            clearTimeout(this._timeOut);            
            this._timeOut = null; 
        }

        if(this._bNeedClose !=  true)
        {
            this._closeTimeOut = setTimeout(function() {
    
                self.open(true);
                
            }, self._reConnectInterval);
        }

       
        this.emit(Event.SOCKET_MSG.CLOSE,bClientClosed);  
    }

    

    _sendData(dataBuf)
    {
        if(!dataBuf || !(dataBuf instanceof ArrayBuffer))
            return -1;

        if (!this._webSocket) 
            return -1;


        if(this.getConnectState() != Common.SocketState.OK)
        {
            return this._state;
        }

        if (this._webSocket.readyState === WebSocket.OPEN) {
            
            console.log('_sendData')
            this._webSocket.send(dataBuf); 
            
            return 0;       
        } else {
            this._state = this.getConnectState();
            //this._webSocket.close();
            return this._state;
            
        }
           
            
    }
 


    checkHeartBeat()
    {
        var self = this;
        // if(this._bClientClosed = true)
        // {
        //     return
        // }
        
        self._timeOut =  setTimeout(function () {

            if(self._bNeedClose !=  true)
            {
                if (self.pingTimes >= 3) //6次没有收到ping消息，认为断了线
                {
                    Common.Print('pingTimes >= 3');
                    self.pingTimes = 0;
                    self.open(true);//认为已经断开连接//然后重连
                    return;
                }
                //记录心跳次数
                self.pingTimes++;
                self.sendPing();
            }
            self.checkHeartBeat();
            }, this._heatInterval);
    }

    sendPing() 
    {
        
        console.log(Common.GetDateString() + 'sendPing');
        var msg = MsgTransfer.encodemsg(ConstMsgId.MDM_KN_COMMAND ,ConstMsgId.SUB_KN_DETECT_SOCKET,92 )
        // var sre = "hert"
        // console.log('msg is: ',sre);
        this.sendMsg(msg);
    }

}
