// https://github.com/eonpatapon/gnome-shell-extension-caffeine

import Gdk from 'gi://Gdk';
import Gtk from 'gi://Gtk';
import GObject from 'gi://GObject';

const genParam = (type, name, ...dflt) =>
  GObject.ParamSpec[type](
    name,
    name,
    name,
    GObject.ParamFlags.READWRITE,
    ...dflt,
  );

const _ = (t) => t;

export let ShortcutSettingWidget = class extends Gtk.Button {
  static {
    GObject.registerClass(
      {
        Properties: {
          shortcut: genParam('string', 'shortcut', ''),
        },
        Signals: {
          changed: { param_types: [GObject.TYPE_STRING] },
        },
      },
      this,
    );
  }

  constructor(content, settings, key, window) {
    super({ valign: Gtk.Align.CENTER, has_frame: false });
    this._key = key;
    this._settings = settings;
    this.window = window;

    this.content = content;
    this.connect('clicked', this._onActivated.bind(this));

    let label = new Gtk.ShortcutLabel({ disabled_text: _('New accelerator…') });
    this._label = label;
    this.set_child(label);

    this.bind_property(
      'shortcut',
      label,
      'accelerator',
      GObject.BindingFlags.DEFAULT,
    );
    [this.shortcut] = this._settings.get_strv(this._key);
  }

  _onActivated(widget) {
    let ctl = new Gtk.EventControllerKey();

    if (!this._editor) {
      this._editor = new Gtk.Window({
        title: 'Accelerator',
        modal: true,
        hide_on_close: true,
        transient_for: widget.get_root(),
        width_request: 480,
        height_request: 320,
        child: this.content,
      });
    }

    this._editor.add_controller(ctl);
    ctl.connect('key-pressed', this._onKeyPressed.bind(this));
    this._editor.present();
  }

  _onKeyPressed(_widget, keyval, keycode, state) {
    let mask = state & Gtk.accelerator_get_default_mod_mask();
    mask &= ~Gdk.ModifierType.LOCK_MASK;

    if (!mask && keyval === Gdk.KEY_Escape) {
      this._editor.close();
      return Gdk.EVENT_STOP;
    }

    if (keyval === Gdk.KEY_BackSpace) {
      this.saveShortcut(); // Clear shortcut
      return Gdk.EVENT_STOP;
    }

    if (
      !this.isValidBinding(mask, keycode, keyval) ||
      !this.isValidAccel(mask, keyval)
    ) {
      return Gdk.EVENT_STOP;
    }

    this.saveShortcut(keyval, keycode, mask);
    return Gdk.EVENT_STOP;
  }

  saveShortcut(keyval, keycode, mask) {
    if (!keyval && !keycode) {
      this.shortcut = '';
    } else {
      this.shortcut = Gtk.accelerator_name_with_keycode(
        null,
        keyval,
        keycode,
        mask,
      );
    }

    console.log('saved new shortcut');

    this.emit('changed', this.shortcut);
    this._settings.set_strv(this._key, [this.shortcut]);
    // this._editor.destroy();
    this._editor.close();
  }

  // Functions from https://gitlab.gnome.org/GNOME/gnome-control-center/-/blob/main/panels/keyboard/keyboard-shortcuts.c

  keyvalIsForbidden(keyval) {
    return [
      // Navigation keys
      Gdk.KEY_Home,
      Gdk.KEY_Left,
      Gdk.KEY_Up,
      Gdk.KEY_Right,
      Gdk.KEY_Down,
      Gdk.KEY_Page_Up,
      Gdk.KEY_Page_Down,
      Gdk.KEY_End,
      Gdk.KEY_Tab,

      // Return
      Gdk.KEY_KP_Enter,
      Gdk.KEY_Return,

      Gdk.KEY_Mode_switch,
    ].includes(keyval);
  }

  isValidBinding(mask, keycode, keyval) {
    return !(
      mask === 0 ||
      (mask === Gdk.SHIFT_MASK &&
        keycode !== 0 &&
        ((keyval >= Gdk.KEY_a && keyval <= Gdk.KEY_z) ||
          (keyval >= Gdk.KEY_A && keyval <= Gdk.KEY_Z) ||
          (keyval >= Gdk.KEY_0 && keyval <= Gdk.KEY_9) ||
          (keyval >= Gdk.KEY_kana_fullstop &&
            keyval <= Gdk.KEY_semivoicedsound) ||
          (keyval >= Gdk.KEY_Arabic_comma && keyval <= Gdk.KEY_Arabic_sukun) ||
          (keyval >= Gdk.KEY_Serbian_dje &&
            keyval <= Gdk.KEY_Cyrillic_HARDSIGN) ||
          (keyval >= Gdk.KEY_Greek_ALPHAaccent &&
            keyval <= Gdk.KEY_Greek_omega) ||
          (keyval >= Gdk.KEY_hebrew_doublelowline &&
            keyval <= Gdk.KEY_hebrew_taf) ||
          (keyval >= Gdk.KEY_Thai_kokai && keyval <= Gdk.KEY_Thai_lekkao) ||
          (keyval >= Gdk.KEY_Hangul_Kiyeog &&
            keyval <= Gdk.KEY_Hangul_J_YeorinHieuh) ||
          (keyval === Gdk.KEY_space && mask === 0) ||
          this.keyvalIsForbidden(keyval)))
    );
  }

  isValidAccel(mask, keyval) {
    return (
      Gtk.accelerator_valid(keyval, mask) ||
      (keyval === Gdk.KEY_Tab && mask !== 0)
    );
  }
};
