/* @license This file Copyright (C) 2020-2022 Mnemosyne LLC.
   It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
   or any future license endorsed by Mnemosyne LLC.
   License text can be found in the licenses/ folder. */

export class ActionManager extends EventTarget {
  constructor() {
    super();
    this.actions = Object.seal({
      'deselect-all': {
        enabled: false,
        shortcut: 'Control+A',
        text: 'Deselect all',
      },
      'move-bottom': { enabled: false, text: 'Move to the back of the queue' },
      'move-down': { enabled: false, text: 'Move down in the queue' },
      'move-top': { enabled: false, text: 'Move to the front of the queue' },
      'move-up': { enabled: false, text: 'Move up in the queue' },
      'open-torrent': {
        enabled: true,
        shortcut: 'Alt+O',
        text: 'Open torrent…',
      },
      'pause-all-torrents': { enabled: false, text: 'Pause all' },
      'pause-selected-torrents': {
        enabled: false,
        shortcut: 'Alt+U',
        text: 'Pause',
      },
      'reannounce-selected-torrents': {
        enabled: false,
        text: 'Ask tracker for more peers',
      },
      'remove-selected-torrents': { enabled: false, text: 'Remove from list…' },
      'resume-selected-torrents': {
        enabled: false,
        shortcut: 'Alt+R',
        text: 'Resume',
      },
      'resume-selected-torrents-now': { enabled: false, text: 'Resume now' },
      'select-all': { enabled: false, shortcut: 'Alt+A', text: 'Select all' },
      'show-about-dialog': { enabled: true, text: 'About' },
      'show-inspector': {
        enabled: false,
        shortcut: 'Alt+I',
        text: 'Torrent Inspector',
      },
      'show-labels-dialog': {
        enabled: false,
        text: 'Edit Labels…',
      },
      'show-move-dialog': {
        enabled: false,
        shortcut: 'Alt+L',
        text: 'Set location…',
      },
      'show-overflow-menu': { enabled: true, text: 'More options…' },
      'show-preferences-dialog': {
        enabled: true,
        shortcut: 'Alt+P',
        text: 'Edit preferences',
      },
      'show-rename-dialog': {
        enabled: false,
        shortcut: 'Alt+N',
        text: 'Rename…',
      },
      'show-shortcuts-dialog': { enabled: true, text: 'Keyboard shortcuts' },
      'show-statistics-dialog': {
        enabled: true,
        shortcut: 'Alt+S',
        text: 'Statistics',
      },
      'start-all-torrents': { enabled: false, text: 'Start all' },
      'toggle-compact-rows': { enabled: true, text: 'Compact rows' },
      'trash-selected-torrents': {
        enabled: false,
        text: 'Trash data and remove from list…',
      },
      'verify-selected-torrents': {
        enabled: false,
        shortcut: 'Alt+V',
        text: 'Verify local data',
      },
    });
  }

  click(name) {
    if (this.isEnabled(name)) {
      const event_ = new Event('click');
      event_.action = name;
      this.dispatchEvent(event_);
    }
  }

  getActionForShortcut(shortcut) {
    for (const [name, properties] of Object.entries(this.actions)) {
      if (shortcut === properties.shortcut) {
        return name;
      }
    }
    return null;
  }

  // return a map of shortcuts to action names
  allShortcuts() {
    return new Map(
      Object.entries(this.actions)
        .filter(([, properties]) => properties.shortcut)
        .map(([name, properties]) => [properties.shortcut, name])
    );
  }

  isEnabled(name) {
    return this._getAction(name).enabled;
  }

  text(name) {
    return this._getAction(name).text;
  }

  keyshortcuts(name) {
    return this._getAction(name).shortcut;
  }

  update(event_) {
    const counts = ActionManager._recount(event_.selected, event_.nonselected);
    this._updateStates(counts);
  }

  _getAction(name) {
    const action = this.actions[name];
    if (!action) {
      throw new Error(`no such action: ${name}`);
    }
    return action;
  }

  static _recount(selected, nonselected) {
    const total = selected.length + nonselected.length;
    const selected_paused = selected.filter((tor) => tor.isStopped()).length;
    const selected_active = selected.length - selected_paused;
    const nonselected_paused = nonselected.filter((tor) =>
      tor.isStopped()
    ).length;
    const nonselected_active = nonselected.length - nonselected_paused;
    const paused = selected_paused + nonselected_paused;
    const active = selected_active + nonselected_active;
    const selected_queued = selected.filter((tor) => tor.isQueued()).length;

    return {
      active,
      nonselected_active,
      nonselected_paused,
      paused,
      selected: selected.length,
      selected_active,
      selected_paused,
      selected_queued,
      total,
    };
  }

  _updateStates(counts) {
    const set_enabled = (enabled, actions) => {
      for (const action of actions) {
        this._updateActionState(action, enabled);
      }
    };

    set_enabled(counts.selected_paused > 0, ['resume-selected-torrents']);

    set_enabled(counts.paused > 0, ['start-all-torrents']);

    set_enabled(counts.active > 0, ['pause-all-torrents']);

    set_enabled(counts.selected_paused > 0 || counts.selected_queued > 0, [
      'resume-selected-torrents-now',
    ]);

    set_enabled(counts.selected_active > 0, [
      'pause-selected-torrents',
      'reannounce-selected-torrents',
    ]);

    set_enabled(counts.selected > 0, [
      'deselect-all',
      'move-bottom',
      'move-down',
      'move-top',
      'move-up',
      'remove-selected-torrents',
      'show-inspector',
      'show-labels-dialog',
      'show-move-dialog',
      'trash-selected-torrents',
      'verify-selected-torrents',
    ]);

    set_enabled(counts.selected === 1, ['show-rename-dialog']);

    set_enabled(counts.selected < counts.total, ['select-all']);
  }

  _updateActionState(name, enabled) {
    const action = this.actions[name];
    if (!action) {
      throw new Error(`no such action: ${name}`);
    }

    if (action.enabled !== enabled) {
      action.enabled = enabled;

      const event = new Event('change');
      event.action = name;
      event.enabled = enabled;
      this.dispatchEvent(event);
    }
  }
}
