/**
 * Main process.
 */

  // Declare globals.
declare global {
    namespace NodeJS {
        interface Global {
            args: object;
            locale: string;
            log: {[key: string]: any};
            kernel: object;
            mainWindow: Electron.BrowserWindow;
        }
    }
}

// Load Logger.
import log from './src/helpers/logger/logger-main';
global.log = log;

// Load external modules.
import * as electron from 'electron';
import * as jetpack from 'fs-jetpack';
import * as path from 'path';
import {app, BrowserWindow, dialog, ipcMain, Menu, session} from 'electron';

// Load app modules.
import Const from './src/constants';

import Kernel from './src/kernel/kernel';
import createBrowserWindow from './src/system/browserWindow';
import SystemInfo from './src/system/systemInfo';

let win: BrowserWindow = null;
const args = process.argv.slice(1);
const serve = args.some(val => val === '--serve');

// Load debug tools. Checks if dev env internally.
const debug = require('electron-debug');
debug();


// Catch fatal exceptions.
process.on('uncaughtException', function (err) {
    log.error('uncaughtException: ', err);
    dialog.showErrorBox('Sorry, something unexpected happened…', err.stack);
    app.exit();
});

// Remote debugging for the Renderer process.
// app.commandLine.appendSwitch('remote-debugging-port', '9222');

// Allow to restore WebGL context if lost.
app.commandLine.appendSwitch('--disable-gpu-process-crash-limit');
app.disableDomainBlockingFor3DAPIs();

// Save userData in separate folders for each environment.
// Thanks to this you can use production and development versions of the app
// on same machine like those are two separate apps.
if (Const.isProdEnv === false) {
    const userDataPath = app.getPath('userData');
    app.setPath('userData', `${userDataPath} (${Const.env.name})`);
}

function createWindow(): BrowserWindow {
    let background;
    let icon;
    let isAngularAppLoaded = false;
    let isClosingE2E = false;
    let isUserClosingTheWindow = false;

    // Save the passed arguments in the globals.
    {
        let args = process.argv.slice(Const.isDevEnv ? 2 : 1);

        global.args = {
            casePaths: args.filter(a => (a.startsWith('-') === false)),
            hasRunFlag: args.some(a => (a === '-r') || (a === '--run')),
        };
    }

    // Disable system menu.
    Menu.setApplicationMenu(null);

    // Set CENOS app color ad icon.
    switch (Const.app.cenosApp) {
        case Const.CenosApp.ANTENNAS: {
            background = '#e1dedc';
            icon = path.join(__dirname, 'renderer/assets/icons/win/cenos@ANTENNAS.ico');
            break;
        }

        case Const.CenosApp.INDUCTION: {
            background = '#00558b';
            icon = path.join(__dirname, 'renderer/assets/icons/win/cenos@INDUCTION.ico');
            break;
        }

        default: {
            background = '#00558b';
            icon = path.join(__dirname, 'renderer/assets/icons/win/cenos.ico');
        }
    }

    // Create BrowserWindow.
    win = createBrowserWindow('main', {
        height: 650,
        backgroundColor: background,
        icon: icon,
        minHeight: 600, // ~560px inner height
        minWidth: 980, // ~960px inner width
        useContentSize: true,
        width: 1000,
        webPreferences: {
            enableRemoteModule: true,
            devTools: Const.isDevEnv,
            nodeIntegration: true,
            contextIsolation: false
        }
    });

    win.loadFile('renderer/index.html');

    // Save as globals.
    global.kernel = Kernel;
    global.mainWindow = win;

    // Log info.
    log.info(`${Const.app.name} v${Const.app.appVersion} is ready`);
    SystemInfo.getSystemInfo().then(sysInfo => log.info(sysInfo));

    // Save locale string as global.
    SystemInfo.getLocale().then(locale => {
        global.locale = locale;
        log.info('Locale: ' + locale);
    });

    // Open dev tools.
    if (Const.isDevEnv) {
        win.webContents.openDevTools();
    }

    // Remember if angular app is loaded.
    ipcMain.on('cen-angular-app-loaded', (event, arg) => {
        isAngularAppLoaded = true;
    });

    // Close app if renderer asks.
    ipcMain.on('cen-close-app', (event, arg) => {
        isUserClosingTheWindow = true;
        win.close();
    });

    // Allow to close app in e2e tests.
    ipcMain.on('cen-close-e2e', (event, arg) => {
        isClosingE2E = true;
    });

    // Notify renderer process to close app.
    win.on('close', (e) => {
        if (isAngularAppLoaded && (isUserClosingTheWindow === false) && (isClosingE2E === false)) {
            win.webContents.send('cen-close-app', true);
            e.preventDefault();
        }
    });

    // Emitted when the window is closed.
    win.on('closed', () => {

        // Dereference the window object, usually you would store window
        // in an array if your app supports multi windows, this is the time
        // when you should delete the corresponding element.
        win = null;
    });

    // Intercept link openings and forward them to default browser
    // Fixme: deprecated. Rework
    win.webContents.on('new-window', (e, url) => {
      e.preventDefault();
      electron.shell.openExternal(url);
    });

    return win;
}

try {

    // This method will be called when Electron has finished
    // initialization and is ready to create browser windows.
    // Some APIs can only be used after this event occurs.
    app.on('ready', async () => {
      // Load Redux DevTools extension for NgRx debugging
      if (Const.isDevEnv) {
        const electronDevtoolsInstaller = await import('electron-devtools-installer');
        const extensions = {
          ReduxDevTools: electronDevtoolsInstaller.REDUX_DEVTOOLS,
          // Mokku: 'llflfcikklhgamfmnjkgpdadpmdplmji' Currently is not supported in Electron but will be useful to use when it will be
        };
        for (const ext of Object.keys(extensions)) {
          await electronDevtoolsInstaller.default(extensions[ext], {loadExtensionOptions: {allowFileAccess: true}});
          console.log(`Added extensions: ${ext}`);
        }
      }

      // Copy legacy 'INDUCTION' config folder to the new place.
      if (Const.app.cenosApp === Const.CenosApp.INDUCTION) {
        const newDir = Const.app.configDir;
        const oldDir = jetpack.path(newDir, '..', '.cenos');
        const newConfig = jetpack.path(newDir, 'config.json');

        if (!jetpack.exists(newConfig) && jetpack.exists(oldDir)) {
          jetpack.copy(oldDir, newDir);
        }
      }

      // Ensure that folders exist.
      jetpack.dir(Const.app.configDir);
      jetpack.dir(Const.app.usageDir);
      jetpack.dir(Const.app.userDir);
      jetpack.dir(Const.app.tempDir);

      createWindow();
    });

    // Quit when all windows are closed.
    app.on('window-all-closed', () => {
        // On OS X it is common for applications and their menu bar
        // to stay active until the user quits explicitly with Cmd + Q
        if (process.platform !== 'darwin') {
            app.quit();
        }
    });

    app.on('activate', () => {
        // On OS X it's common to re-create a window in the app when the
        // dock icon is clicked and there are no other windows open.
        if (win === null) {
            createWindow();
        }
    });

} catch (e) {
    // Catch Error
    // throw e;
}
