
import Defaults from '../entities/Defaults.js';
import Client from '../dao/client.js';
import Server from '../dao/socketServer.js';
import util from 'util';

class IPC{
    constructor(){

    }
    
    //public members
    config=new Defaults;
    of={};
    server=false;

    //protected methods
    get connectTo(){
        return connect;
    }
    get connectToNet(){
        return connectNet;
    }
    get disconnect(){
        return disconnect
    }
    get serve(){
        return serve;
    }
    get serveNet(){
        return serveNet;
    }
    get log(){
        return log;
    }

    set connectTo(value){
        return connect;
    }
    set connectToNet(value){
        return connectNet;
    }
    set disconnect(value){
        return disconnect
    }
    set serve(value){
        return serve;
    }
    set serveNet(value){
        return serveNet;
    }
    set log(value){
        return log;
    }
}

    

function log(...args){
    if(this.config.silent){
        return;
    }

    for(let i=0, count=args.length; i<count; i++){
        if(typeof args[i] != 'object'){
            continue;
        }

        args[i]=util.inspect(
            args[i],
            {
                depth:this.config.logDepth,
                colors:this.config.logInColor
            }
        );
    }

    this.config.logger(
        args.join(' ')
    );
}

function disconnect(id){
    if(!this.of[id]){
        return;
    }

    this.of[id].explicitlyDisconnected=true;

    this.of[id].off('*','*');
    if(this.of[id].socket){
        if(this.of[id].socket.destroy){
            this.of[id].socket.destroy();
        }
    }

    delete this.of[id];
}

function serve(path,callback){
    if(typeof path=='function'){
        callback=path;
        path=false;
    }
    if(!path){
        this.log(
            'Server path not specified, so defaulting to',
            'ipc.config.socketRoot + ipc.config.appspace + ipc.config.id',
            this.config.socketRoot+this.config.appspace+this.config.id
        );
        path=this.config.socketRoot+this.config.appspace+this.config.id;
    }

    if(!callback){
        callback=emptyCallback;
    }

    this.server=new Server(
        path,
        this.config,
        log
    );

    this.server.on(
        'start',
        callback
    );
}

function emptyCallback(){
    //Do Nothing
}

function serveNet(host,port,UDPType,callback){
    if(typeof host=='number'){
        callback=UDPType;
        UDPType=port;
        port=host;
        host=false;
    }
    if(typeof host=='function'){
        callback=host;
        UDPType=false;
        host=false;
        port=false;
    }
    if(!host){
        this.log(
            'Server host not specified, so defaulting to',
            'ipc.config.networkHost',
            this.config.networkHost
        );
        host=this.config.networkHost;
    }
    if(host.toLowerCase()=='udp4' || host.toLowerCase()=='udp6'){
        callback=port;
        UDPType=host.toLowerCase();
        port=false;
        host=this.config.networkHost;
    }

    if(typeof port=='string'){
        callback=UDPType;
        UDPType=port;
        port=false;
    }
    if(typeof port=='function'){
        callback=port;
        UDPType=false;
        port=false;
    }
    if(!port){
        this.log(
            'Server port not specified, so defaulting to',
            'ipc.config.networkPort',
            this.config.networkPort
        );
        port=this.config.networkPort;
    }

    if(typeof UDPType=='function'){
        callback=UDPType;
        UDPType=false;
    }

    if(!callback){
        callback=emptyCallback;
    }

    this.server=new Server(
        host,
        this.config,
        log,
        port
    );

    if(UDPType){
        this.server[UDPType]=true;
        if(UDPType === "udp4" && host === "::1") {
            // bind udp4 socket to an ipv4 address
            this.server.path = "127.0.0.1";
        }
    }

    this.server.on(
        'start',
        callback
    );
}

function connect(id,path,callback){
    if(typeof path == 'function'){
        callback=path;
        path=false;
    }

    if(!callback){
        callback=emptyCallback;
    }

    if(!id){
        this.log(
            'Service id required',
            'Requested service connection without specifying service id. Aborting connection attempt'
        );
        return;
    }

    if(!path){
        this.log(
            'Service path not specified, so defaulting to',
            'ipc.config.socketRoot + ipc.config.appspace + id',
            (this.config.socketRoot+this.config.appspace+id).data
        );
        path=this.config.socketRoot+this.config.appspace+id;
    }

    if(this.of[id]){
        if(!this.of[id].socket.destroyed){
            this.log(
                'Already Connected to',
                id,
                '- So executing success without connection'
            );
            callback();
            return;
        }
        this.of[id].socket.destroy();
    }

    this.of[id] = new Client(this.config,this.log);
    this.of[id].id = id;
    (this.of[id].socket)? (this.of[id].socket.id=id):null;
    this.of[id].path = path;

    this.of[id].connect();

    callback(this);
}

function connectNet(id,host,port,callback){
    if(!id){
        this.log(
            'Service id required',
            'Requested service connection without specifying service id. Aborting connection attempt'
        );
        return;
    }
    if(typeof host=='number'){
        callback=port;
        port=host;
        host=false;
    }
    if(typeof host=='function'){
        callback=host;
        host=false;
        port=false;
    }
    if(!host){
        this.log(
            'Server host not specified, so defaulting to',
            'ipc.config.networkHost',
            this.config.networkHost
        );
        host=this.config.networkHost;
    }

    if(typeof port=='function'){
        callback=port;
        port=false;
    }
    if(!port){
        this.log(
            'Server port not specified, so defaulting to',
            'ipc.config.networkPort',
            this.config.networkPort
        );
        port=this.config.networkPort;
    }

    if(typeof callback == 'string'){
        UDPType=callback;
        callback=false;
    }
    if(!callback){
        callback=emptyCallback;
    }

    if(this.of[id]){
        if(!this.of[id].socket.destroyed){

            this.log(
                'Already Connected to',
                id,
                '- So executing success without connection'
            );
            callback();
            return;
        }
        this.of[id].socket.destroy();
    }

    this.of[id] = new Client(this.config,this.log);
    this.of[id].id = id;
    (this.of[id].socket)? (this.of[id].socket.id=id):null;
    this.of[id].path = host;
    this.of[id].port = port;

    this.of[id].connect();

    callback(this);
}

export {
    IPC as default,
    IPC
};
