//////////////////////////////////////////////////////////////////////////////////////////
//   _  _ ____ _  _ ___  ____                                                           //
//   |_/  |__| |\ | |  \ |  |    This file belongs to Kando, the cross-platform         //
//   | \_ |  | | \| |__/ |__|    pie menu. Read more on github.com/kando-menu/kando     //
//                                                                                      //
//////////////////////////////////////////////////////////////////////////////////////////

// SPDX-FileCopyrightText: Simon Schneegans <code@simonschneegans.de>
// SPDX-License-Identifier: MIT

import { ipcRenderer, OpenDialogOptions, webFrame } from 'electron';

import { COMMON_WINDOW_API } from '../common/common-window-api';
import {
  BackendInfo,
  MenuThemeDescription,
  SoundThemeDescription,
  VersionInfo,
  WMInfo,
  SystemInfo,
  AppDescription,
  LevelProgress,
} from '../common';
import { IPCMenuManager } from './utils/ipc-menu-manager';

/**
 * These functions are available in the settings window's renderer process. They are
 * available on the window object as `window.settingsAPI.<method>`. In addition, the
 * common window API functions are also be available as `window.commonAPI.<method>`.
 */
export const SETTINGS_WINDOW_API = {
  /**
   * This will be called by the renderer process when it is fully loaded. This is used to
   * notify the host process that the renderer process is ready to receive messages.
   */
  settingsWindowReady: () => {
    ipcRenderer.send('settings-window.ready');
  },

  /** This will return some information about the currently used backend. */
  getBackendInfo: (): Promise<BackendInfo> => {
    return ipcRenderer.invoke('settings-window.get-backend-info');
  },

  /** Returns the current version string of Kando. */
  getVersionInfo: (): Promise<VersionInfo> => {
    return ipcRenderer.invoke('settings-window.get-version');
  },

  /** Returns some information about the currently used window manager. */
  getWMInfo: (): Promise<WMInfo> => {
    return ipcRenderer.invoke('settings-window.get-wm-info');
  },

  /** Returns some information about the current system. */
  getSystemInfo: (): Promise<SystemInfo> => {
    return ipcRenderer.invoke('settings-window.get-system-info');
  },

  /** Returns the position of the top left corner of the settings window. */
  getWindowPosition: (): Promise<{ x: number; y: number }> => {
    return ipcRenderer.invoke('settings-window.get-position');
  },

  /** Returns the index of the last opened menu. */
  getCurrentMenu: function (): Promise<number> {
    return ipcRenderer.invoke('settings-window.get-current-menu');
  },

  /** This will return the path to Kando's config directory. */
  getConfigDirectory: (): Promise<string> => {
    return ipcRenderer.invoke('settings-window.get-config-directory');
  },

  /** This will return the path to the user's menu themes directory. */
  getMenuThemesDirectory: (): Promise<string> => {
    return ipcRenderer.invoke('settings-window.get-menu-themes-directory');
  },

  /** This will return all available menu themes. */
  getAllMenuThemes: (): Promise<Array<MenuThemeDescription>> => {
    return ipcRenderer.invoke('settings-window.get-all-menu-themes');
  },

  /** This will return all available presets for a given menu theme directory. */
  getMenuThemePresets: (
    themeId: string
  ): Promise<Array<{ name: string; colors: Record<string, string> }>> => {
    return ipcRenderer.invoke('settings-window.get-menu-theme-presets', themeId);
  },

  /** This will return all available sound themes. */
  getAllSoundThemes: (): Promise<Array<SoundThemeDescription>> => {
    return ipcRenderer.invoke('settings-window.get-all-sound-themes');
  },

  /** This will return all installed applications. */
  getInstalledApps: (): Promise<Array<AppDescription>> => {
    return ipcRenderer.invoke('settings-window.get-installed-apps');
  },

  /** This will return the current level and achievements progress. */
  getLevelProgress: (): Promise<LevelProgress> => {
    return ipcRenderer.invoke('settings-window.get-level-progress');
  },

  /** This will be called by the host when the level progress has been changed. */
  onLevelProgressChanged: (func: (progress: LevelProgress) => void) => {
    ipcRenderer.on('settings-window.level-progress-changed', (event, progress) => {
      func(progress);
    });
  },

  /** Mark the achievements as viewed. */
  markAchievementsAsViewed: () => {
    ipcRenderer.send('settings-window.mark-achievements-as-viewed');
  },

  /** This will reset the level progress and achievements. */
  resetLevelProgress: () => {
    ipcRenderer.send('settings-window.reset-level-progress');
  },

  /** This will be called by the host when achievement dialog should be shown. */
  onShowAchievementsDialog: (func: () => void) => {
    ipcRenderer.on('settings-window.show-achievements-dialog', () => {
      func();
    });
  },

  /** Open the menu with the given index in the list of all menus. */
  openMenu: (index: number) => {
    ipcRenderer.send('settings-window.open-menu', index);
  },

  /** This will show the web developer tools. */
  showDevTools: (forWindow: 'menu-window' | 'settings-window') => {
    ipcRenderer.send('settings-window.show-dev-tools', forWindow);
  },

  /** This will reload the current menu theme. */
  reloadMenuTheme: () => {
    ipcRenderer.send('settings-window.reload-menu-theme');
  },

  /** This will reload the current menu theme. */
  reloadSoundTheme: () => {
    ipcRenderer.send('settings-window.reload-sound-theme');
  },

  /** This will reload the current icon themes. */
  reloadIconThemes: () => {
    ipcRenderer.send('settings-window.reload-icon-themes');
  },

  /**
   * This will be called by the host process when the icon themes should be reloaded.
   *
   * @param callback This callback will be called when the icon themes should be reloaded.
   */
  onReloadIconThemes: (func: () => void) => {
    webFrame.clearCache();
    ipcRenderer.on('settings-window.reload-icon-themes', func);
  },

  /** This will open a file picker and return the selected file path. */
  openFilePicker: (config: OpenDialogOptions): Promise<string> => {
    return ipcRenderer.invoke('settings-window.open-file-picker', config);
  },

  /**
   * This will open a file picker to let the user select a location to save the backup of
   * the config.json file.
   */
  backupGeneralSettings: () => {
    ipcRenderer.send('settings-window.backup-general-settings');
  },

  /**
   * This will open a file picker to let the user select a location to save the backup of
   * the menu.json file.
   */
  backupMenuSettings: () => {
    ipcRenderer.send('settings-window.backup-menu-settings');
  },

  /**
   * This will open a file picker to let the user select a previously backed up
   * config.json to restore the general settings from.
   */
  restoreGeneralSettings: () => {
    ipcRenderer.send('settings-window.restore-general-settings');
  },

  /**
   * This will open a file picker to let the user select a previously backed up menu.json
   * to restore the menu settings from.
   */
  restoreMenuSettings: () => {
    ipcRenderer.send('settings-window.restore-menu-settings');
  },

  /** This will trigger the export of the given menu to a JSON file. */
  exportMenu: (menuIndex: number): Promise<boolean> => {
    return ipcRenderer.invoke('settings-window.export-menu', menuIndex);
  },

  /** This will trigger the import of a menu from a JSON file. */
  importMenu: (): Promise<boolean> => {
    return ipcRenderer.invoke('settings-window.import-menu');
  },

  /**
   * This will export the current menu theme colors as a preset with the given name.
   *
   * @param themeId The ID of the theme.
   * @param colors The colors to export.
   * @param presetName The name to save the preset as.
   */
  exportMenuThemePreset: (
    themeId: string,
    colors: Record<string, string>,
    presetName: string
  ): Promise<void> => {
    return ipcRenderer.invoke(
      'settings-window.export-menu-theme-preset',
      themeId,
      colors,
      presetName
    );
  },

  /**
   * This will open the directory containing the presets for the given menu theme.
   *
   * @param themeId The ID of the theme.
   */
  openMenuThemePresetsDirectory: (themeId: string) => {
    ipcRenderer.send('settings-window.open-menu-theme-presets-directory', themeId);
  },

  /**
   * This will be called by the host when menu theme presets have been changed.
   *
   * @param callback This callback will be called when presets are changed (e.g., after
   *   export).
   * @returns A function to disconnect the listener.
   */
  onPresetsChanged: (callback: () => void): (() => void) => {
    ipcRenderer.on('settings-window.presets-changed', callback);

    return () => {
      ipcRenderer.off('settings-window.presets-changed', callback);
    };
  },
};

/**
 * This type extends the global window object with the `api` objects. These `api` objects
 * contain all functions that can be called by the renderer process.
 */
export type WindowWithAPIs = {
  readonly commonAPI: typeof COMMON_WINDOW_API;
  readonly settingsAPI: typeof SETTINGS_WINDOW_API;
  ipcAPI: IPCMenuManager;
} & Window;
