import { TouchStartEvent } from "../runtime/touch"
import { ContainerElement, type AnimateStyle, type BaseStyle, type Color, type ContainerStyle, type DrawableStyle, type OnLayoutCallback, type PaddingStyle, type ScrollStyle, type Style, type UINodeChild, type ChildrenFn, } from "./UINode"

export type UIScreenStyle = ContainerStyle & DrawableStyle & PaddingStyle & ScrollStyle & BaseStyle & ScrollStyle & { refreshControlColor?: Color }

// Default to `false`: a bare `UIScreen` means the non-scrollable screen the `UIScreen(...)`
// constructor returns, so `const s: UIScreen = UIScreen([...])` type-checks. The type is invariant
// in `Scrollable` (the fluent `style()` puts `this` in a contravariant position), so a `boolean`
// default wouldn't accept the constructor's `UIScreen<false>`. Opt into scrolling via
// `.makeScrollable(): UIScreen<true>`; write `UIScreen<boolean>` explicitly when you need the union.
export interface UIScreen <Scrollable extends boolean = false> {
  readonly type: "screen",
  scrollable: Scrollable,
  style: Style<this, UIScreenStyle>,
  animateTo: AnimateStyle<this, DrawableStyle & BaseStyle>
  animateFrom: AnimateStyle<this, DrawableStyle & BaseStyle>

  open(): void,
  close(): void

  append(...nodes: UINodeChild[]): this
  insert(index: number, ...nodes: UINodeChild[]): this
  remove(...nodes: UINodeChild[]): this
  setContent(nodes: UINodeChild[] | ChildrenFn): this
  
  readonly children: UINodeChild[],

  makeScrollable(): UIScreen<true>,
  onScroll: Scrollable extends true? (callback: (scrollPosition: number) => void) => void: never,
  onOverscroll: Scrollable extends true? (callback: (scrollPosition: number) => void) => void: never,
  onBackPressed(callback: () => void): this
  onRefresh: Scrollable extends true? ((callback: () => void | Promise<void>) => this): never,

  onOpen(callback: () => void): this
  onClose(callback: () => void): this

  onLayout(onLayout: OnLayoutCallback): this

  setClass(name: string, enabled: boolean): this
  toggleClass(name: string): this
  hasClass(name: string): boolean
  bindClass(name: string, fn: () => boolean): this

  onTouchStart(callback: (ev: TouchStartEvent<UIScreen<boolean>>) => void): this,
}

export class ScreenElement extends ContainerElement<"screen"> {
  scrollable = false as const
  open(): void {
    _creatorUI.openScreen(this) 
  }
  close(): void {
    _creatorUI.closeScreen()
  }
  protected ol: (() => void)[] = []
  onOpen(callback: () => void): this {
    this.ol.push(callback)
    return this
  }
  protected cl: (() => void)[] = []
  onClose(callback: () => void): this {
    this.cl.push(callback)
    return this
  }
  readonly touchStartListeners: any[] = []
  _interactive = false
  onTouchStart (callback: any): this {
    this._interactive = true
    this.touchStartListeners.push(callback)
    return this
  }
  _emitTouchStart(pointerId: number, clientX: number, clientY: number): any {
    const touchEvent = new TouchStartEvent(clientX, clientY, pointerId)
    touchEvent.target = this
    for (let callback of this.touchStartListeners) {
      callback(touchEvent)
      if ((touchEvent as any)._trackHandler) {
        return (touchEvent as any)._trackHandler
      }
    }
    return null
  }
  makeScrollable(): UIScreen<true> {
    this.scrollable = true as any

    Object.assign(this, {
      sl: [] as any[],
      osl: [] as any[],
      onScroll(callback: any) {
        this.sl.push(callback)
        return this
      },
      onOverscroll(callback: any) {
        this.osl.push(callback)
        return this
      },
      onRefresh(callback: any) {
        (this as any)._refreshCallback = callback
        return this 
      }
    })

    return this as any as UIScreen<true>
  }
  _backButtonCallback?: () => void
  onBackPressed(callback: any): this {
    this._backButtonCallback = callback
    return this
  }
  declare _refreshCallback: any
  declare onRefresh: never
  declare onScroll: never
  declare onOverscroll: never
}

export function UIScreen(): UIScreen<false>;
export function UIScreen(children: UINodeChild[] | ChildrenFn): UIScreen<false>;
export function UIScreen(style: UIScreenStyle): UIScreen<false>;
export function UIScreen(style: UIScreenStyle, children: UINodeChild[]): UIScreen<false>;
export function UIScreen(...args: [] | [UINodeChild[] | ChildrenFn | UIScreenStyle] | [UIScreenStyle, children: UINodeChild[] | ChildrenFn]): UIScreen<false> {
  if (args.length === 0) {
    return new ScreenElement("screen", {}, [])
  } else if (args.length === 1) {
    if (Array.isArray(args[0]) || typeof args[0] === "function") {
      return new ScreenElement("screen", {}, args[0])
    } else {
      return new ScreenElement("screen", args[0], [])
    }
  } else {
    return new ScreenElement("screen", args[0], args[1])
  }
}
