import { app, ipcMain, IpcMainEvent } from 'electron';
import * as path from 'path';
import * as url from 'url';
import * as fse from 'fs-extra';

import { AppWindow } from './app/AppWindow';
import { now } from './util/now';
import { showUncaughtException } from './crash/show-uncaught-exception';
import { Channel } from '@ucap-webmessenger/native-electron';
import { root } from './util/root';

let appWindow: AppWindow | null = null;

const launchTime = now();
let readyTime: number | null = null;

type OnDidLoadFn = (window: AppWindow) => void;
let onDidLoadFns: Array<OnDidLoadFn> | null = [];

let preventQuit = false;
function handleUncaughtException(error: Error) {
  preventQuit = true;

  // If we haven't got a window we'll assume it's because
  // we've just launched and haven't created it yet.
  // It could also be because we're encountering an unhandled
  // exception on shutdown but that's less likely and since
  // this only affects the presentation of the crash dialog
  // it's a safe assumption to make.
  const isLaunchError = appWindow === null;

  if (appWindow) {
    appWindow.destroy();
    appWindow = null;
  }

  showUncaughtException(isLaunchError, error);
}

function getUptimeInSeconds() {
  return (now() - launchTime) / 1000;
}

process.on('uncaughtException', (error: Error) => {
  // error = withSourceMappedStack(error);
  // reportError(error, getExtraErrorContext());
  handleUncaughtException(error);
});

let isDuplicateInstance = false;
const gotSingleInstanceLock = app.requestSingleInstanceLock();
isDuplicateInstance = !gotSingleInstanceLock;

app.on('second-instance', (event, args, workingDirectory) => {
  // Someone tried to run a second instance, we should focus our window.
  if (appWindow) {
    if (appWindow.isMinimized()) {
      appWindow.restore();
    }

    if (!appWindow.isVisible()) {
      appWindow.show();
    }

    appWindow.focus();
  }
});

if (isDuplicateInstance) {
  app.quit();
}

function createWindow() {
  const window = new AppWindow();

  if (__DEV__) {
    const {
      default: installExtension,
      REDUX_DEVTOOLS
    } = require('electron-devtools-installer');

    require('electron-debug')({ showDevTools: true });

    const ChromeLens = {
      id: 'idikgljglpfilbhaboonnpnnincjhjkd',
      electron: '>=1.2.1'
    };

    const extensions = [REDUX_DEVTOOLS, ChromeLens];

    for (const extension of extensions) {
      try {
        installExtension(extension);
      } catch (e) {}
    }
  }

  window.onClose(() => {
    appWindow = null;
    if (!__DARWIN__ && !preventQuit) {
      app.quit();
    }
  });

  window.onDidLoad(() => {
    window.show();

    const fns = onDidLoadFns;
    onDidLoadFns = null;
    for (const fn of fns) {
      fn(window);
    }
  });

  window.load();

  appWindow = window;
}

// 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', () => {
  if (isDuplicateInstance) {
    return;
  }

  readyTime = now() - launchTime;

  createWindow();

  ipcMain.on('uncaught-exception', (event: IpcMainEvent, error: Error) => {
    handleUncaughtException(error);
  });

  ipcMain.on(
    'send-error-report',
    (
      event: IpcMainEvent,
      { error, extra }: { error: Error; extra: { [key: string]: string } }
    ) => {}
  );
});

// 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', () => {
  onDidLoad(window => {
    window.show();
  });
});

function onDidLoad(fn: OnDidLoadFn) {
  if (onDidLoadFns) {
    onDidLoadFns.push(fn);
  } else {
    if (appWindow) {
      fn(appWindow);
    }
  }
}

ipcMain.on(Channel.checkForUpdates, (event: IpcMainEvent, ...args: any[]) => {
  event.returnValue = false;
});

ipcMain.on(Channel.readFile, (event: IpcMainEvent, ...args: any[]) => {
  try {
    const rBuf = fse.readFileSync(root(args[0]));
    event.returnValue = rBuf.buffer;
  } catch (error) {
    event.returnValue = null;
  }
});

ipcMain.on(Channel.saveFile, (event: IpcMainEvent, ...args: any[]) => {
  try {
    fse.writeFileSync(root(args[0]), args[1]);
    event.returnValue = true;
  } catch (error) {
    event.returnValue = false;
  }
});
