'use strict'

const path = require('path')
const Emitter = require('event-kit').Emitter
const { KeyboardLayoutManager } = require('node-gyp-build')(path.join(__dirname, '..'))

const FALLBACK_KEYBOARD_LAYOUT = 'us'

const emitter = new Emitter()
const keymapsByLayoutName = new Map()

let nativeManager = null
let nativeManagerUnavailable = false

/**
 * Builds the native manager on first use, returning null when the platform
 * cannot supply a keyboard layout.
 *
 * Construction is deferred rather than done at module scope because the Linux
 * backend opens an X display in its constructor. Doing that on `require` takes
 * down any process that has no reachable display — a Wayland session without
 * XWayland, a headless container — even when the caller never asks for a
 * keymap.
 *
 * @returns {object|null} The native manager, or null when unavailable.
 */
function getNativeManager () {
  if (nativeManager || nativeManagerUnavailable) return nativeManager

  try {
    nativeManager = new KeyboardLayoutManager(() => {
      emitter.emit('did-change-current-keyboard-layout', getCurrentKeyboardLayout())
    })
  } catch (error) {
    nativeManagerUnavailable = true
    process.emitWarning(
      `Keyboard layout detection is unavailable (${error.message}). ` +
      'Falling back to an empty keymap.',
      'KeyboardLayoutWarning'
    )
  }

  return nativeManager
}

function getCurrentKeymap () {
  const manager = getNativeManager()
  if (!manager) return {}

  const currentLayout = getCurrentKeyboardLayout()
  let currentKeymap = keymapsByLayoutName.get(currentLayout)
  if (!currentKeymap) {
    currentKeymap = manager.getCurrentKeymap()
    keymapsByLayoutName.set(currentLayout, currentKeymap)
  }
  return currentKeymap
}

function getCurrentKeyboardLayout () {
  const manager = getNativeManager()
  return manager ? manager.getCurrentKeyboardLayout() : FALLBACK_KEYBOARD_LAYOUT
}

function getCurrentKeyboardLanguage () {
  const manager = getNativeManager()
  return manager ? manager.getCurrentKeyboardLanguage() : FALLBACK_KEYBOARD_LAYOUT
}

function getInstalledKeyboardLanguages () {
  const manager = getNativeManager()
  if (!manager) return []

  var languages = {}

  for (var language of (manager.getInstalledKeyboardLanguages() || [])) {
    languages[language] = true
  }

  return Object.keys(languages)
}

function onDidChangeCurrentKeyboardLayout (callback) {
  return emitter.on('did-change-current-keyboard-layout', callback)
}

function observeCurrentKeyboardLayout (callback) {
  callback(getCurrentKeyboardLayout())
  return onDidChangeCurrentKeyboardLayout(callback)
}

module.exports = {
  getCurrentKeymap: getCurrentKeymap,
  getCurrentKeyboardLayout: getCurrentKeyboardLayout,
  getCurrentKeyboardLanguage: getCurrentKeyboardLanguage,
  getInstalledKeyboardLanguages: getInstalledKeyboardLanguages,
  onDidChangeCurrentKeyboardLayout: onDidChangeCurrentKeyboardLayout,
  observeCurrentKeyboardLayout: observeCurrentKeyboardLayout
}
