import { getContext, onDestroy, setContext } from 'svelte'

/**
 * @public
 */
export interface FocusManager {
  next: (inc?: 1 | -1) => void
  setFocus: (idx: number) => void
  setFocusPos: (order: number) => void
  updateFocus: (idx: number, order: number) => void

  // Check if current manager has focus
  hasFocus: () => boolean
}

class FocusManagerImpl implements FocusManager {
  counter = 0
  elements: Array<{
    id: number
    order: number
    focus: () => boolean
    isFocus: () => boolean
  }> = []

  current = 0
  register (order: number, focus: () => boolean, isFocus: () => boolean): number {
    const el = { id: this.counter++, order, focus, isFocus }
    this.elements.push(el)
    this.sort()
    return el.id
  }

  unregister (idx: number): void {
    this.elements = this.elements.filter((it) => it.id !== idx)
    this.sort()
  }

  sort (): void {
    // this.needSort = 0
    this.elements.sort((a, b) => {
      return a.order - b.order
    })
  }

  next (inc?: 1 | -1): void {
    while (true) {
      this.current = this.current + (inc ?? 1)
      if (this.elements[Math.abs(this.current) % this.elements.length].focus()) {
        break
      }
    }
  }

  setFocus (idx: number): void {
    this.current = this.elements.findIndex((it) => it.id === idx) ?? 0
    this.elements[Math.abs(this.current) % this.elements.length].focus()
  }

  setFocusPos (order: number): void {
    const idx = this.elements.findIndex((it) => it.order === order)
    if (idx !== undefined) {
      this.current = idx
      this.elements[Math.abs(this.current) % this.elements.length].focus()
    }
  }

  updateFocus (idx: number, order: number): void {
    const el = this.elements.find((it) => it.id === idx)
    if (el !== undefined) {
      if (el.order !== order) {
        el.order = order
        this.sort()
      }
    }
  }

  hasFocus (): boolean {
    for (const el of this.elements) {
      if (el.isFocus()) {
        return true
      }
    }
    return false
  }
}

/**
 * @public
 */
export function createFocusManager (): FocusManager {
  const mgr = new FocusManagerImpl()
  setFocusManager(mgr)
  return mgr
}

export function setFocusManager (manager: FocusManager): void {
  setContext('ui.focus.elements', manager)
}

/**
 * @public
 */
export function getFocusManager (): FocusManager | undefined {
  return getContext('ui.focus.elements')
}

/**
 * Register new focus reciever if order !== -1
 * @public
 */
export function registerFocus (
  order: number,
  item: { focus: () => boolean, isFocus: () => boolean }
): { idx: number, focusManager?: FocusManager } {
  const focusManager = getFocusManager() as FocusManagerImpl
  if (order === -1) {
    return { idx: -1, focusManager }
  }
  const idx = focusManager?.register(order, item.focus, item.isFocus) ?? -1
  if (idx !== -1) {
    onDestroy(() => {
      focusManager.unregister(idx)
    })
  }
  return { idx, focusManager }
}
