let path=require('path');
const os = process.platform === 'win32' ? '_win' : '';

let pty =null; //require(ptypath);
const Logger = require('../logs/clsLogger').clsLogger;
const logger = new Logger();
let util = require('util');

const ransi = require('strip-ansi');

const Bluetooth = class Bluetooth {
    constructor() {
        //if bluetoothctl now works on pi3
        //sudo adduser pi bluetooth
        //newgrp bluetooth
        let os = require('os');
        if (os.platform() != 'linux') {
            this.isBluetoothControlExists = false;
            logger.System('bluetooth control is not supported in this os');
            return;
        }



        pty=require('node-pty')
        this.term = pty.spawn('bash', [], {
            name: 'xterm-color',
            cols: 100,
            rows: 40,
            cwd: process.env.HOME,
            env: process.env
        });

        this.bluetoothEvents = {
            Device: 'DeviceEvent',
            Controller: 'ControllerEvent',
            DeviceSignalLevel: 'DeviceSignalLevel',
            Connected: 'Connected',
            Paired: 'Paired',
            AlreadyScanning: 'AlreadyScanning'
        };
        this.mydata = "";
        this.devices = [];
        this.controllers = [];
        this.isBluetoothControlExists = false;
        this.isBluetoothReady = false;
        this.isScanning = false;
        this.isConfirmingPassKey = false;

        if (os.platform() == 'linux') {
            this.term.write('type bluetoothctl\r');
            this.term.write('bluetoothctl --help\r')
        } else {
            logger.System('bluetooth control is not supported in this os');
            return;
        }

        this.term.on('data', (data) => {
            data = ransi(data).replace('[bluetooth]#', '');
            if (data.indexOf('bluetoothctl is ') !== -1 && data.indexOf('/usr/bin/bluetoothctl') !== -1) {
                this.isBluetoothControlExists = true;
                this.isBluetoothReady = true;
                logger.System('bluetooth controller exists');
                this.term.write('bluetoothctl\r');
                this.term.write('power on\r');
                this.term.write('agent on\r');
                setInterval(() => { this.checkInfo() }, 5000)
            }
            //console.log("mydata:" + data)
            let regexdevice = /(\[[A-Z]{3,5}\])?\s?Device\s([0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2})\s(?!RSSI)(?!Class)(?!Icon)(?!not available)(?!UUIDs:)(?!Connected)(?!Paired)(?![0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2})(?!\s)(.+)/gm;
            let regexcontroller = /\[[A-Z]{3,5}\]?\s?Controller\s([0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2})\s(?!Discovering)(.+) /gm;
            let regexsignal = /\[[A-Z]{3,5}\]?\s?Device\s([0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2})\sRSSI:\s-(.+)/gm;
            let regexinfo = /Device ([0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2})\r?\n?\t?Name: (.+)\r?\n?\t?Alias: (.+)\r?\n?\t?Class: (.+)\r?\n?\t?Icon: (.+)\r?\n?\t?Paired: (.+)\r?\n?\t?Trusted: (.+)\r?\n?\t?Blocked: (.+)\r?\n?\t?Connected: (.+)\r?\n?\t?/gmi;

            let regexconnected = /\[[A-Z]{3,5}\]?\s?Device\s([0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2})\sConnected:\s([a-z]{2,3})/gm;
            let regexpaired = /\[[A-Z]{3,5}\]?\s?Device\s([0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2})\sPaired:\s([a-z]{2,3})/gm;
            let regextrusted = /\[[A-Z]{3,5}\]?\s?Device\s([0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2})\sTrusted:\s([a-z]{2,3})/gm;
            let regexblocked = /\[[A-Z]{3,5}\]?\s?Device\s([0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2})\sBlocked:\s([a-z]{2,3})/gm;
            let regexpasskeyconfirmation = /\[agent\] Confirm passkey\s([0-9A-F]+)\s[^:]+:/gm;

            let regexscanon1 = 'Discovery started';
            let regexscanon2 = 'Failed to start discovery: org.bluez.Error.InProgress';
            let regexscanon3 = 'Discovering: yes';
            let regexscanoff1 = 'Discovery stopped';
            let regexscanoff2 = 'Discovering: no';

            this._checkDevice(regexdevice, data);
            this._checkinfo(data);
            this._checkSignal(regexsignal, data);
            this._checkController(regexcontroller, data);
            this._checkConnected(regexconnected, data);
            this._checkPaired(regexpaired, data);
            this._checkTrusted(regextrusted, data);
            this._checkBlocked(regexblocked, data);
            this._checkPasskeyConfirmation(regexpasskeyconfirmation, data);

            if (data.indexOf(regexscanoff1) !== -1 || data.indexOf(regexscanoff2) !== -1) this.isScanning = false;
            if (data.indexOf(regexscanon1) !== -1 || data.indexOf(regexscanon2) !== -1 || data.indexOf(regexscanon3) !== -1) this.isScanning = true;
        })
    }


    checkInfo() {
        if (this.devices.length > 0) {
            for (let i = 0; i < this.devices.length; i++) {
                if (this.devices[i].paired == '' && this.devices[i].trycount < 4) {
                    this.devices[i].trycount += 1;
                    this.info(this.devices[i].mac);
                    logger.System('checking info of ' + this.devices[i].mac)

                }
            }
        }
    }

    _checkBlocked(regstr, data) {
        let m;
        while ((m = regstr.exec(data)) !== null) {
            if (m.index === regstr.lastIndex) {
                regstr.lastIndex++;
            }
            //m[1] - macid
            //m[2] - yes or no
            let devices = this.devices;
            if (devices.length > 0) {
                for (let j = 0; j < devices.length; j++) {
                    if (devices[j].mac == m[1]) {
                        devices[j].blocked = m[2];
                        logger.System(m[1] + " blocked " + m[2]);
                        this.emit(this.bluetoothEvents.Device, devices)
                    }
                }
            }
        }
    }

    _checkPaired(regstr, data) {
        let m;
        while ((m = regstr.exec(data)) !== null) {
            if (m.index === regstr.lastIndex) {
                regstr.lastIndex++;
            }

            let devices = this.devices;
            //m[1] - macid
            //m[2] - yes or no
            if (devices.length > 0) {
                for (let j = 0; j < devices.length; j++) {
                    if (devices[j].mac == m[1]) {
                        devices[j].paired = m[2];
                        logger.System(m[1] + " paired " + m[2]);
                        this.emit(this.bluetoothEvents.Device, devices)
                    }
                }
            }
        }
    }

    _checkPasskeyConfirmation(regstr, data) {
        let m;
        while ((m = regstr.exec(data)) !== null) {
            if (m.index === regstr.lastIndex) {
                regstr.lastIndex++;
            }
            //m[1] - passkey
            //console.log("Confirm passkey : " + m[1]);
            this.emit(this.bluetoothEvents.PassKey, m[1]);
             confirmPasskey(true);

            this.isConfirmingPassKey = true;
        }
    }

    _checkTrusted(regstr, data) {
        let m;
        while ((m = regstr.exec(data)) !== null) {
            if (m.index === regstr.lastIndex) {
                regstr.lastIndex++;
            }

            let devices = this.devices;
            //m[1] - macid
            //m[2] - yes or no
            if (devices.length > 0) {
                for (let j = 0; j < devices.length; j++) {
                    if (devices[j].mac == m[1]) {
                        devices[j].trusted = m[2];
                        logger.System(m[1] + " trusted " + m[2]);
                        this.emit(this.bluetoothEvents.Device, devices)

                    }
                }
            }
        }
    }

    _checkConnected(regstr, data) {
        let m;
        while ((m = regstr.exec(data)) !== null) {
            if (m.index === regstr.lastIndex) {
                regstr.lastIndex++;
            }

            let devices = this.devices;
            //m[1] - macid
            //m[2] - yes or no
            if (devices.length > 0) {
                for (let j = 0; j < devices.length; j++) {
                    if (devices[j].mac == m[1]) {
                        devices[j].connected = m[2];
                        logger.System(m[1] + " connected " + m[2]);
                        this.emit(this.bluetoothEvents.Device, devices)
                    }
                }
            }
        }
    }

    _checkinfo(data) {
        let devices = this.devices;
        let regstr = /Device ([0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2}[\.:-][0-9A-F]{1,2})\r?\n?\t?Name: (.+)\r?\n?\t?Alias: (.+)\r?\n?\t?Class: (.+)\r?\n?\t?Icon: (.+)\r?\n?\t?Paired: (.+)\r?\n?\t?Trusted: (.+)\r?\n?\t?Blocked: (.+)\r?\n?\t?Connected: (.+)\r?\n?\t?/gmi;
        let m;
        while ((m = regstr.exec(data)) !== null) {
            if (m.index === regstr.lastIndex) {
                regstr.lastIndex++;
            }


            //m[1] - macid
            //m[2] - device name
            //m[3] - alias
            //m[4] - Class
            //m[5] - Icon
            //m[6] - paired
            //m[7] - trusted
            //m[8] - blocked
            //m[9] - connected
            if (devices.length > 0) {
                for (let j = 0; j < devices.length; j++) {
                    if (devices[j].mac == m[1]) {
                        devices[j].name = m[3];
                        devices[j].class = m[4];
                        devices[j].icon = m[5];
                        devices[j].paired = m[6];
                        devices[j].trusted = m[7];
                        devices[j].blocked = m[8];
                        devices[j].connected = m[9];
                        this.emit(this.bluetoothEvents.Device, devices);
                        //console.log ('info received:' + JSON.stringify(devices[j]))
                    }
                }
            }
        }
    }

    _checkSignal(regstr, data) {
        let m;
        let devices = this.devices;
        while ((m = regstr.exec(data)) !== null) {
            if (m.index === regstr.lastIndex) {
                regstr.lastIndex++;
            }


            //m[1] - macid
            //m[2] - signal Level
            if (devices.length > 0) {
                for (let j = 0; j < devices.length; j++) {
                    if (devices[j].mac == m[1]) {
                        devices[j].signal = parseInt(m[2]);
                        //console.log('signal level of:' + m[1] + ' is ' + m[2])
                        this.emit(this.bluetoothEvents.Device, devices);
                        this.emit(this.bluetoothEvents.DeviceSignalLevel, devices, m[1], m[2]);
                    }
                }
            }
        }
    }

    _checkController(regstr, data) {
        let m;
        let devices = this.devices;
        while ((m = regstr.exec(data)) !== null) {
            if (m.index === regstr.lastIndex) {
                regstr.lastIndex++;
            }

            this.controllers = [];
            //m[1] - macid
            //m[2] - controllername

            this.controllers.push({ mac: m[1], name: m[2] });

            this.emit(this.bluetoothEvents.Controller, this.controllers);
            //console.log('controller found:' + m[1])
            this.term.write('power on\r');
            this.term.write('agent on\r');

        }
    }

    _checkDevice(regstr, data) {
        let m;
        let devices = this.devices;
        while ((m = regstr.exec(data)) !== null) {
            if (m.index === regstr.lastIndex) {
                regstr.lastIndex++;
            }

            //m[1] - [NEW] or [DEL] etc..
            //m[2] - macid
            //m[3] - devicename
            if (m[1] == "[DEL]") {
                //remove from list
                if (devices.length > 0) {
                    for (let j = 0; j < devices.length; j++) {
                        if (devices[j].mac == m[2]) {
                            devices.splice(j, 1);
                            logger.System('deleting device ' + m[2])
                        }
                    }
                }
            } else {
                let found = false;
                if (devices.length > 0) {
                    for (let j = 0; j < devices.length; j++) {
                        if (devices[j].mac == m[2]) found = true;
                        if (devices[j].mac == m[2] && m[1] == "[NEW]") found = false;
                    }
                }
                if (!found) {
                    logger.System('adding device ' + m[2]);
                    devices.push({
                        mac: m[2],
                        name: m[3],
                        signal: 0,
                        paired: '',
                        trusted: '',
                        icon: '',
                        class: '',
                        blocked: '',
                        connected: '',
                        trycount: 0
                    });
                }
            }
        }
        if ((regstr.exec(data)) !== null) this.emit(this.bluetoothEvents.Device, devices)

    }

    //region exports

    agent(start) {
        this.term.write('agent ' + (start ? 'on' : 'off') + '\r');
    }

    power(start) {
        this.term.write('power ' + (start ? 'on' : 'off') + '\r');
    }

    scan(startScan) {
        this.term.write('scan ' + (startScan ? 'on' : 'off') + '\r');
    }
    pairable(canpairable) {
        this.term.write('pairable ' + (canpairable ? 'on' : 'off') + '\r');
    }
    discoverable(candiscoverable) {
        this.term.write('discoverable ' + (candiscoverable ? 'on' : 'off') + '\r');
    }


    pair(macID) {
        this.term.write('pair ' + macID + '\r');
    }
    confirmPassKey(confirm) {
        this.isConfirmingPassKey = false;
        this.term.write(confirm ? 'yes\r' : 'no\r');
    }

    trust(macID) {
        this.term.write('trust ' + macID + '\r');
    }

    untrust(macID) {
        this.term.write('untrust ' + macID + '\r');
    }


    block(macID) {
        this.term.write('block ' + macID + '\r');
    }
    unblock(macID) {
        this.term.write('unblock ' + macID + '\r');
    }


    connect(macID) {
        this.term.write('connect ' + macID + '\r');
    }

    disconnect(macID) {
        this.term.write('disconnect ' + macID + '\r');
    }

    remove(macID) {
        this.term.write('remove ' + macID + '\r');
    }

    info(macID) {
        this.term.write('info ' + macID + '\r');
    }


    getPairedDevices() {
        this.devices = [];
        this.term.write('paired-devices\r');
    }

    getDevicesFromController() {
        this.devices = [];
        this.term.write('devices\r');
    }

    checkBluetoothController() {
        try {
            let execSync = require("child_process").execSync;
            return !!execSync("type bluetoothctl", { encoding: "utf8" });
        }
        catch (e) {
            return false;
        }
    }

    //endregion

};

const  EventEmitter = require('events').EventEmitter;
util.inherits(Bluetooth, EventEmitter);
module.exports = Bluetooth;