import * as util from 'util';
import { default as handler } from './common/handler';
import { Package } from 'omelox-protocol';
import * as EventEmitter from 'events';
import { getLogger } from 'omelox-logger';
import { ISocket } from '../interfaces/ISocket';
import * as dgram from 'dgram';
import * as path from 'path';
let logger = getLogger('omelox', path.basename(__filename));


let ST_INITED = 0;
let ST_WAIT_ACK = 1;
let ST_WORKING = 2;
let ST_CLOSED = 3;

export class UdpSocket extends EventEmitter implements ISocket {
    id: number;
    socket: dgram.Socket;
    peer: dgram.RemoteInfo;
    host: string;
    port: number;
    remoteAddress: { ip: string; port: number };
    state: number;

    constructor(id: number, socket: dgram.Socket, peer: dgram.RemoteInfo) {
        super();
        this.id = id;
        this.socket = socket;
        this.peer = peer;
        this.host = peer.address;
        this.port = peer.port;
        this.remoteAddress = {
            ip: this.host,
            port: this.port
        };

        let self = this;
        this.on('package', function (pkg) {
            if (!!pkg) {
                pkg = Package.decode(pkg);
                handler(self, pkg);
            }
        });

        this.state = ST_INITED;
    }


    /**
     * Send byte data package to client.
     *
     * @param  {Buffer} msg byte data
     */
    send(msg: any) {
        if (this.state !== ST_WORKING) {
            return;
        }
        if (msg instanceof String) {
            msg = Buffer.from(msg as string);
        } else if (!(msg instanceof Buffer)) {
            msg = Buffer.from(JSON.stringify(msg));
        }
        this.sendRaw(Package.encode(Package.TYPE_DATA, msg));
    }

    sendRaw(msg: any) {
        this.socket.send(msg, 0, msg.length, this.port, this.host, function (err, bytes) {
            if (!!err) {
                logger.error('send msg to remote with err: %j', err.stack);
                return;
            }
        });
    }

    sendForce(msg: any) {
        if (this.state === ST_CLOSED) {
            return;
        }
        this.sendRaw(msg);
    }

    handshakeResponse(resp: any) {
        if (this.state !== ST_INITED) {
            return;
        }
        this.sendRaw(resp);
        this.state = ST_WAIT_ACK;
    }

    sendBatch(msgs: any[]) {
        if (this.state !== ST_WORKING) {
            return;
        }
        let rs = [];
        for (let i = 0; i < msgs.length; i++) {
            let src = Package.encode(Package.TYPE_DATA, msgs[i]);
            rs.push(src);
        }
        this.sendRaw(Buffer.concat(rs));
    }

    disconnect() {
        if (this.state === ST_CLOSED) {
            return;
        }
        this.state = ST_CLOSED;
        this.emit('disconnect', 'the connection is disconnected.');
    }
}