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

import { AppWindow } from './app/AppWindow';
import { now } from './util/now';
import { showUncaughtException } from './crash/show-uncaught-exception';

import {
  UpdaterChannel,
  FileChannel,
  IdleStateChannel,
  NotificationChannel,
  ChatChannel,
  MessengerChannel
} from '@ucap-webmessenger/native-electron';
import { ElectronNotificationService } from '@ucap-webmessenger/electron-notification';

import { root } from './util/root';
import { DefaultFolder } from './lib/default-folder';
import { FileUtil } from './lib/file-util';

import { IdleChecker } from './lib/idle-checker';
import { NotificationRequest } from '@ucap-webmessenger/native';
import { ElectronAppChannel } from '@ucap-webmessenger/electron-core';

const appIconPath = __LINUX__
  ? path.join(__dirname, 'static', 'icon-logo.png')
  : path.join(__dirname, 'resources/image', 'ico_64_64.png');

let appWindow: AppWindow | null = null;
let appTray: Tray | null = null;

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

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

let preventQuit = false;

let notificationService: ElectronNotificationService | null;

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;
let idle: IdleChecker | null;

app.on(ElectronAppChannel.SecondInstance, (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(appIconPath);

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

    import('electron-debug').then(ed => {
      ed.default({ showDevTools: true });
    });

    import('electron-devtools-installer').then(edi => {
      const ChromeLens = {
        id: 'idikgljglpfilbhaboonnpnnincjhjkd',
        electron: '>=1.2.1'
      };

      const extensions = [edi.REDUX_DEVTOOLS, ChromeLens];

      for (const extension of extensions) {
        try {
          edi.default(extension);
        } catch (e) {
          console.log(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;
}

function createTray() {
  appTray = new Tray(appIconPath);

  const contextMenu = Menu.buildFromTemplate([
    {
      label: '로그아웃',
      // accelerator: 'Q',
      // selector: 'terminate:',
      click: () => {
        appWindow.show();
        appWindow.browserWindow.webContents.send(MessengerChannel.Logout);
      }
    },
    {
      label: '설정',
      // accelerator: 'Q',
      // selector: 'terminate:',
      click: () => {
        appWindow.show();
        appWindow.browserWindow.webContents.send(MessengerChannel.ShowSetting);
      }
    },
    { label: '버전', submenu: [{ label: 'Ver. ' + app.getVersion() }] },
    {
      label: '종료',
      // accelerator: 'Q',
      // selector: 'terminate:',
      click: () => {
        // 메신저에 로그아웃 후 종료
        appWindow = null;
        app.exit();
      }
    }
  ]);
  appTray.setToolTip('UCapMessenger');
  appTray.setContextMenu(contextMenu);

  appTray.on('click', () => {
    appWindow.isVisible() ? appWindow.hide() : appWindow.show();
  });
}

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

  readyTime = now() - launchTime;

  createWindow();

  createTray();

  notificationService = new ElectronNotificationService({
    width: 340,
    height: 100,
    padding: 0,
    borderRadius: 0,
    // appIcon: iconPath,
    displayTime: 5000,
    defaultStyleContainer: {},
    defaultStyleAppIcon: { display: 'none' },
    defaultStyleImage: {},
    defaultStyleClose: {},
    defaultStyleText: {}
  });

  notificationService.options.defaultWindow.webPreferences.preload = path.join(
    __dirname,
    'resources/notification/preload.js'
  );

  notificationService.templatePath = path.join(
    __dirname,
    'resources/notification/template.html'
  );

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

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

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

ipcMain.on(FileChannel.ReadFile, (event: IpcMainEvent, ...args: any[]) => {
  try {
    fse.readFile(root(args[0]), (err, data) => {
      if (!!err) {
        event.returnValue = null;
      } else {
        event.returnValue = new Blob([data]);
      }
    });
  } catch (error) {
    event.returnValue = null;
  }
});

ipcMain.on(
  FileChannel.SaveFile,
  async (event: IpcMainEvent, ...args: any[]) => {
    try {
      const buffer: Buffer = args[0];
      const fileName: string = args[1];
      const mimeType: string = args[2];
      let savePath: string = path.join(
        !!args[3] ? args[3] : DefaultFolder.downloads(),
        fileName
      );
      savePath = await FileUtil.uniqueFileName(savePath);

      fse.writeFile(savePath, buffer, err => {
        if (!err) {
          event.returnValue = savePath;
        } else {
          event.returnValue = undefined;
        }
      });
    } catch (error) {
      event.returnValue = undefined;
    }
  }
);

ipcMain.on(
  IdleStateChannel.StartCheck,
  (event: IpcMainEvent, ...args: any[]) => {
    if (!!idle) {
      idle.destoryChecker();
      idle = null;
    }
    idle = new IdleChecker(appWindow.browserWindow); // default 10min
    idle.startChecker();
  }
);

ipcMain.on(
  NotificationChannel.Notify,
  (event: IpcMainEvent, ...args: any[]) => {
    const noti: NotificationRequest = args[0];

    notificationService.notify({
      title: noti.title,
      text: noti.contents,
      image:
        noti.image ||
        path.join(__dirname, 'resources/notification/image/img_nophoto_50.png'),
      sound: noti.useSound
        ? path.join(
            'file://',
            __dirname,
            'resources/notification/sound/messageAlarm.mp3'
          )
        : '',
      onClick: e => {
        appWindow.browserWindow.webContents.send(
          ChatChannel.OpenRoom,
          noti.roomSeq
        );
        appWindow.show();
        e.close();
      }
    });
  }
);

ipcMain.on(
  NotificationChannel.CloseAllNotify,
  (event: IpcMainEvent, ...args: any[]) => {
    console.log('Channel.closeAllNotify', args);
  }
);
