import { Options } from './types'
import { cloneNode } from './clone-node'
import { embedImages } from './embed-images'
import { applyStyle } from './apply-style'
import { embedWebFonts, getWebFontCSS } from './embed-webfonts'
import {
  canvasToBlob,
  checkCanvasDimensions,
  createImage,
  getImageSize,
  getPixelRatio,
  nodeToDataURL,
  nodeToSVG,
  svgToDataURL
} from './util'

export async function toSvg<T extends HTMLElement>(
  node: T,
  options: Options = {}
): Promise<SVGElement> {
  const { width, height } = getImageSize(node, options)
  const clonedNode = (await cloneNode(node, options, true)) as HTMLElement
  await embedWebFonts(clonedNode, options)
  await embedImages(clonedNode, options)
  applyStyle(clonedNode, options)
  return nodeToSVG(clonedNode, width, height)
}

export async function toSvgDataURL<T extends HTMLElement>(
  node: T,
  options: Options = {}
): Promise<string> {
  const { width, height } = getImageSize(node, options)
  const clonedNode = (await cloneNode(node, options, true)) as HTMLElement
  await embedWebFonts(clonedNode, options)
  await embedImages(clonedNode, options)
  applyStyle(clonedNode, options)
  const datauri = await nodeToDataURL(clonedNode, width, height)
  return datauri
}

export async function toCanvasAndSVG<T extends HTMLElement>(
  node: T,
  options: Options = {}
): Promise<{
  canvas: HTMLCanvasElement
  svg: SVGElement
}> {
  const { width, height } = getImageSize(node, options)
  const svg = await toSvg(node, options)
  const svgDataURL = await svgToDataURL(svg)
  const img = await createImage(svgDataURL)

  const canvas = document.createElement('canvas')
  const context = canvas.getContext('2d')!
  context.imageSmoothingEnabled = true
  const ratio = options.pixelRatio || getPixelRatio()
  const canvasWidth = options.canvasWidth || width
  const canvasHeight = options.canvasHeight || height

  canvas.width = canvasWidth * ratio
  canvas.height = canvasHeight * ratio

  if (!options.skipAutoScale) {
    checkCanvasDimensions(canvas)
  }
  canvas.style.width = `${canvasWidth}`
  canvas.style.height = `${canvasHeight}`

  if (options.backgroundColor) {
    context.fillStyle = options.backgroundColor
    context.fillRect(0, 0, canvas.width, canvas.height)
  }

  context.drawImage(img, 0, 0, canvas.width, canvas.height)

  return { canvas, svg }
}

export async function toCanvas<T extends HTMLElement>(
  node: T,
  options: Options = {}
): Promise<HTMLCanvasElement> {
  const { canvas } = await toCanvasAndSVG(node, options)
  return canvas
}

export async function toPixelData<T extends HTMLElement>(
  node: T,
  options: Options = {}
): Promise<Uint8ClampedArray> {
  const { width, height } = getImageSize(node, options)
  const canvas = await toCanvas(node, options)
  const ctx = canvas.getContext('2d')!
  return ctx.getImageData(0, 0, width, height).data
}

export async function toPng<T extends HTMLElement>(
  node: T,
  options: Options = {}
): Promise<string> {
  const canvas = await toCanvas(node, options)
  return canvas.toDataURL()
}

export async function toJpeg<T extends HTMLElement>(
  node: T,
  options: Options = {}
): Promise<string> {
  const canvas = await toCanvas(node, options)
  return canvas.toDataURL('image/jpeg', options.quality || 1)
}

export async function toBlob<T extends HTMLElement>(
  node: T,
  options: Options = {}
): Promise<Blob | null> {
  const canvas = await toCanvas(node, options)
  const blob = await canvasToBlob(canvas)
  return blob
}

export async function getFontEmbedCSS<T extends HTMLElement>(
  node: T,
  options: Options = {}
): Promise<string> {
  return getWebFontCSS(node, options)
}
