import { OrgRoamNode } from '../../api'
import { NodeObject } from 'force-graph'
import { initialVisuals } from '../config'
import { hexToRGBA, LinksByNodeId } from '../../pages'
import wrap from 'word-wrap'

export interface drawLabelsProps {
  labelBackgroundColor: string
  labelTextColor: string
  node: NodeObject
  ctx: any
  globalScale: number
  highlightedNodes: { [id: string]: {} }
  previouslyHighlightedNodes: { [id: string]: {} }
  visuals: typeof initialVisuals
  opacity: number
  nodeSize: (node: NodeObject) => number
  filteredLinksByNodeId: LinksByNodeId
  nodeRel: number
  hoverNode: NodeObject | null
  lastHoverNode: OrgRoamNode | null
}

export const getLabelOpacity = (
  fadeFactor: number,
  visuals: typeof initialVisuals,
  globalScale: number,
  opacity: number,
  isHighlighty: boolean,
) => {
  return isHighlighty
    ? Math.max(fadeFactor, opacity)
    : 1 * fadeFactor * (-1 * (visuals.highlightFade * opacity - 1))
}

export function drawLabels(props: drawLabelsProps) {
  const {
    labelBackgroundColor,
    labelTextColor,
    node,
    ctx,
    globalScale,
    highlightedNodes,
    previouslyHighlightedNodes,
    visuals,
    opacity,
    nodeSize,
    filteredLinksByNodeId,
    nodeRel,
    hoverNode,
    lastHoverNode,
  } = props

  if (!node) {
    return
  }

  if (!visuals.labels) {
    return
  }
  const hoverId = hoverNode?.id ?? ''
  const lastHoverId = lastHoverNode?.id ?? ''
  const links = filteredLinksByNodeId[(node as OrgRoamNode).id] ?? []

  const isHighlighty = !!(highlightedNodes[node.id!] || previouslyHighlightedNodes[node.id!])

  const fadeFactor = Math.min(
    5 * (globalScale - visuals.labelScale) +
      2 *
        Math.pow(Math.min(links.length, visuals.labelDynamicDegree), visuals.labelDynamicStrength),
    1,
  )
  if (fadeFactor < 0.01 && !isHighlighty) {
    return
  }
  const nodeTitle = (node as OrgRoamNode).title ?? ''

  const label = nodeTitle.substring(0, visuals.labelLength)

  const nodeS = Math.cbrt(
    (visuals.nodeRel * nodeSize(node)) / Math.pow(globalScale, visuals.nodeZoomSize),
  )
  const fontSize = visuals.labelFontSize / Math.cbrt(Math.pow(globalScale, visuals.nodeZoomSize))
  //   ? Math.max((visuals.labelFontSize * nodeS) / 2, (visuals.labelFontSize * nodeS) / 3)
  //    : (visuals.labelFontSize * nodeS) / 3

  //  * nodeS) / 3
  const textWidth = ctx.measureText(label).width
  const bckgDimensions = [textWidth * 1.1, fontSize].map((n) => n + fontSize * 0.5) as [
    number,
    number,
  ] // some padding

  // draw label background
  const textOpacity = getLabelOpacity(fadeFactor, visuals, globalScale, opacity, isHighlighty)
  if (visuals.labelBackgroundColor && visuals.labelBackgroundOpacity) {
    const backgroundOpacity = textOpacity * visuals.labelBackgroundOpacity
    const labelBackground = hexToRGBA(labelBackgroundColor, backgroundOpacity)
    ctx.fillStyle = labelBackground
    ctx.fillRect(
      node.x! - bckgDimensions[0] / 2,
      node.y! - bckgDimensions[1] / 2 + nodeS,
      ...bckgDimensions,
    )
  }

  // draw label text
  ctx.textAlign = 'center'
  ctx.textBaseline = 'middle'
  const labelText = hexToRGBA(labelTextColor, textOpacity)
  ctx.fillStyle = labelText
  ctx.font = `${fontSize}px Sans-Serif`
  const wordsArray = wrap(label, { width: visuals.labelWordWrap }).split('\n')

  const truncatedWords =
    nodeTitle.length > visuals.labelLength
      ? [...wordsArray.slice(0, -1), `${wordsArray.slice(-1)}...`]
      : wordsArray

  const highlightedNodeOffset = [hoverId, lastHoverId].includes((node as OrgRoamNode).id)
    ? 1 + 0.3 * opacity
    : 1
  truncatedWords.forEach((word, index) => {
    ctx.fillText(
      word,
      node.x!,
      node.y! + highlightedNodeOffset * nodeS * 8 + visuals.labelLineSpace * fontSize * index,
    )
  })
}