/**
  * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
  * 
  * @param {String} text The text to be rendered.
  * @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
  * 
  * @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
  */

import { useTimeline } from ".."

function getTextWidthInCanvas(text: string, font: string) {
  if (typeof window === "undefined") {
    return 0
  }
  const canvas = document.createElement("canvas")
  const context = canvas.getContext("2d")
  if (!context) { return 0 }

  context.font = font
  const metrics = context.measureText(text)
  return metrics.width
}


// one option could be to pre-compute some of the width
const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789()_-"+=,;:?/\\&@#'.split('')
const charLength = characters.reduce((acc, char) => ({
  ...acc,
  [char]: getTextWidthInCanvas(char, "bold Arial")
}), {} as Record<string, number>)
let defaultCharLength = 5.561523437

// change this whenever you modify the font size
const webglFontWidthFactor = 1.7

/**
 * Compute the text of a simple Arial text in a WebGL environmment
 * This actually just do a lookup + sum
 * 
 * @param text 
 * @returns 
 */
export function getWebGLCharWidth(char: string = ""): number {

  const cellWidthInPixels = useTimeline.getState().cellWidth

  let responsiveHack = 1.5

  if (cellWidthInPixels < 16) {
    responsiveHack = 1.20
  } else if (cellWidthInPixels < 20) {
    responsiveHack = 1.10
  } else if (cellWidthInPixels < 24) {
    responsiveHack = 1.05
  } else if (cellWidthInPixels < 28) {
    responsiveHack = 1
  } else if (cellWidthInPixels < 32) {
    responsiveHack = 1.0
  } else if (cellWidthInPixels < 48) {
    responsiveHack = 0.9
  } else if (cellWidthInPixels < 64) {
    responsiveHack = 0.9
  } else if (cellWidthInPixels < 128) {
    responsiveHack = 0.8
  } else {
    responsiveHack = 0.7
  }
  return responsiveHack * webglFontWidthFactor * (charLength[char] || defaultCharLength)
}

/**
 * Compute the text of a simple Arial text in a WebGL environmment
 * This actually just do a lookup + sum
 * 
 * @param text 
 * @returns 
 */
export function getWebGLTextWidth(text: string = ""): number {
  return text.split('').reduce((s, c) => (s + getWebGLCharWidth(c)), 0)
}

/**
 * Clamp a text to a given
 * @param text 
 * @param maxWidthInPixels 
 * @returns 
 */
export function clampWebGLText(
  input: string,
  maxWidthInPixels: number,
  maxNbLines: number
): string[] {
  let buffer = ""
  let width = 0
  let lines: string[] = []

  const text = `${input || ""}`.replace('\n', ' ').trim()
  const characters = text.split('')
  for (const c of characters) {
    width += getWebGLCharWidth(c)
    buffer += c
    if (width >= maxWidthInPixels) {
      if (lines.length >= (maxNbLines - 1)) {
        buffer = buffer.trim() // to avoid writing "and .."
        buffer += ".."
        break
      } else {
        // TODO: we should do something smarter, which is to split the last sentence
        const words = buffer.split(" ")
        const lastWord = (words.at(-1) || "")
        if (lastWord.length) {
          lines.push(words.slice(0, -1).join(" "))
          buffer = lastWord
          width = getWebGLTextWidth(lastWord)
        } else {
           lines.push(buffer)
           buffer = ""
           width = 0
        }
      }
    }
  }

  if (buffer.length) {
    lines.push(buffer)
  }
  return lines
}

export function clampWebGLTextNaive(input: string = "", maxWidthInPixels: number = 0): string {
  // this cutoff is very approximate as we should make it dependent on each character's width
  // a simple heuristic can be to count the uppercase / lower case
  const maxhInCharacter = Math.ceil(maxWidthInPixels / 3.4)

  const text = `${input || ""}`

  return (text.length >= maxhInCharacter)
              ? `${text.slice(0, maxhInCharacter)}..`
              : text
   
}