function Message(obj, conn) {
    
    this.isResponsable = function() {
        return obj.id >= 0
    }

    this.sendResponse = function(data) {
        conn.send({
            id: obj.id,
            is_response: true,
            data: data
        })
    }

    this.data = function() {
        return obj.data
    }

    return this
}

/* 
{
    connected: function
    сlosed: function
    incoming: function
} 
*/
function Conn(addr, callback = {}) {
    
    self = this
    let socket = null
    let connected = false
    let rStore = {}
    let idCounter = 0, idMax = Math.pow(2, 62)
    let mQueue = []

    function nextID() {
        idCounter += 1
        if (idCounter == idMax)
            idCounter = 0
        return idCounter
    }

    function connect() {
        socket = new WebSocket("ws://" + addr + "/api")

        socket.onopen = function(e) {
            connected = true
            if (callback.connected != null)
                callback.connected()
            //console.log('connected', mQueue)
            mQueue.forEach(function(obj) {
                self.sendMessage(obj.obj, obj.rCallback, obj.deadline)
            })
            mQueue = []
        }
    
        socket.onclose = function(e) {
            //console.log('close...')
            connected = false
            if (callback.closed != null)
                callback.closed()
            setTimeout(function() {
                self.reconnect()
            }, 3000)
        }
    
        socket.onmessage = function(e) {
            let mObj = JSON.parse(e.data)
            //console.log(mObj)
            if (mObj.is_response) {
                //console.log('response')
                let rCall = rStore[mObj.id]
                //console.log('rc', rCall, rStore)
                if (rCall) {
                    clearTimeout(rCall.cancel)
                    delete rStore[mObj.id]
                    rCall.callback(mObj.data)
                    //console.log(rStore)
                }
            } else {
                let m = new Message(mObj, self)
                callback.incoming(m)
            }
        }
    }

    this.reconnect = function() {
        //console.log('reconnect', connected)
        connect()
    }

    function send (obj) {
        if (!connected) {
            return
        }
        socket.send(JSON.stringify(obj))
    }

    this.sendMessage = function(obj, rCallback, deadline = 30000) {
        if (!connected) {
            mQueue.push({
                obj: obj,
                rCallback: rCallback,
                deadline: deadline
            })
            return
        }
        if (deadline == 0) {
            send({
                id: id,
                data: obj,
            })
        } else {
            let id = nextID()
            send({
                id: id,
                data: obj,
            })
            let cancel = setTimeout(function() {
                delete rStore[id]
            }, deadline)
            rStore[id] = {
                cancel: cancel,
                callback: rCallback,
            }
        }
    }

    connect()
    return self
}