const GObject     = imports.gi.GObject
const St          = imports.gi.St
const Clutter     = imports.gi.Clutter
const GtkSettings = imports.gi.Gtk.Settings.get_default()
const Main        = imports.ui.main
const Unite       = imports.misc.extensionUtils.getCurrentExtension()
const AppMenu     = Main.panel.statusArea.appMenu
const AggMenu     = Main.panel.statusArea.aggregateMenu
const Handlers    = Unite.imports.handlers
const VERSION     = Unite.imports.constants.VERSION

function actorHasClass(actor, name) {
  return actor.has_style_class_name && actor.has_style_class_name(name)
}

function getWidgetArrow(widget) {
  let arrow = widget._arrow

  if (!arrow) {
    const last  = widget.get_n_children() - 1
    const actor = widget.get_children()[last]

    if (actor) {
      if (actorHasClass(actor, 'popup-menu-arrow')) {
        arrow = actor
      } else {
        arrow = getWidgetArrow(actor)
      }
    }
  }

  if (arrow && !widget.hasOwnProperty('_arrow')) {
    widget._arrow = arrow
  }

  return arrow
}

function toggleWidgetArrow(widget, hide) {
  const arrow = widget && getWidgetArrow(widget)

  if (arrow) {
    if (hide && !widget._arrowHandled) {
      arrow.visible = false
      widget._arrowHandled = true
    }

    if (!hide && widget._arrowHandled) {
      arrow.visible = true
      delete widget._arrowHandled
    }
  }
}

var LayoutManager = GObject.registerClass(
  class UniteLayoutManager extends GObject.Object {
    _init() {
      this.signals  = new Handlers.Signals()
      this.settings = new Handlers.Settings()
      this.styles   = new Handlers.Styles()

      this.signals.connect(
        Main.panel._leftBox, 'actor_added', this._onHideDropdownArrows.bind(this)
      )

      this.signals.connect(
        Main.panel._centerBox, 'actor_added', this._onHideDropdownArrows.bind(this)
      )

      this.signals.connect(
        Main.panel._rightBox, 'actor_added', this._onHideDropdownArrows.bind(this)
      )

      this.settings.connect(
        'notifications-position', this._onNotificationsChange.bind(this)
      )

      this.settings.connect(
        'hide-app-menu-icon', this._onHideAppMenuIcon.bind(this)
      )

      if (VERSION < 40) {
        this.settings.connect(
          'hide-app-menu-arrow', this._onHideAppMenuArrow.bind(this)
        )

        this.settings.connect(
          'hide-aggregate-menu-arrow', this._onHideAggMenuArrow.bind(this)
        )

        this.settings.connect(
          'hide-dropdown-arrows', this._onHideDropdownArrows.bind(this)
        )
      }

      this.settings.connect(
        'reduce-panel-spacing', this._onChangeStyles.bind(this)
      )

      if (VERSION < 36) {
        this.settings.connect(
          'use-system-fonts', this._onChangeStyles.bind(this)
        )

        this.signals.connect(
          GtkSettings, 'notify::gtk-font-name', this._onChangeStyles.bind(this)
        )
      }
    }

    _onNotificationsChange() {
      const setting = this.settings.get('notifications-position')

      if (setting != 'center') {
        const context  = St.ThemeContext.get_for_stage(global.stage)
        const banner   = Main.messageTray._bannerBin
        const mappings = { left: 'START', right: 'END' }
        const position = mappings[setting]

        banner.set_x_align(Clutter.ActorAlign[position])
        banner.set_width(390 * context.scale_factor)
      } else {
        this._resetNotifications()
      }
    }

    _onHideAppMenuIcon() {
      const setting = this.settings.get('hide-app-menu-icon')

      if (setting) {
        AppMenu._iconBox.hide()
      } else {
        this._resetAppMenuIcon()
      }
    }

    _onHideAppMenuArrow() {
      const setting = this.settings.get('hide-app-menu-arrow')

      if (setting) {
        toggleWidgetArrow(AppMenu, true)
      } else {
        this._resetAppMenuArrow()
      }
    }

    _onHideAggMenuArrow() {
      const setting = this.settings.get('hide-aggregate-menu-arrow')

      if (setting) {
        toggleWidgetArrow(AggMenu, true)
      } else {
        this._resetAggMenuArrow()
      }
    }

    _onHideDropdownArrows() {
      const setting = this.settings.get('hide-dropdown-arrows')

      if (setting) {
        for (const [name, widget] of Object.entries(Main.panel.statusArea)) {
          if (name != 'aggregateMenu' && name != 'appMenu') {
            toggleWidgetArrow(widget, true)
          }
        }
      } else {
        this._resetDropdownArrows()
      }
    }

    _onChangeStyles() {
      const fonts = this.settings.get('use-system-fonts')
      const space = this.settings.get('reduce-panel-spacing')

      this._resetStyles()

      if (VERSION < 36 && fonts) {
        const font = GtkSettings.gtk_font_name.replace(/\s\d+$/, '')

        this.styles.addWidgetStyle('uiGroup', Main.uiGroup, `font-family: ${font};`)
        this.styles.addWidgetStyle('panel', Main.panel, 'font-size: 11.25pt;')
      }

      if (space) {
        Main.panel._addStyleClassName('small-spacing')
      }

      if (VERSION < 34) {
        Main.panel._addStyleClassName('extra-spacing')
      }

      this._syncStyles()
    }

    _resetNotifications() {
      const banner = Main.messageTray._bannerBin

      banner.set_x_align(Clutter.ActorAlign.CENTER)
      banner.set_width(-1)
    }

    _resetAppMenuIcon() {
      AppMenu._iconBox.show()
    }

    _resetAppMenuArrow() {
      toggleWidgetArrow(AppMenu, false)
    }

    _resetAggMenuArrow() {
      toggleWidgetArrow(AggMenu, false)
    }

    _resetDropdownArrows() {
      for (const [name, widget] of Object.entries(Main.panel.statusArea)) {
        if (name != 'aggregateMenu' && name != 'appMenu') {
          toggleWidgetArrow(widget, false)
        }
      }
    }

    _resetStyles() {
      Main.panel._removeStyleClassName('small-spacing')
      Main.panel._removeStyleClassName('extra-spacing')

      this.styles.deleteStyle('uiGroup')
      this.styles.deleteStyle('panel')
    }

    // Fix for panel spacing not applied until mouse-over
    // Issue: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/1708
    _syncStyles() {
      const space = this.settings.get('reduce-panel-spacing')

      if (VERSION >= 34 && space) {
        Object.values(Main.panel.statusArea).forEach((item) => {
          if (item !== null) {
            item.add_style_pseudo_class('hover')
            item.remove_style_pseudo_class('hover')
          }
        })
      }
    }

    activate() {
      this._onNotificationsChange()
      this._onHideAppMenuIcon()

      if (VERSION < 40) {
        this._onHideAppMenuArrow()
        this._onHideAggMenuArrow()
        this._onHideDropdownArrows()
      }

      this._onChangeStyles()
    }

    destroy() {
      this._resetNotifications()
      this._resetAppMenuIcon()

      if (VERSION < 40) {
        this._resetAppMenuArrow()
        this._resetAggMenuArrow()
        this._resetDropdownArrows()
      }

      this._resetStyles()
      this._syncStyles()

      this.signals.disconnectAll()
      this.settings.disconnectAll()
      this.styles.removeAll()
    }
  }
)