export type PathCommands = Array<string | number>

export interface Quad {
  p1: Position
  p2: Position
  p3: Position
  p4: Position
}

export interface Position {
  x: number
  y: number
}

export interface Bounds {
  minX: number
  maxX: number
  minY: number
  maxY: number
  width?: number
  height?: number
  allPoints: Position[]
}

export interface AreaBounds {
  name: string
  bounds: { allPoints: Position[] }
}

interface ViewportSize {
  width: number
  height: number
}

export interface ResetData {
  viewportSize: ViewportSize
  visualViewportSize?: ViewportSize
  deviceScaleFactor: number
  pageScaleFactor: number
  pageZoomFactor: number
  emulationScaleFactor: number
  scrollX: number
  scrollY: number
}

// Overlay class should be used to implement various tools and provide
// access to globals via the window object it receives in the constructor.
// Old logic is kept temporarily while the tools are being migrated.
export class Overlay {
  protected viewportSize = { width: 800, height: 600 }
  protected visualViewportSize?: ViewportSize
  protected deviceScaleFactor = 1
  protected emulationScaleFactor = 1
  protected pageScaleFactor = 1
  protected pageZoomFactor = 1
  protected scrollX = 0
  protected scrollY = 0
  protected style: CSSStyleSheet[]
  protected canvas?: HTMLCanvasElement
  protected canvasWidth = 0
  protected canvasHeight = 0
  protected platform?: string
  #window?: Window
  #document?: Document
  #context?: CanvasRenderingContext2D | null
  #installed = false

  constructor(window: Window, style: CSSStyleSheet[] = []) {
    this.#window = window
    this.#document = window.document
    if (!Array.isArray(style)) {
      style = [style]
    }
    this.style = style
  }

  setCanvas(canvas: HTMLCanvasElement) {
    this.canvas = canvas
    this.#context = canvas.getContext("2d")
  }

  install() {
    for (const style of this.style) {
      adoptStyleSheet(style)
    }
    this.#installed = true
  }

  uninstall() {
    for (const style of this.style) {
      document.adoptedStyleSheets = document.adoptedStyleSheets.filter(s => s !== style)
    }
    this.#installed = false
  }

  reset(resetData?: ResetData) {
    if (resetData) {
      this.viewportSize = resetData.viewportSize
      this.visualViewportSize = resetData.visualViewportSize
      this.deviceScaleFactor = resetData.deviceScaleFactor
      this.pageScaleFactor = resetData.pageScaleFactor
      this.pageZoomFactor = resetData.pageZoomFactor
      this.emulationScaleFactor = resetData.emulationScaleFactor
      this.scrollX = Math.round(resetData.scrollX)
      this.scrollY = Math.round(resetData.scrollY)
    }
    this.resetCanvas()
  }

  resetCanvas() {
    if (!this.canvas || !this.#context) {
      return
    }

    this.canvas.width = this.deviceScaleFactor * this.viewportSize.width
    this.canvas.height = this.deviceScaleFactor * this.viewportSize.height
    this.canvas.style.width = this.viewportSize.width + "px"
    this.canvas.style.height = this.viewportSize.height + "px"

    this.#context.scale(this.deviceScaleFactor, this.deviceScaleFactor)

    this.canvasWidth = this.viewportSize.width
    this.canvasHeight = this.viewportSize.height
  }

  setPlatform(platform: string) {
    this.platform = platform
    if (!this.#installed) {
      this.install()
    }
  }

  dispatch(message: unknown[]) {
    const functionName = message.shift() as string
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    ;(this as any)[functionName].apply(this, message)
  }

  eventHasCtrlOrMeta(event: KeyboardEvent) {
    return this.platform === "mac"
      ? event.metaKey && !event.ctrlKey
      : event.ctrlKey && !event.metaKey
  }

  get context(): CanvasRenderingContext2D {
    if (!this.#context) {
      throw new Error("Context object is missing")
    }
    return this.#context
  }

  get document(): Document {
    if (!this.#document) {
      throw new Error("Document object is missing")
    }
    return this.#document
  }

  get window(): Window {
    if (!this.#window) {
      throw new Error("Window object is missing")
    }
    return this.#window
  }

  get installed(): boolean {
    return this.#installed
  }
}

export function log(text: string) {
  let element = document.getElementById("log")
  if (!element) {
    element = createChild(document.body, "div")
    element.id = "log"
  }
  createChild(element, "div").textContent = text
}

export function createChild(
  parent: HTMLElement,
  tagName: string,
  className?: string,
): HTMLElement {
  const element = createElement(tagName, className)
  element.addEventListener(
    "click",
    (e: Event) => {
      e.stopPropagation()
    },
    false,
  )
  parent.appendChild(element)
  return element
}

export function createTextChild(parent: HTMLElement, text: string): Text {
  const element = document.createTextNode(text)
  parent.appendChild(element)
  return element
}

export function createElement(tagName: string, className?: string) {
  const element = document.createElement(tagName)
  if (className) {
    element.className = className
  }
  return element
}

export function ellipsify(str: string, maxLength: number) {
  if (str.length <= maxLength) {
    return String(str)
  }
  return str.substr(0, maxLength - 1) + "\u2026"
}

export function constrainNumber(num: number, min: number, max: number): number {
  if (num < min) {
    num = min
  } else if (num > max) {
    num = max
  }
  return num
}

declare global {
  interface Document {
    adoptedStyleSheets: CSSStyleSheet[]
  }
}

export function adoptStyleSheet(styleSheet: CSSStyleSheet) {
  document.adoptedStyleSheets = [...document.adoptedStyleSheets, styleSheet]
}
