const pjson = require('../../package.json');
const fs = require('fs'),
    path = require('path'),
    os = require('os'),
    Logger = require('../logs/clsLogger').clsLogger;
let logger = new Logger();
require('utils-strings');
require('linqjs');

let _ = require('lodash');

class smgutils {
    constructor() {
        this.appversion = pjson.version;
        let appRootDir = process.cwd();
        this.SchedulesSaveDir = path.join(appRootDir, 'files');
        if (!fs.existsSync(this.SchedulesSaveDir)) {
            fs.mkdirSync(this.SchedulesSaveDir);
        }

        this.AnnouncesSaveDir = path.join(this.SchedulesSaveDir, 'announces');
        if (!fs.existsSync(this.AnnouncesSaveDir)) {
            fs.mkdirSync(this.AnnouncesSaveDir);
        }
        this._filepath = path.join(appRootDir, 'config.ini');
        this.config = {};

        this.UserType =
            {
                Windows: 1,
                Linux: 2,
                LinuxConsole: 3,
                WindowsService: 4,
                Unknown: 5,
                LinuxArm: 6,
                MacOSX: 7,
                Linux_X64: 11,
                Linux_X86: 12,
                Linux_ArmV6: 13,
                Linux_ArmV7: 14,
                Linux_ArmV8: 15,
                Windows_X64: 16,
                Windows_X86: 17,
                WindowsXP_X86: 18,
                WindowsXP_X64: 19
            };


        this.getPlatformInit();


        this._key = "7e4bac048ef766e83f0ec8c079e1f90c2eb690a9";
        this._method = "aes";
    }

    async getPlatformInit(){
        await this.getPlatform();
       this.serial= (await this.getSerial())
    }

    async readConfig() {

        let tconfig = {};
        tconfig.Settings = {};
        tconfig.Settings.deviceID = 0;
        tconfig.Settings.soundCard = 1;
        tconfig.Settings.serviceUrl = 'http://newmanager.smg.com.tr/smgpush';
        tconfig.Settings.downloadURL = 'http://cdn.smg.com.tr/';
        tconfig.Settings.updateUrl = 'http://newmanager.smg.com.tr/update/';
        tconfig.Settings.Comment = '';
        tconfig.Settings.NextButtonVisible = 1;
        tconfig.Settings.AllowMultipleInstance = 0;
        tconfig.Settings.ServicePort = 9090;
        tconfig.Settings.LCDEnabled = 1;
        tconfig.Settings.DisableWebserver = 0;
        tconfig.Settings.ClientType = this.osType;
        tconfig.Settings.Buffer = 500;
        tconfig.Settings.VolumeControl = 1;

        tconfig.DJ = {};
        tconfig.DJ.DJAutoStart = 0;
        tconfig.DJ.DJLoop = 0;
        tconfig.DJ.DJScheduleID = 14393;
        tconfig.DJ.DJEnabled = 0;

        tconfig.proxy = {};
        tconfig.proxy.proxyEnabled = 0;
        tconfig.proxy.proxyHost = '8.6.7.0.1';
        tconfig.proxy.proxyUser = 'smg';
        tconfig.proxy.proxyPass = 'smg';
        tconfig.proxy.proxyPort = 808;

        tconfig.iceCast = {};
        tconfig.iceCast.isEnabled = 0;
        tconfig.iceCast.Genre = 'SMG Live';
        tconfig.iceCast.ServerAddress = "localhost";
        tconfig.iceCast.ServerPort = 8000;
        tconfig.iceCast.ServerPass = 'hackme';
        tconfig.iceCast.ServerPublic = true;
        tconfig.iceCast.ServerMountPoint = 'smg';
        tconfig.iceCast.ServerSongUrl = 'http://ice.smg.com.tr';
        tconfig.iceCast.Bitrate = 56;
        tconfig.iceCast.Mode = 2;
        tconfig.iceCast.Quality = 12;
        tconfig.iceCast.SampleRate = 22050;
        tconfig.iceCast.hasLocalOutput = 0;

        tconfig.Bluetooth = {};
        tconfig.Bluetooth.isAutoConnectEnabled = 0;
        tconfig.Bluetooth.macID = "";

        tconfig.MultiOutput = {};
        tconfig.MultiOutput.isEnabled = 0;
        tconfig.MultiOutput.Channel = 1;

        tconfig.Plugins = {};
        tconfig.Plugins.AAC=0;
        tconfig.Plugins.AC3=0;
        tconfig.Plugins.APE=0;
        tconfig.Plugins.MPC=0;
        tconfig.Plugins.SPX=0;
        tconfig.Plugins.TTA=0;
        tconfig.Plugins.FX=0;

        tconfig.DebugMode = {};
        tconfig.DebugMode.Debug = 0;

        tconfig.Watchdog = {};
        tconfig.Watchdog.delay=0;
        tconfig.Watchdog.exePath=path.join(process.cwd(), 'SMGPlayer.exe');
        tconfig.Watchdog.updatePath=path.join(process.cwd(), 'update');

        tconfig.ConnectRetry = {};
        tconfig.ConnectRetry.First=1000;
        tconfig.ConnectRetry.Second=2000;

        tconfig.SubscribeEvents = {};
        tconfig.SubscribeEvents.Subscribe=0;

        tconfig.PrayerTime = {};
        tconfig.PrayerTime.Enabled = 0;
        tconfig.PrayerTime.countryID = 0;
        tconfig.PrayerTime.cityID = 0;
        tconfig.PrayerTime.townID = 0;
        tconfig.PrayerTime.townUrl = '';
        tconfig.PrayerTime.townName = '';
        tconfig.PrayerTime.volume = 100;
        tconfig.PrayerTime.volumeDown = -10;
        tconfig.PrayerTime.volumeUp = 10;
        tconfig.PrayerTime.checkHour = 19;
        tconfig.PrayerTime.checkMinute = 0;
        tconfig.PrayerTime.prayerUrl = 'http://newmanager.smg.com.tr/prayertimes/';

        tconfig.InputPlayer = {};
        tconfig.InputPlayer.Enabled = 0;
        tconfig.InputPlayer.recordingCard = -1;

        tconfig.SSDPServer = {};
        tconfig.SSDPServer.Enabled = 1;



        let ini = require('ini');
        if (!fs.existsSync(this._filepath)) {
            fs.writeFileSync(this._filepath, ini.stringify(tconfig, {section: ''}));
        }

        tconfig = ini.parse(fs.readFileSync(this._filepath, 'utf-8'));

        if(tconfig.Settings.VolumeControl == undefined) {
            tconfig.Settings.VolumeControl = 1;
            fs.writeFileSync(this._filepath, ini.stringify(tconfig, {section: ''}));
        }
        if (tconfig.Settings.Buffer == undefined) {
            tconfig.Settings.Buffer = 500;
            fs.writeFileSync(this._filepath, ini.stringify(tconfig, {section: ''}));
        }

        if (tconfig.Bluetooth == undefined) {
            tconfig.Bluetooth = {};
            tconfig.Bluetooth.isAutoConnectEnabled = 0;
            tconfig.Bluetooth.macID = "";
            fs.writeFileSync(this._filepath, ini.stringify(tconfig, {section: ''}));
        }

        if (tconfig.MultiOutput == undefined) {
            tconfig.MultiOutput = {};
            tconfig.MultiOutput.isEnabled = 0;
            tconfig.MultiOutput.Channel = 1;
            fs.writeFileSync(this._filepath, ini.stringify(tconfig, {section: ''}));
        }

        if (tconfig.iceCast == undefined) {
            tconfig.iceCast = {};
            tconfig.iceCast.isEnabled = 0;
            tconfig.iceCast.Genre = 'SMG Live';
            tconfig.iceCast.ServerAddress = "localhost";
            tconfig.iceCast.ServerPort = 8000;
            tconfig.iceCast.ServerPass = 'hackme';
            tconfig.iceCast.ServerPublic = true;
            tconfig.iceCast.ServerMountPoint = 'smg';
            tconfig.iceCast.ServerSongUrl = 'http://ice.smg.com.tr';
            tconfig.iceCast.Bitrate = 56;
            tconfig.iceCast.Mode = 2;
            tconfig.iceCast.Quality = 12;
            tconfig.iceCast.SampleRate = 22050;
            tconfig.iceCast.hasLocalOutput = 0;
            fs.writeFileSync(this._filepath, ini.stringify(tconfig, {section: ''}));
        }

        if(tconfig.Plugins==undefined){
            tconfig.Plugins={};
            tconfig.Plugins.AAC=0;
            tconfig.Plugins.AC3=0;
            tconfig.Plugins.APE=0;
            tconfig.Plugins.MPC=0;
            tconfig.Plugins.SPX=0;
            tconfig.Plugins.TTA=0;
            tconfig.Plugins.FX=0;
            fs.writeFileSync(this._filepath, ini.stringify(tconfig, {section: ''}));
        }

        if(tconfig.ConnectRetry == undefined) {
            tconfig.ConnectRetry = {};
            tconfig.ConnectRetry.First=1000;
            tconfig.ConnectRetry.Second=2000;
            fs.writeFileSync(this._filepath, ini.stringify(tconfig, {section: ''}));
        }
        if(tconfig.SubscribeEvents == undefined) {
            tconfig.SubscribeEvents = {};
            tconfig.SubscribeEvents.Subscribe=0;
            fs.writeFileSync(this._filepath, ini.stringify(tconfig, {section: ''}));
        }

        if(tconfig.DebugMode == undefined) {
            tconfig.DebugMode = {};
            tconfig.DebugMode.Debug=0;
            fs.writeFileSync(this._filepath, ini.stringify(tconfig, {section: ''}));
        }

        if(tconfig.PrayerTime == undefined) {
            tconfig.PrayerTime = {};
            tconfig.PrayerTime.Enabled = 0;
            tconfig.PrayerTime.countryID = 0;
            tconfig.PrayerTime.cityID = 0;
            tconfig.PrayerTime.townID = 0;
            tconfig.PrayerTime.townUrl = '';
            tconfig.PrayerTime.townName = '';
            tconfig.PrayerTime.volume = 100;
            tconfig.PrayerTime.volumeDown = -10;
            tconfig.PrayerTime.volumeUp = 10;
            tconfig.PrayerTime.checkHour = 19;
            tconfig.PrayerTime.checkMinute = 0;
            tconfig.PrayerTime.prayerUrl = 'http://newmanager.smg.com.tr/prayertimes/';
            fs.writeFileSync(this._filepath, ini.stringify(tconfig, {section: ''}));
        }

        if(tconfig.InputPlayer == undefined) {
            tconfig.InputPlayer = {};
            tconfig.InputPlayer.Enabled = 0;
            fs.writeFileSync(this._filepath, ini.stringify(tconfig, {section: ''}));
        }

        if(tconfig.SSDPServer == undefined) {
            tconfig.SSDPServer = {};
            tconfig.SSDPServer.Enabled = 1;
            fs.writeFileSync(this._filepath, ini.stringify(tconfig, {section: ''}));
        }

        tconfig.MultiOutput.isEnabled = tconfig.MultiOutput.isEnabled == 0 ? false : true;
        tconfig.Settings.ClientType = this.osType;
        this.config = tconfig;
        return tconfig
    }

    get isCompiled() {
        return ((typeof process.pkg) == 'object');
    }

    get isArm() {
        const os = require('os');
        return os.arch() === 'arm';
    }

    getIPs() {

        let os = require('os');
        let q = _.flattenDepth(_.toArray(os.networkInterfaces())).where(c => !c.internal && c.family == 'IPv4').select((c) => {
            return {
                mac: c.mac,
                ip: c.address
            }
        });
        if(q.length > 0) {
            return q;
        }
        return [{mac: '00:00:00:00:00:00', ip: '0.0.0.0'}];
    }

    async unzipfile(file, unzippath, deleteFolder, deleteSource, deleteSourceIfUnzipFails) {
        let fs = require('fs-extra');
        let unzip = require('unzip');

        if (!(await fs.pathExists(file))) {
            return {err: 1, msg: 'file not found'}
        }
        if (deleteFolder) {
            await fs.remove(unzippath)
        }

        await fs.ensureDir(unzippath);

        let result = await (new Promise((resolve, reject) => {
            fs.createReadStream(file).pipe(unzip.Extract({path: unzippath}).on('error', (err) => {
                resolve({err: 2, msg: err.toString()})


            })).on('close', () => {
                resolve({err: 0, msg: 'success'})
            });
        }));

        if (result.err == 0 && deleteSource) {
            await fs.remove(file)
        }
        if (result.err == 2 && deleteSourceIfUnzipFails) {
            await fs.remove(file)
        }
        return result;

    }

    deleteFolder(fpath) {
        if (fs.existsSync(fpath)) {
            fs.readdirSync(fpath).forEach(function (file, index) {
                let curPath = path.join(fpath, file);
                if (fs.lstatSync(curPath).isDirectory()) { // recurse
                    this.deleteFolder(curPath);
                } else { // delete file
                    fs.unlinkSync(curPath);
                }
            });
            fs.rmdirSync(fpath);
        }
    };

    async CompareAndDeleteFiles(fileArray, folderPath) {
        let realfiles = await this.getFilesInfo(folderPath);
        let deletefiles = realfiles.where(function (i) {
            return !fileArray.contains(i.file)
        });
        logger.System('Compare and delete files, total:' + fileArray.length);
        this.deleteFilesArray(deletefiles, folderPath);
    }

    deleteFilesArray(filearray, folderpath) {
        let fse = require('fs-extra');
        let path = require('path');
        logger.System('deleting file Array, total:' + filearray.length);
        filearray.forEach((i) => {
            try{
                let fpath = path.join(folderpath, i.FileName);
                if (fse.pathExistsSync(fpath)) {
                    try {
                        fse.removeSync(fpath);
                    } catch (err) {
                        logger.System(`Error deleting file. err: ${err} file: ${i.FileName}`);
                    }
                }

            }
            catch(e){
                logger.System('Error in smgutils delete files array method');
            }
        });
    }

    // deleteFiles(filearray, folderpath) {
    //     let fs = require('fs');
    //     let path = require('path');
    //     logger.System('deleting files, total:' + filearray.length);
    //     filearray.forEach(function (i) {
    //         try {
    //             let fpath = path.join(folderpath, i);
    //             fs.unlinkSync(fpath);
    //         } catch (err) {
    //             logger.System(err);
    //         }
    //     })
    // }


    async ArmInfo() {
        const {exec, spawn} = require('child-process-async');
        let child = await exec('cat /proc/cpuinfo', {});
        let output = child.stdout;
        const regex = /processor\t: (.+)\nmodel name\s:\s([^\s]+).*\n.*\n.*\n.*\nCPU architecture:\s(.*)\n.*\n.*\n.*\n\nHardware\s:\s([^\s]+)\nRevision\s:\s([^\s]+)\nSerial\s\s:\s([^\s]+)/gm;
        let m = regex.exec(output);
        if (m) {
            let obj = {
                cpucount: parseInt(m[1]) + 1,
                cputype: m[2],
                cpuarch: m[3],
                hardware: m[4],
                revision: m[5],
                serial: m[6]
            };
            return obj;
        } else {
            const tinkerBoardRegex = /processor\t:\s(.+)\nmodel name\t:\s([^\s]+).*\n.*\n.*\n.*\nCPU architecture:\s(.*)\n.*\n.*\nCPU revision\t:\s(.*)\n\nHardware\t:\s([^\s]+).*\nRevision\t:\s(.*)\nSerial\t\t:\s([^\s]+)/gm;
            m = tinkerBoardRegex.exec(output);
            if(m) {
                let obj = {
                    cpucount: parseInt(m[1]) +1,
                    cputype: m[2],
                    cpuarch: m[3],
                    hardware: m[5],
                    revision: m[4],
                    serial: m[7]
                };
                return obj;
            }
            return null;
        }
    }

    async getPlatform() {
        let pl = os.platform();
        let arch = os.arch();
        switch (os.platform()) {
            case 'win32':

                if (os.arch() == 'x64' && os.release().substring(0, 2) == '5.') {
                    this.osType = this.UserType.WindowsXP_X64;
                }
                else if (os.arch() == 'x86' && os.release().substring(0, 2) == '5.') {
                    this.osType = this.UserType.WindowsXP_X86;
                }
                else if (os.arch() == 'x64') {
                    this.osType = this.UserType.Windows_X64;
                }
                else if (os.arch() == 'x86') {
                    this.osType = this.UserType.Windows_X86;
                }
                else {
                    this.osType = this.UserType.Windows;
                }

                break;
            case 'darwin':
                this.osType = this.UserType.MacOSX;

                break;
            case 'linux':
                if (os.arch() == 'ia32') {
                    this.osType = this.UserType.Linux_X86;
                }
                else if (os.arch() == 'x64') {
                    this.osType = this.UserType.Linux_X64;
                }
                else if (os.arch() == 'arm') {
                    let osinfo = await this.ArmInfo();
                    if (osinfo.cputype == 'ARMv7') {
                        this.osType = this.UserType.Linux_ArmV7
                    } else if (osinfo.cputype.substr(0, 5) == 'ARMv6') {
                        this.osType = this.UserType.Linux_ArmV6;
                    } else {
                        this.osType = this.UserType.LinuxArm;
                    }
                }
                else {
                    this.osType = this.UserType.Unknown
                }
                break;
            default :
                break;
        }
    }

    async getFilesInfo(folderpath) {
        let klaw = require('klaw');
        let path = require('path');
        let q = await (new Promise((resolve, reject) => {
            let arr = [];
            klaw(folderpath, {depthLimit: 0})
                .on('data', (item) => {
                    try {
                    if(!item.stats.isDirectory()){
                            arr.push({
                                file: item.path.split(path.sep).pop(),
                                size: item.stats.size,
                                birthtime: item.stats.birthtime,
                                atime: item.stats.atime,
                                chtime: item.stats.ctime
                            });
                    }
                        }catch (e) {
                            logger.System('Error getting files info, err: ' + e);
                        }
                })
                .on('end', () => {
                    resolve(arr);
                });
        }));
        return q;

    }

    getIP() {
        let arr = '';
        let ifaces = os.networkInterfaces();
        Object.keys(ifaces).forEach((ifname) => {
            ifaces[ifname].where((i) => {
                return i.family == 'IPv4' && i.internal == false
            }).forEach((i) => {
                arr += i.address + ':::'
            })
        });
        if (arr != '') {
            arr = arr.substring(0, arr.length - 3)
        }
        return arr
    }

    Encrypt(text) {
        return text.encrypt(this._key, this._method);
    }

    Decrypt(text) {
        return text.decrypt(this._key, this._method);
    }

    async getFiles(pathOfFolder){
        let klaw = require('klaw');
        let path=require('path');
        let q = await (new Promise((resolve, reject) => {
            let files = [];
            let q = klaw(pathOfFolder).on('data', (item) => {
                files.push({path: item.path, size: item.stats.size,
                    isFile:item.stats.isDirectory()?false:true,
                    filename:item.path.split(path.sep).pop(),
                    directory:item.path.replace(item.path.split(path.sep).pop(),'')
                })
            }).on('end',()=>{
                resolve(files);
            })

        }));
        return q;
    }

    async getSerial() {
        const cryptorjs = require('cryptorjs');
        let data = '';
        const { exec , spawn } = require('child-process-async');
        await this.getPlatform();
        let  platform = process,
            win32RegBinPath = {
                native: '%windir%\\System32',
                mixed: '%windir%\\sysnative\\cmd.exe /c %windir%\\System32'
            },
            guid = {
                darwin: 'ioreg -rd1 -c IOPlatformExpertDevice',
                win32: `${win32RegBinPath[this.isWindowsProcessMixedOrNativeArchitecture()]}\\REG ` +
                'QUERY HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography ' +
                '/v MachineGuid',
                linux: '( cat /var/lib/dbus/machine-id /etc/machine-id 2> /dev/null || hostname ) | head -n 1 || :',
                freebsd: 'kenv -q smbios.system.uuid',
                armv7:'cat /proc/cpuinfo'
            };
        switch (this.osType) {
            case this.UserType.MacOSX:
            data = cryptorjs.hash((await exec(guid['darwin'], {})).stdout.split('IOPlatformUUID')[1].split('\n')[0].replace(/\=|\s+|\"/ig, '').toLowerCase(),'sha1');
                break;
            case this.UserType.Windows_X64:
            case this.UserType.Windows_X86:
            data = cryptorjs.hash((await exec(guid['win32'], {})).stdout.toString().split('REG_SZ')[1].replace(/\r+|\n+|\s+/ig, '').toLowerCase(),'sha1');
                break;
            case this.UserType.Linux_X64:
            case this.UserType.Linux_X86:
            data = cryptorjs.hash((await exec(guid['linux'], {})).stdout.toString().replace(/\r+|\n+|\s+/ig, '').toLowerCase(),'sha1');
                break;
            case this.UserType.Linux_ArmV7:
            data = cryptorjs.hash((await this.ArmInfo()).serial,'sha1');
            default:
                break;
        }
        if(data) return data;
    }

    writeConfig(config) {
        fs.writeFileSync(this._filepath, ini.stringify(config, {section: ''}), 'utf8');
    }

    isWindowsProcessMixedOrNativeArchitecture() {
        // detect if the node binary is the same arch as the Windows OS.
        // or if this is 32 bit node on 64 bit windows.
        if(process.platform !== 'win32') {
            return '';
        }
        if( process.arch === 'ia32' && process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432') ) {
            return 'mixed';
        }
        return 'native';
    }
}

module.exports = smgutils;
