const Plugin = require('../../app.asar/core/factory/Plugin');
const util = require('util');

let Plug = function() {
    "use strict";
    const childProcess = require('child_process');
    const path = require('path');
    const net = require('net');
    const session = require('../../app.asar/core/service/Session');
    const ee = require('../../app.asar/core/service/Event');
    const se = require('../../app.asar/core/renderer/shared_events');
    const fm = require('../../app.asar/core/service/FileManager');
    const fs = require('fs');
    const myself = this;
    const dialog = require('electron').dialog;
    const BrowserWindow = require('electron').BrowserWindow;
    const http = require('http');

    const MAX_BUFFER_SIZE = 6990000;

    Plugin.call(this);
    myself._name = __dirname.split(path.sep).pop();
    const LOG = 'plugins/' + myself._name;
    let errors = [];
    let isFatal = false;
    let retry = 24;

    myself.loadSettings = function(reject) {
        let filename = path.resolve(session.get(session.KEYS.DIRECTORY_ASARS) + path.sep + 'plugin_' + myself._name + '.asar' + path.sep + 'plugin.json');
        return new Promise(function(resolve, re) {
            setTimeout(function() {
                fm.read(filename).then(function(data) {
                    let build = session.get(session.KEYS.BUILD);
                    data.executable = data.platform[build.target];
                    resolve(data);
                }).catch(function(error) {
                    console.log(LOG, error);
                    reject(error);
                    re(error);
                });
            }, 1);
        });
    };

    myself.findPort = function(reject) {
        return new Promise(function(resolve, re) {
            setTimeout(function() {
                let server = http.createServer();
                let port = -1;
                server.on('close', function() {
                    if (port === -1) {
                        reject();
                        re();
                    } else {
                        setTimeout(function() {
                            console.log(LOG, 'found free port at', port);
                            resolve(port);
                        }, 600);
                    }
                });
                server.listen(0, function() {
                    port = server.address().port;
                    server.close();
                    server = null;
                });
            }, 1);
        });
    };

    myself.runExecutable = function(remote, reject) {
        return new Promise(function(resolve, re) {
            if (session.get(session.KEYS.QUITTING)) {
                reject();
                return;
            }
            let softphone;
            setTimeout(function() {
                let dir = session.get(session.KEYS.DIRECTORY_PLUGINS) + path.sep + myself._name + path.sep;
                let args = ['-p', remote.port, '-t', 24 + retry, '-c', dir]; //
                let file = dir + remote.settings.executable;
                let build = session.get(session.KEYS.BUILD);
                try {
                    try {
                        fs.chmodSync(file, '0700');
                    } catch (error) {
                        console.log(LOG, error);
                    }
                    if (build.target === "linux-x64") {
                        file = 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/Lib;' + file;
                    }
                    softphone = childProcess.spawn(file, args, {
                        cwd: dir,
                        shell: false,
                        detached: false,
                        stdio: ['ignore', 'ignore', 'ignore']
                    });
                    var f = function() {
                        if (reject) {
                            reject(remote);
                            reject = null;
                            re(remote);
                        }
                    };
                    softphone.on('close', function() {
                        session.set(session.KEYS.HAS_MYXPAD_REMOTE, false);
                        session.set(session.KEYS.SOFTPHONE_IS_RUNNING, false);
                        console.log(LOG, remote.port, 'close event received');
                        f();
                    });
                    softphone.on('error', function() {
                        session.set(session.KEYS.HAS_MYXPAD_REMOTE, false);
                        session.set(session.KEYS.SOFTPHONE_IS_RUNNING, false);
                        console.log(LOG, remote.port, 'error event received');
                        f();
                    });
                    setTimeout(function() {
                        if (softphone && resolve) {
                            resolve(softphone);
                            resolve = null;
                        }
                    }, 5000);
                } catch (e) {
                    softphone = null;
                    console.log(LOG, remote.port, e);
                    reject(e);
                    reject = null;
                }
            }, 1);
        });
    };

    myself.listenToSocket = function(remote, reject, res) {
        console.log(LOG, remote.port, 'listening ...');
        return new Promise(function(resolve, re) {
            var rm = function(cl) {
                remote.client = null;
                try {
                    cl.close();
                } catch (e) {

                }
            };
            setTimeout(function() {
                let client;
                try {
                    client = new net.Socket();
                    remote.client = client;
                    client.setTimeout(0);
                    remote.msgs = '';
                    client.on('data', function(raw) {
                        //console.log(LOG, remote.port, 'data', raw.toString());
                        if (remote.msgs.length > 0) {
                            remote.msgs += raw.toString();
                        } else {
                            remote.msgs = raw.toString();
                        }
                        let msgs = remote.msgs.replace(/[;]/ig, '').split('\r\n'),
                            l = msgs.length,
                            i = -1,
                            msg = null,
                            valid = false;
                        while (++i < l) {
                            msg = msgs[i];
                            if (msg && msg.length) {
                                myself.onSocketMessage(remote, msg.replace(';', ''));
                                valid = true;
                            }
                        }
                        if (valid) {
                            if (resolve !== null) {
                                resolve(client);
                                resolve = null;
                            }
                            remote.msgs = '';
                        }
                    });
                    let onError = function(event, msg) {
                        console.log(LOG, remote.port, 'client', event, msg ? msg : '', typeof reject);
                        session.set(session.KEYS.HAS_MYXPAD_REMOTE, false);
                        session.set(session.KEYS.SOFTPHONE_IS_RUNNING, false);
                        if (reject) {
                            reject(remote);
                            reject = null;
                            re(remote);
                        }
                    };
                    client.on('connect', function() {
                        console.log(LOG, remote.port, 'client connected');
                        if (reject && resolve && client) {
                            let build = session.get(session.KEYS.BUILD);
                            myself.sendMsg(client, 'initialize "' + build.code + '"');
                            if (myself._window.getSettings() && Array.isArray(myself._window.getSettings().plugins) && myself._window.getSettings().plugins.indexOf('sso') !== -1 && build.target === 'win32-x64') {
                                myself.sendMsg(client, 'systemGetKerberos ' + remote.settings.spn);
                            } else {
                                myself.sendMsg(client, 'logout');
                                if (resolve !== null) {
                                    resolve(client);
                                    resolve = null;
                                }
                            }
                        }
                    });
                    client.on('error', function(msg) {

                        let build = session.get(session.KEYS.BUILD);
                        if ((build.target === 'win32-x64' || build.target === 'win32-ia32')) {
                            if (msg && msg.length && msg.indexOf('ECONNREFUSED') !== -1) {
                                let locale = session.get(session.KEYS.I18NKEYS);
                                dialog.showMessageBox(null, {
                                    type: 'error',
                                    title: build.name + ' - ' + build.publisher,
                                    message: locale ? locale.i__notification_softphone_failure_local_network : '',
                                    detail: locale ? locale.i__notification_softphone_failure_local_network_content : '',
                                    cancelId: 0,
                                    buttons: [(locale ? locale.i__continue_without_softphone : ''), (locale ? locale.i__quit : '')]
                                }, function(choice) {
                                    console.log(LOG, 'fatal', choice);
                                    if (choice === 0) {
                                        session.set(session.KEYS.SOFTPHONE_IS_RUNNING, false);
                                        session.set(session.KEYS.HAS_MYXPAD_REMOTE, false);
                                        res();
                                        if (myself._window) {
                                            myself._window.clear();
                                            myself._window.close();
                                            myself._window.show();
                                        }
                                    } else {
                                        ee.emit(ee.events.QUITTING);
                                        session.set(session.KEYS.QUITTING, true);
                                        ee.emit(ee.events.QUIT);
                                    }
                                });
                            } else {
                                rm(client);
                                onError('error', msg);
                            }
                        } else {
                            rm(client);
                            onError('error', msg);
                        }
                    });
                    client.on('timeout', function(msg) {
                        rm(client);
                        onError('timeout', msg);
                    });
                    client.on('close', function(msg) {
                        rm(client);
                        onError('close', msg);
                    });
                    client.on('end', function(msg) {
                        rm(client);
                        onError('end', msg);
                    });
                    client.connect(remote.port, '127.0.0.1', function() {
                        console.log(LOG, remote.port, 'client launched');
                    });
                } catch (e) {
                    console.log(LOG, remote.port, "can't listen", e);
                    rm(client);
                    reject(e);
                }
            }, 1);
        });
    };

    var toValue = function(data) {
        if (!data || data === 'null' || data === '""') {
            return '';
        } else if (!isNaN(Number(data))) {
            return Number(data);
        } else if (data.toLowerCase() === 'false' || data.toLowerCase() === 'true') {
            return data.toLowerCase() === 'true' ? true : false;
        }
        return data.replace(/["]/ig, '');
    };

    myself.softphoneMsgToJson = function(msg) {
        if (!msg || msg.indexOf('(') === -1 || msg.indexOf(')') === -1) {
            console.log(LOG, 'parse error', msg);
            return {};
        }
        let data = {};
        data.action = toValue(msg.split('(')[0]);
        let args = msg.slice(msg.indexOf('(') + 1, msg.length - 1);
        if (args.indexOf(',') !== -1) {
            args = args.split(',');
        } else {
            args = [args];
        }
        data.type = args.length > 0 ? toValue(args.shift()) : '';
        data.event = args.length > 1 ? toValue(args.shift()) : '';
        if (args && args.length) {
            args = args.shift().replace(/["]/ig, '');
            if (args.indexOf('|') !== -1) {
                let a = args.split('|'),
                    l = a.length,
                    i = -1;
                while (++i < l) {
                    a[i] = toValue(a[i]);
                }
                data.data = a;
            } else {
                data.data = toValue(args);
            }
        }
        return data;
    };

    myself.sendMsgToWindow = function(data) {
        if (myself._window && myself._window && myself._window.getWindow() && myself._window.getWindow().webContents) {
            console.log(LOG, '[>>]', myself._window.getName(), data);
            try {
                myself._window.getWindow().webContents.send(se.KEYS.PLUGIN_EVENT, data);
            } catch (error) {
                console.log(LOG, error);
            }
        }
    };

    myself.mapGetDeviceToMyXpadRemote = function(type, received) {
        let hashs = {
            eAudioCodecs: se.KEYS.GET_AUDIO_CODECS,
            eAudioCodecsList: se.KEYS.GET_AUDIO_CODECS_FREE,
            eAudioDeviceIn: se.KEYS.GET_DEVICE_IN,
            eAudioDeviceInWanted: se.KEYS.GET_DEVICE_IN_WANTED,
            eAudioDeviceOut: se.KEYS.GET_DEVICE_OUT,
            eAudioDeviceOutWanted: se.KEYS.GET_DEVICE_OUT_WANTED,
            eAudioDeviceRinger: se.KEYS.GET_DEVICE_RINGER,
            eAudioDeviceRingerWanted: se.KEYS.GET_DEVICE_RINGER_WANTED,
            eAudioDevicesIn: se.KEYS.GET_DEVICES_IN,
            eAudioDevicesOut: se.KEYS.GET_DEVICES_OUT,
            eAudioDevicesRinger: se.KEYS.GET_DEVICES_RINGER,
            eAudioInMute: se.KEYS.GET_DEVICE_IN_MUTE,
            eAudioInVolume: se.KEYS.GET_DEVICE_IN_VOLUME,
            eAudioOutMute: se.KEYS.GET_DEVICE_OUT_MUTE,
            eAudioOutVolume: se.KEYS.GET_DEVICE_OUT_VOLUME,
            eAudioRingerMute: se.KEYS.GET_DEVICE_RINGER_MUTE,
            eAudioRingerVolume: se.KEYS.GET_DEVICE_RINGER_VOLUME,
            eHIDEnable: se.KEYS.GET_HID_ENABLE,
            eHIDDevice: se.KEYS.GET_HIDDEVICE,
            eNetSecure: se.KEYS.GET_NET_SECURE,
            eNetSecureForced: se.KEYS.GET_NET_SECURE_FORCED,
            eHIDDeviceControl: se.KEYS.GET_DEVICE_CONTROL
        };
        if (received) {
            return hashs[type];
        }
        for (let k in hashs) {
            if (hashs.hasOwnProperty(k) && type === hashs[k]) {
                return 'SIPGet' + k.slice(1, k.length);
            }
        }
        return null;
    };

    myself.mapSetDeviceToMyXpadRemote = function(type, received) {
        let hashs = {
            eAudioCodecs: se.KEYS.SET_AUDIO_CODECS,
            eAudioDeviceIn: se.KEYS.SET_DEVICE_IN,
            eAudioDeviceInWanted: se.KEYS.SET_DEVICE_IN_WANTED,
            eAudioDeviceOut: se.KEYS.SET_DEVICE_OUT,
            eAudioDeviceOutWanted: se.KEYS.SET_DEVICE_OUT_WANTED,
            eAudioDeviceRinger: se.KEYS.SET_DEVICE_RINGER,
            eAudioDeviceRingerWanted: se.KEYS.SET_DEVICE_RINGER_WANTED,
            eAudioInMute: se.KEYS.SET_DEVICE_IN_MUTE,
            eAudioInVolume: se.KEYS.SET_DEVICE_IN_VOLUME,
            eAudioOutMute: se.KEYS.SET_DEVICE_OUT_MUTE,
            eAudioOutVolume: se.KEYS.SET_DEVICE_OUT_VOLUME,
            eAudioRingerMute: se.KEYS.SET_DEVICE_RINGER_MUTE,
            eAudioRingerVolume: se.KEYS.SET_DEVICE_RINGER_VOLUME,
            eHIDEnable: se.KEYS.SET_HID_ENABLE,
            eNetSecureForced: se.KEYS.SET_NET_SECURE_FORCED,
            eHIDDeviceControl: se.KEYS.SET_DEVICE_CONTROL
        };
        if (received) {
            return hashs[type];
        }
        for (let k in hashs) {
            if (hashs.hasOwnProperty(k) && type === hashs[k]) {
                return 'SIPSet' + k.slice(1, k.length);
            }
        }
        return null;
    };

    myself._opened = false;

    myself.onSocketMessage = function(remote, str) {
        let data = myself.softphoneMsgToJson(str);
        console.log(LOG, remote.port, '[<<]', str, data.action, data.type, data.event, data.data);
        switch (data.action) {
            case 'onSystemKerberos':
                session.set(session.KEYS.KERBEROS_TOKEN, data.data && data.data.indexOf('T1R') !== 0 ? data.data : '');
                break;
            case 'onProfileEvent':
            case 'onSystemEvent':
            case 'onConnexionEvent':
            case 'onSystemGetIPs':
            case 'forceLogout':
                if (data.action === 'onConnexionEvent' && data.type === 'service.softphone') {
                    console.log(LOG, 'netsecureforced', remote.eNetSecureForced, data.event, ',', data.data);
                    if (data.event === 'eStop' && data.data === 2) {
                        if (remote.eNetSecureForced) {
                            /*
                            session.set(session.KEYS.SOFTPHONE_MUST_NOT_RUN, true);
                            session.set(session.KEYS.SOFTPHONE_IS_RUNNING, false);
                             */
                            let locale = session.get(session.KEYS.I18NKEYS);

                            dialog.showErrorBox(locale ? locale.i__notification_softphone_netsecure_title : '', locale ? locale.i__notification_softphone_netsecure_error_content : '');
                            /*
                            console.log(LOG, remote.port, 'WARNING DISABLE NETSECURE');
                            myself.sendMsg(remote.client, 'SIPSetNetSecureForced "false"');
                            myself.sendMsg(remote.client, 'SIPGetNetSecureForced');
                            myself.sendMsg(remote.client, 'startConnexion "service.softphone"');
                            */
                            /*
                            let build = session.get(session.KEYS.BUILD);
                            let locale = session.get(session.KEYS.I18NKEYS);
                            myself._opened = true;
                            dialog.showMessageBox(null, {
                            	type: 'error',
                            	title: build.name + ' - ' + build.publisher,
                            	message: locale ? locale.i__notification_softphone_netsecure_title : '',
                            	detail: locale ? locale.i__notification_softphone_netsecure_content : '',
                            	cancelId: 0,
                            	buttons: [(locale ? locale.i__no : ''), (locale ? locale.i__yes : '')]
                            }, function(choice) {
                            	myself._opened = false;
                            	console.log(LOG, 'fatal', choice);
                            	if (choice === 0) {
                            		session.set(session.KEYS.SOFTPHONE_IS_RUNNING, false);
                            		session.set(session.KEYS.HAS_MYXPAD_REMOTE, false);
                            	} else {
                            		myself.sendMsg(remote.client, 'SIPSetNetSecureForced "false"');
                            		myself.sendMsg(remote.client, 'SIPGetNetSecureForced');
                            		myself.sendMsg(remote.client, 'startConnexion "service.softphone"');
                            		if (myself._window && myself._window.getWindow()) {
                            			// reload main when softphone is ready again
                            			try {
                            				myself._window.close();
                            				setTimeout(myself._window.show, 1000);
                            			} catch (error) {
                            				console.log(LOG, 'launchedSoftphone', error, myself._window.getWindow());
                            			}
                            		}
                            	}
                            });
                            */
                        }
                    }
                }
                myself.sendMsgToWindow(data);
                if (data.action === 'onConnexionEvent' && data.type === 'service.softphone') {
                    if (data.event === 'eRun' && !session.get(session.KEYS.SOFTPHONE_IS_RUNNING)) {
                        console.log(LOG, '***** softphone is now running *****');
                        session.set(session.KEYS.SOFTPHONE_IS_RUNNING, data.event === 'eRun');
                    } else if (data.event !== 'eRun' && session.get(session.KEYS.SOFTPHONE_IS_RUNNING)) {
                        console.log(LOG, '***** softphone is now stopped *****');
                        session.set(session.KEYS.SOFTPHONE_IS_RUNNING, data.event === 'eRun');
                    }
                }
                if (data.action === 'onSystemEvent') {
                    switch (data.type) {
                        case 'LogXPath':
                            console.log(LOG, 'LOGX is ', data.data);
                            session.set(session.KEYS.LOGXPATH, data.data);
                            break;
                        case 'LogXInfo':
                            console.log(LOG, 'LOGXINFO ', data.data);
                            ee.emit(ee.events.PLUGIN.LOGXINFO, data.data);
                            break;
                        case 'LogXExport':
                            console.log(LOG, 'LOGXEXPORT ', data.data);
                            ee.emit(ee.events.PLUGIN.LOGXEXPORT, data.data);
                            break;
                        case 'AutoRun':
                            remote.settings.START_WITH_OS = data.data;
                            break;
                        case 'DontSleep':
                            remote.settings.PREVENT_SLEEP = data.data;
                            break;
                    }
                }
                break;
            case 'onContactsExportToIS':
                ee.emit(ee.events.PLUGIN.IMPORT_OUTLOOK_CHANGED, data);
                break;
            case 'onCallEvent':
                myself.sendMsgToWindow(data);
                let oldValue = 0,
                    newValue = 0,
                    k = null;
                for (k in remote.calls) {
                    if (remote.calls.hasOwnProperty(k)) {
                        oldValue += 1;
                    }
                }
                oldValue = oldValue > 0;
                switch (data.type) {
                    case 'eCallCreate':
                        if (!remote.calls.hasOwnProperty(data.data)) {
                            remote.calls[data.data] = true;
                        }
                        break;
                    case 'eCallDestroy':
                        if (remote.calls.hasOwnProperty(data.data)) {
                            delete remote.calls[data.data];
                        }
                        break;
                }
                for (k in remote.calls) {
                    if (remote.calls.hasOwnProperty(k)) {
                        newValue += 1;
                    }
                }
                newValue = newValue > 0;
                if (oldValue !== newValue) {
                    console.log(LOG, ee.events.ACTIVE_CALL_CHANGED, oldValue, newValue);
                    session.set(session.KEYS.HAS_SOFTPHONE_CALL, newValue > 0);
                    ee.emit(ee.events.ACTIVE_CALL_CHANGED, newValue > 0);
                }
                break;
            case 'onSIPSettingsChange':
                if (data.type === 'eNetSecureForced') {
                    remote.eNetSecureForced = data.data;
                }
                let action = myself.mapGetDeviceToMyXpadRemote(data.type, true);
                if (action && remote && remote.callbacks.hasOwnProperty(action)) {
                    console.log(LOG, 'callback GET', action, remote.callbacks[action].id);
                    try {
                        remote.callbacks[action].sender.send(remote.callbacks[action].id, data.data);
                    } catch (error) {
                        console.log(LOG, action, remote.callbacks[action], error);
                    }
                    delete remote.callbacks[action];
                }
                if (action && action.length) {
                    ee.emit(ee.events.PLUGIN.DEVICE_SETTING_CHANGED, {
                        action: action,
                        value: data.data
                    });
                }
                action = myself.mapSetDeviceToMyXpadRemote(data.type, true);
                if (action && remote && remote.callbacks.hasOwnProperty(action)) {
                    let audio = session.get(session.KEYS.USER).audio;
                    audio = audio ? audio : {};
                    if (audio) {
                        audio[action] = data.data;
                        session.setPersistentByKey(session.KEYS.USER, 'audio', audio);
                    }
                    console.log(LOG, 'callback GET', action, remote.callbacks[action].id);
                    try {
                        remote.callbacks[action].sender.send(remote.callbacks[action].id, data.data);
                    } catch (error) {
                        console.log(LOG, action, remote.callbacks[action], error);
                    }
                    delete remote.callbacks[action];
                }
                break;
                /*
			case 'onSIPAudioCardChange':
				let audio = session.getPersistentByKey(session.KEYS.USER, 'audio');
				if (audio && !data.type && (audio[se.KEYS.SET_DEVICE_IN] !== data.event || audio[se.KEYS.SET_DEVICE_OUT] !== data.data)) {
					ee.emit(ee.events.PLUGIN.DEVICE_AUDIO_CARD_CHANGED, { in : data.event,
						out: data.data
					});
				}
				break;
				*/
            case 'onSIPAudioVumeter':
                ee.emit(ee.events.PLUGIN.VUMETER, {
                    mic: data.type,
                    speaker: data.data
                });
                break;
            case 'onSIPNetworkQualityChange':
                ee.emit(ee.events.PLUGIN.DEVICE_SIP_NETWORK_QUALITY_CHANGED, data);
                break;
        }
    };

    myself.sendMsg = function(client, msg, ignore) {
        try {
            client.write(msg + '\n');
            if (!ignore) {
                console.log(LOG, client.remotePort, '[>>]', msg);
            }
        } catch (error) {
            if (!ignore) {
                if (client) {
                    console.log(LOG, client.remotePort, '[>>]', error, msg);
                } else {
                    console.log(LOG, '[>>]', error, msg);
                }
            }
        }
    };

    myself.setStartWithOs = function(remote) {
        let build = session.get(session.KEYS.BUILD);
        if (remote.client && (build.target === 'win32-x64' || build.target === 'win32-ia32')) {
            if (session.get(session.KEYS.USER).platform.START_WITH_OS !== remote.settings.START_WITH_OS) {
                myself.sendMsg(remote.client, 'systemSetAutorun "' + session.get(session.KEYS.ELECTRON) + '","' + session.get(session.KEYS.USER).platform.START_WITH_OS + '","-startWithOs"');
            }
            if (session.get(session.KEYS.USER).platform.PREVENT_SLEEP !== remote.settings.PREVENT_SLEEP) {
                myself.sendMsg(remote.client, 'systemDontSleep "' + !!session.get(session.KEYS.USER).platform.PREVENT_SLEEP + '"');
            }
        } else if (build.target === 'darwin-x64') {
            try {
                let AutoLaunch = require(path.resolve(session.get(session.KEYS.DIRECTORY_APPLICATION), 'app.asar', 'node_modules', 'auto-launch', 'dist', 'index.js'));
                let al = new AutoLaunch({
                    name: build.name,
                    path: process.execPath.split('.app/Content')[0] + '.app'
                });
                if (session.get(session.KEYS.USER).platform.START_WITH_OS) {
                    al.isEnabled().then(function(enabled) {
                        if (!enabled) {
                            al.enable();
                        }
                    }).catch(function(e) {
                        console.log(LOG, 'error with autolaunch', e);
                    });
                } else {
                    al.isEnabled().then(function(enabled) {
                        if (enabled) {
                            al.disable();
                        }
                    }).catch(function(e) {
                        console.log(LOG, 'error with autolaunch', e);
                    });
                }
            } catch (error) {
                console.log(error);
            }
        }
    };

    myself.registerUserSettingsChanged = function(remote) {
        var f = function() {
            myself.setStartWithOs(remote);
        };
        ee.addListener(ee.events.USER_SETTINGS_CHANGED, f);
        return function() {
            console.log(LOG, 'registerUserSettingsChanged removed');
            ee.removeListener(ee.events.USER_SETTINGS_CHANGED, f);
        };
    };

    myself.registerUserUriChanged = function(remote) {
        var f = function(uri) {
            if (uri && uri.length) {
                myself.sendMsg(remote.client, 'login "' + uri + '"');
            } else {
                myself.sendMsg(remote.client, 'logout');
            }
        };
        ee.addListener(ee.events.USER_URI_CHANGED, f);
        return function() {
            console.log(LOG, 'registerUserUriChanged removed');
            ee.removeListener(ee.events.USER_URI_CHANGED, f);
        };
    };

    myself.registerImportOutlook = function(remote) {
        var f = function(ID) {
            var s = session.get(session.KEYS.SESSION);
            console.log(LOG, 'importOutlook', s);
            myself.sendMsg(remote.client, 'contactsExportToIS "' + ID + '","Outlook","true","' + s.restUri + '","' + s.sessionID + '"');
        };
        ee.addListener(ee.events.PLUGIN.IMPORT_OUTLOOK, f);
        return function() {
            console.log(LOG, 'registerImportOutlook removed');
            ee.removeListener(ee.events.PLUGIN.IMPORT_OUTLOOK, f);
        };
    };

    myself.registerConnexionActionChanged = function(remote) {
        var f = function(data) {
            let datas = [];
            if (data.args && data.args.length) {
                let l = data.args.length,
                    i = -1;
                while (++i < l) {
                    datas.push('"' + data.args[i] + '"');
                }
            }
            datas = datas.join(',');
            switch (data.action) {
                case se.KEYS.CONNEXION_CREATE:
                    myself.sendMsg(remote.client, 'SIPGetNetSecureForced');
                    myself.sendMsg(remote.client, 'createConnexion ' + datas);
                    break;
                case se.KEYS.CONNEXION_EDIT:
                    myself.sendMsg(remote.client, 'setAdvanceCnxParam ' + datas);
                    break;
                case se.KEYS.CONNEXION_START:
                    myself.sendMsg(remote.client, 'startConnexion ' + datas);
                    break;
                case se.KEYS.CONNEXION_STOP:
                    myself.sendMsg(remote.client, 'stopConnexion ' + datas);
                    break;
            }
        };
        ee.addListener(ee.events.CONNEXION_ACTION, f);
        return function() {
            console.log(LOG, 'registerConnexionActionChanged removed');
            ee.removeListener(ee.events.CONNEXION_ACTION, f);
        };
    };

    myself.registerLogChanged = function(remote) {
        var f = function(msg) {
            if (msg && msg.length > MAX_BUFFER_SIZE) {
                msg = msg.substring(0, MAX_BUFFER_SIZE) + ' ...';
            }
            myself.sendMsg(remote.client, 'log "' + msg + '"', true);
        };
        ee.addListener(ee.events.PLUGIN.LOG, f);
        return function() {
            console.log(LOG, 'registerLogChanged removed');
            ee.removeListener(ee.events.PLUGIN.LOG, f);
        };
    };

    myself.registerIpsChanged = function(remote) {
        var f = function(id) {
            console.log(LOG, ee.events.PLUGIN.GET_IPS, id);
            myself.sendMsg(remote.client, 'systemGetIPs');
        };
        ee.addListener(ee.events.PLUGIN.GET_IPS, f);
        return function() {
            console.log(LOG, 'registerIpsChanged removed');
            ee.removeListener(ee.events.PLUGIN.GET_IPS, f);
        };
    };

    myself.registerGetDeviceChanged = function(remote) {
        var f = function(data) {
            console.log(LOG, ee.events.PLUGIN.GET_DEVICE, data.action, data.id);
            let action = myself.mapGetDeviceToMyXpadRemote(data.action);
            remote.callbacks[data.action] = data;
            myself.sendMsg(remote.client, action);
        };
        ee.addListener(ee.events.PLUGIN.GET_DEVICE, f);
        return function() {
            console.log(LOG, 'registerGetDeviceChanged removed');
            ee.removeListener(ee.events.PLUGIN.GET_DEVICE, f);
        };
    };

    myself.registerSetDeviceChanged = function(remote) {
        var f = function(data) {
            console.log(LOG, ee.events.PLUGIN.SET_DEVICE, data.action, data.id);
            let action = myself.mapSetDeviceToMyXpadRemote(data.action);
            remote.callbacks[data.action] = data;
            myself.sendMsg(remote.client, action + ' "' + data.args[0] + '"');
        };
        ee.addListener(ee.events.PLUGIN.SET_DEVICE, f);
        return function() {
            console.log(LOG, 'registerSetDeviceChanged removed');
            ee.removeListener(ee.events.PLUGIN.SET_DEVICE, f);
        };
    };

    myself.registerSetVuMeterChanged = function(remote) {
        var f = function(data) {
            console.log(LOG, 'registerSetVuMeterChanged', data);
            myself.sendMsg(remote.client, 'SIPSetEnableVumeter "' + data + '"');
        };
        ee.addListener(ee.events.PLUGIN.SET_VUMETER, f);
        return function() {
            console.log(LOG, 'registerSetVuMeterChanged removed');
            ee.removeListener(ee.events.PLUGIN.SET_VUMETER, f);
        };
    };

    myself.registerGetLogXInfoChanged = function(remote) {
        var f = function(id) {
            console.log(LOG, ee.events.PLUGIN.GET_LOGXINFO, id);
            myself.sendMsg(remote.client, 'systemGetLogXInfo');
        };
        ee.addListener(ee.events.PLUGIN.GET_LOGXINFO, f);
        return function() {
            console.log(LOG, 'registerGetLogXInfo removed');
            ee.removeListener(ee.events.PLUGIN.GET_LOGXINFO, f);
        };
    };

    myself.registerGetLogXExportChanged = function(remote) {
        var f = function(data) {
            console.log(LOG, ee.events.PLUGIN.GET_LOGXEXPORT, data.start, data.end);
            myself.sendMsg(remote.client, 'systemExportLogx "' + data.start + '","' + data.end + '"');
        };
        ee.addListener(ee.events.PLUGIN.GET_LOGXEXPORT, f);
        return function() {
            console.log(LOG, 'registerGetLogXExport removed');
            ee.removeListener(ee.events.PLUGIN.GET_LOGXEXPORT, f);
        };
    };

    myself.registerDisconnectSoftphone = function(remote) {
        var f = function() {
            console.log(LOG, 'quitting app');
            myself.sendMsg(remote.client, 'stopConnexion "service.softphone"');
        };
        ee.addListener(ee.events.DISCONNECT_SOFTPHONE, f);
        return function() {
            console.log(LOG, 'registerQuitting removed');
            ee.removeListener(ee.events.DISCONNECT_SOFTPHONE, f);
        };
    };

    myself.registerQuitting = function(remote) {
        var f = function() {
            console.log(LOG, 'quitting app');
            myself.sendMsg(remote.client, 'quitApp');
        };
        ee.addListener(ee.events.QUITTING, f);
        return function() {
            console.log(LOG, 'registerQuitting removed');
            ee.removeListener(ee.events.QUITTING, f);
        };
    };

    myself.registerListeners = function(remote) {
        return new Promise(function(resolve) {
            setTimeout(function() {
                let registers = [];
                registers.push(myself.registerUserSettingsChanged(remote));
                registers.push(myself.registerUserUriChanged(remote));
                registers.push(myself.registerConnexionActionChanged(remote));
                registers.push(myself.registerLogChanged(remote));
                registers.push(myself.registerIpsChanged(remote));
                registers.push(myself.registerGetDeviceChanged(remote));
                registers.push(myself.registerSetDeviceChanged(remote));
                registers.push(myself.registerImportOutlook(remote));
                registers.push(myself.registerSetVuMeterChanged(remote));
                registers.push(myself.registerGetLogXInfoChanged(remote));
                registers.push(myself.registerGetLogXExportChanged(remote));
                registers.push(myself.registerDisconnectSoftphone(remote));
                registers.push(myself.registerQuitting(remote));
                let audio = session.get(session.KEYS.USER).audio;
                if (audio) {
                    for (let k in audio) {
                        if (audio.hasOwnProperty(k)) {
                            let action = myself.mapSetDeviceToMyXpadRemote(k);
                            console.log(LOG, 'restoring previous audio devices', action, audio[k]);
                            myself.sendMsg(remote.client, action + ' "' + audio[k] + '"');
                        }
                    }
                }
                myself.sendMsg(remote.client, 'setSoftwareInfo "' + session.get(session.KEYS.BUILD).name + '","' + session.get(session.KEYS.BUILD).url + '"');
                myself.setStartWithOs(remote);
                myself.sendMsg(remote.client, 'SIPGetAudioCodecsList');
                myself.sendMsg(remote.client, 'SIPGetAudioCodecs');
                myself.sendMsg(remote.client, 'SIPEnableMonitoring "1"');
                myself.sendMsg(remote.client, 'SIPGetNetSecureForced');
                resolve(function() {
                    console.log(LOG, 'unregistering', registers.length, 'handlers');
                    while (registers.length) {
                        registers.pop()();
                    }
                });
            }, 1);
        });
    };

    myself.launchSoftphone = function(resolve, reject) {
        let build = session.get(session.KEYS.BUILD);
        if (build.target === "darwin-x64" || build.target === "win32-x64" || build.target === "win32-ia32") {
            myself.loadSettings(reject).then(function(settings) {
                console.log(LOG, 'launchSoftphone 1/5');
                var remote = {
                    settings: settings,
                    port: -1,
                    softphone: null,
                    client: null,
                    unregister: null,
                    callbacks: {},
                    calls: {},
                    userSettings: {},
                    netSecureForced: false
                };
                myself.findPort(remote, reject).then(function(port) {
                    remote.port = port;
                    console.log(LOG, 'launchSoftphone 2/5');
                    myself.runExecutable(remote, reject).then(function(softphone) {
                        console.log(LOG, 'launchSoftphone 3/5');
                        remote.softphone = softphone;
                        myself.listenToSocket(remote, reject, resolve).then(function(client) {
                            console.log(LOG, 'launchSoftphone 4/5');
                            remote.client = client;
                            myself.registerListeners(remote, reject).then(function(unregister) {
                                console.log(LOG, 'launchSoftphone 5/5', myself._window);
                                remote.unregister = unregister;
                                session.set(session.KEYS.HAS_MYXPAD_REMOTE, true);
                                if (myself._window && myself._window.getWindow() && session.get(session.KEYS.READY)) {
                                    // reload main when softphone is ready again
                                    try {
                                        myself._window.close();
                                        setTimeout(myself._window.show, 1000);
                                    } catch (error) {
                                        console.log(LOG, 'launchedSoftphone', error, myself._window.getWindow());
                                    }
                                }

                                resolve();
                            }).catch(reject);
                        }).catch(reject);
                    }).catch(reject);
                }).catch(reject);
            }).catch(reject);
        } else {
            console.log(LOG, 'launchSoftphone', 'unsupported', process.platform);
            if (session.get(session.KEYS.HAS_MYXPAD_REMOTE)) {
                session.set(session.KEYS.HAS_MYXPAD_REMOTE, false);
            }
            if (session.get(session.KEYS.SOFTPHONE_IS_RUNNING)) {
                session.set(session.KEYS.SOFTPHONE_IS_RUNNING, false);
            }
            ee.addListener(ee.events.PLUGIN.LOG, function(msg) {
                if (Array.isArray(msg)) {
                    console.log(LOG, '[<<]', myself._window.getName(), msg);
                }
            });
            resolve();
        }
    };

    myself.fatal = function(resolve, reject) {
        let sleeping = session.set(session.KEYS.SLEEPING);
        if (!sleeping && !isFatal) {
            isFatal = true;
            let locale = session.get(session.KEYS.I18NKEYS);
            let build = session.get(session.KEYS.BUILD);
            if (!session.get(session.KEYS.QUITTING)) {
                dialog.showMessageBox(null, {
                    type: 'error',
                    title: build.name + ' - ' + build.publisher,
                    message: locale ? locale.i__notification_softphone_failure_title : '',
                    detail: locale ? locale.i__notification_softphone_failure_content : '',
                    cancelId: 0,
                    buttons: [(locale ? locale.i__continue_without_softphone : ''), (locale ? locale.i__quit : '')]
                }, function(choice) {
                    console.log(LOG, 'fatal', choice);
                    isFatal = false;
                    if (choice === 0) {
                        session.set(session.KEYS.SOFTPHONE_IS_RUNNING, false);
                        session.set(session.KEYS.HAS_MYXPAD_REMOTE, false);
                        resolve();
                        if (myself._window) {
                            myself._window.clear();
                            myself._window.close();
                            myself._window.show();
                        }
                    } else {
                        ee.emit(ee.events.QUITTING);
                        session.set(session.KEYS.QUITTING, true);
                        try {
                            let a = BrowserWindow.getAllWindows(),
                                l = a.length,
                                i = -1;
                            while (++i < l) {
                                a[i].destroy();
                            }
                        } catch (error) {
                            console.log(LOG, error);
                        }
                        if (reject) {
                            reject();
                        } else {
                            require('electron').app.quit();
                        }
                    }
                });
            }
        }
    };

    myself.handleSoftphoneError = function(resolve, reject) {
        console.log(LOG, 'registering softphone error handler');
        return function(data) {
            ee.emit(ee.events.ACTIVE_CALL_CHANGED, false);
            session.set(session.KEYS.HAS_SOFTPHONE_CALL, false);
            console.log(LOG, 'handleSoftphoneError', util.inspect(data, false, null, false));
            if (session.get(session.KEYS.QUITTING)) {
                return;
            }
            if (data) {
                if (data.unregister) {
                    data.unregister();
                    data.unregister = null;
                }
                if (data.client) {
                    try {
                        data.client.destroy('forced destroy');
                    } catch (e) {}
                    data.client = null;
                }
                if (data.softphone) {
                    try {
                        data.softphone.kill("SIGTERM");
                    } catch (e) {}
                    data.softphone = null;
                }
                if (session.get(session.KEYS.QUITTING)) {
                    return;
                }
                if (errors.indexOf(data.port) === -1) {
                    errors.push(data.port);
                    if (retry > 0) {
                        retry -= 1;
                        if (retry === 23) {
                            if (session.get(session.KEYS.SOFTPHONE_IS_RUNNING)) {
                                // we tell to myIstra that softphone is disconnected
                                session.set(session.KEYS.SOFTPHONE_IS_RUNNING, false);
                            }
                            /*
                            myself.sendMsgToWindow({
                            	action: 'onConnexionEvent',
                            	type: 'service.softphone',
                            	event: 'eError',
                            	data: "5"
                            });
                            */
                            if (session.get(session.KEYS.HAS_MYXPAD_REMOTE)) {
                                session.set(session.KEYS.HAS_MYXPAD_REMOTE, false);
                            }
                            /*
                            if (session.get(session.KEYS.USER).notification.AUDIO) {
                            	let locale = session.get(session.KEYS.I18NKEYS);
                            	ee.emit(ee.events.NOTIFICATION_EMITED, {
                            		kind: 'NOTIFICATION_SOFTPHONE_RESTART_ON',
                            		title: locale ? locale.i__notification_softphone_restarting_title : '',
                            		label: locale ? locale.i__notification_softphone_restarting_content : '',
                            		icon: session.get(session.KEYS.LOGO),
                            		ts: -1
                            	});
                            }
                            */
                        }
                        setTimeout(function() {
                            myself.launchSoftphone(myself.handleSoftphoneSuccess(resolve), myself.handleSoftphoneError(resolve, reject));
                        }, 1);
                    } else {
                        myself.fatal(resolve, reject);
                    }
                } else {
                    if (!data || !data.port) {
                        if (retry > 0) {
                            retry -= 1;
                            setTimeout(function() {
                                myself.launchSoftphone(myself.handleSoftphoneSuccess(resolve), myself.handleSoftphoneError(resolve, reject));
                            }, 1);
                        } else {
                            myself.fatal(resolve, reject);
                        }
                    }
                }
            } else {
                if (retry > 0) {
                    retry -= 1;
                    setTimeout(function() {
                        myself.launchSoftphone(myself.handleSoftphoneSuccess(resolve), myself.handleSoftphoneError(resolve, reject));
                    }, 1);
                } else {
                    myself.fatal(resolve, reject);
                }
            }
        };
    };

    myself.handleSoftphoneSuccess = function(resolve) {
        console.log(LOG, 'registering softphone success handler');
        return function() {
            console.log(LOG, 'handleSoftphoneSuccess', resolve !== null);
            if (resolve !== null) {
                resolve();
                resolve = null;
            }
            retry = 24;
        };
    };

    myself.execute = function(window) {
        console.log(LOG, 'execute');
        return new Promise(function(resolve, reject) {
            myself._window = window;
            retry = 24;
            setTimeout(function() {
                myself.launchSoftphone(myself.handleSoftphoneSuccess(resolve), myself.handleSoftphoneError(resolve, reject));
            }, 1);
        });
    };

    return {
        execute: myself.execute,
        getName: myself.getName
    };
};

util.inherits(Plug, Plugin);
module.exports = Plug;
