// Gnome imports
import Adw from "gi://Adw";
import GObject from "gi://GObject";
import Gdk from "gi://Gdk";

// Extension imports
import { gettext as _ } from "resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js";

// Shared state
import { ConfigManager } from "../shared/settings.js";
import { PrefsThemeManager } from "./prefs-theme-manager.js";

// Prefs UI
import { ColorRow, PreferencesPage, ResetButton, SpinButtonRow, SwitchRow } from "./widgets.js";
import { Logger } from "../shared/logger.js";

export class AppearancePage extends PreferencesPage {
  static {
    GObject.registerClass(this);
  }

  /**
   * @param {string} selector
   */
  static getCssSelectorAsMessage(selector) {
    switch (selector) {
      // TODO: make separate color selection for preview hint
      case ".window-tiled-border":
        return _("Tiled window");
      case ".window-tabbed-border":
        return _("Tabbed window");
      case ".window-stacked-border":
        return _("Stacked window");
      case ".window-floated-border":
        return _("Floating window");
      case ".window-split-border":
        return _("Split direction hint");
    }
  }

  constructor({ settings, dir }) {
    super({ title: _("Appearance"), icon_name: "brush-symbolic" });
    this.settings = settings;
    let configMgr = new ConfigManager({ dir });
    this.themeMgr = new PrefsThemeManager({ configMgr: configMgr, settings: settings });
    this.add_group({
      title: _("Gaps"),
      description: _("Change the gap size between windows"),
      children: [
        new SpinButtonRow({
          title: _("Gap size"),
          range: [0, 32, 1],
          settings,
          bind: "window-gap-size",
        }),
        new SpinButtonRow({
          title: _("Gap size multiplier"),
          range: [0, 32, 1],
          settings,
          bind: "window-gap-size-increment",
        }),
        new SwitchRow({
          title: _("Disable gaps for single window"),
          subtitle: _("Disables window gaps when only a single window is present"),
          settings,
          bind: "window-gap-hidden-on-single",
        }),
      ],
    });
    const borderRadiusSelectors = [
      ".window-tiled-border",
      ".window-split-border",
      ".window-stacked-border",
      ".window-tabbed-border",
      ".window-floated-border",
      ".window-tilepreview-tiled",
      ".window-tilepreview-stacked",
      ".window-tilepreview-swap",
      ".window-tilepreview-tabbed",
    ];

    const currentRadius = parseInt(
      this.themeMgr.removePx(
        this.themeMgr.getCssProperty(".window-tiled-border", "border-radius").value ?? "14px"
      )
    );

    const borderRadiusRow = new SpinButtonRow({
      title: _("Border radius"),
      subtitle: _("Set to 0 for square corners"),
      range: [0, 28, 1],
      init: currentRadius,
      onChange: (value) => {
        const px = this.themeMgr.addPx(value);
        for (const selector of borderRadiusSelectors) {
          this.themeMgr.setCssProperty(selector, "border-radius", px);
        }
      },
    });

    borderRadiusRow.add_suffix(
      new ResetButton({
        onReset: () => {
          const defaultRadius = 14;
          const px = this.themeMgr.addPx(defaultRadius);
          for (const selector of borderRadiusSelectors) {
            this.themeMgr.setCssProperty(selector, "border-radius", px);
          }
          borderRadiusRow.activatable_widget.value = defaultRadius;
        },
      })
    );

    this.add_group({
      title: _("Style"),
      description: _("Change how the shell looks"),
      children: [
        new SwitchRow({
          title: _("Preview hint"),
          subtitle: _("Shows where the window will be tiled when you let go of it"),
          experimental: true,
          settings,
          bind: "preview-hint-enabled",
        }),
        new SwitchRow({
          title: _("Border around focused window"),
          subtitle: _("Display a colored border around the focused window"),
          settings,
          bind: "focus-border-toggle",
        }),
        new SwitchRow({
          title: _("Window split hint border"),
          subtitle: _("Show split direction border on focused window"),
          settings,
          bind: "split-border-toggle",
        }),
        new SwitchRow({
          title: _("Forge in quick settings"),
          subtitle: _("Toggles the Forge tile in quick settings"),
          experimental: true,
          settings,
          bind: "quick-settings-enabled",
        }),
        borderRadiusRow,
      ],
    });
    this.add_group({
      title: _("Color"),
      description: _("Changes the focused window's border and preview hint colors"),
      children: [
        "window-tiled-border",
        "window-tabbed-border",
        "window-stacked-border",
        "window-floated-border",
        "window-split-border",
      ].map((x) => this._createColorOptionWidget(x)),
    });
  }

  /**
   * @param {string} prefix
   */
  _createColorOptionWidget(prefix) {
    const selector = `.${prefix}`;
    const theme = this.themeMgr;
    const title = AppearancePage.getCssSelectorAsMessage(selector);
    const colorScheme = theme.getColorSchemeBySelector(selector);
    const row = new Adw.ExpanderRow({ title });

    const borderSizeRow = new SpinButtonRow({
      title: _("Border size"),
      range: [1, 6, 1],
      // subtitle: 'Properties of the focus hint',
      max_width_chars: 1,
      max_length: 1,
      width_chars: 2,
      xalign: 1,
      init: theme.removePx(theme.getCssProperty(selector, "border-width").value),
      onChange: (value) => {
        const px = theme.addPx(value);
        Logger.debug(`Setting border width for selector: ${selector} ${px}`);
        theme.setCssProperty(selector, "border-width", px);
      },
    });

    borderSizeRow.add_suffix(
      new ResetButton({
        onReset: () => {
          const borderDefault = theme.defaultPalette[colorScheme]["border-width"];
          theme.setCssProperty(selector, "border-width", theme.addPx(borderDefault));
          borderSizeRow.activatable_widget.value = borderDefault;
        },
      })
    );

    const updateCssColors = (rgbaString) => {
      const rgba = new Gdk.RGBA();

      if (rgba.parse(rgbaString)) {
        Logger.debug(`Setting color for selector: ${selector} ${rgbaString}`);
        const previewBorderRgba = rgba.copy();
        const previewBackgroundRgba = rgba.copy();
        const overviewBackgroundRgba = rgba.copy();

        previewBorderRgba.alpha = 0.3;
        previewBackgroundRgba.alpha = 0.2;
        overviewBackgroundRgba.alpha = 0.5;

        // The primary color updates the focus hint:
        theme.setCssProperty(selector, "border-color", rgba.to_string());

        // Only apply below on the tabbed scheme
        if (colorScheme === "tabbed") {
          const tabBorderRgba = rgba.copy();
          const tabActiveBackgroundRgba = rgba.copy();
          tabBorderRgba.alpha = 0.6;
          theme.setCssProperty(
            `.window-${colorScheme}-tab`,
            "border-color",
            tabBorderRgba.to_string()
          );
          theme.setCssProperty(
            `.window-${colorScheme}-tab-active`,
            "background-color",
            tabActiveBackgroundRgba.to_string()
          );
        }
        // And then finally the preview when doing drag/drop tiling:
        theme.setCssProperty(
          `.window-tilepreview-${colorScheme}`,
          "border-color",
          previewBorderRgba.to_string()
        );
        theme.setCssProperty(
          `.window-tilepreview-${colorScheme}`,
          "background-color",
          previewBackgroundRgba.to_string()
        );
      }
    };

    const borderColorRow = new ColorRow({
      title: _("Border color"),
      init: theme.getCssProperty(selector, "border-color").value,
      onChange: updateCssColors,
    });

    borderColorRow.add_suffix(
      new ResetButton({
        onReset: () => {
          const selectorColor = theme.defaultPalette[colorScheme].color;
          updateCssColors(selectorColor);
          const rgba = new Gdk.RGBA();
          if (rgba.parse(selectorColor)) {
            borderColorRow.colorButton.set_rgba(rgba);
          }
        },
      })
    );

    row.add_row(borderColorRow);
    row.add_row(borderSizeRow);

    return row;
  }
}
