import bumpy, chroma, flatty/binny, os, pixie/common, pixie/contexts,
    pixie/fileformats/bmp, pixie/fileformats/gif, pixie/fileformats/jpg,
    pixie/fileformats/png, pixie/fileformats/ppm, pixie/fileformats/qoi,
    pixie/fileformats/svg, pixie/fonts, pixie/images, pixie/masks, pixie/paints,
    pixie/paths, strutils, vmath

export bumpy, chroma, common, contexts, fonts, images, masks, paints, paths, vmath

type
  FileFormat* = enum
    ffPng, ffBmp, ffJpg, ffGif, ffQoi, ffPpm

converter autoStraightAlpha*(c: ColorRGBX): ColorRGBA {.inline, raises: [].} =
  ## Convert a premultiplied alpha RGBA to a straight alpha RGBA.
  c.rgba()

converter autoPremultipliedAlpha*(c: ColorRGBA): ColorRGBX {.inline, raises: [].} =
  ## Convert a straight alpha RGBA to a premultiplied alpha RGBA.
  c.rgbx()

proc decodeImage*(data: string | seq[uint8]): Image {.raises: [PixieError].} =
  ## Loads an image from memory.
  if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature):
    decodePng(data)
  elif data.len > 2 and data.readUint16(0) == cast[uint16](jpgStartOfImage):
    decodeJpg(data)
  elif data.len > 2 and data.readStr(0, 2) == bmpSignature:
    decodeBmp(data)
  elif data.len > 5 and
    (data.readStr(0, 5) == xmlSignature or data.readStr(0, 4) == svgSignature):
    decodeSvg(data)
  elif data.len > 6 and data.readStr(0, 6) in gifSignatures:
    decodeGif(data)
  elif data.len > (14+8) and data.readStr(0, 4) == qoiSignature:
    decodeQoi(data)
  elif data.len > 9 and data.readStr(0, 2) in ppmSignatures:
    decodePpm(data)
  else:
    raise newException(PixieError, "Unsupported image file format")

proc decodeMask*(data: string | seq[uint8]): Mask {.raises: [PixieError].} =
  ## Loads a mask from memory.
  if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature):
    newMask(decodePng(data))
  else:
    raise newException(PixieError, "Unsupported mask file format")

proc readImage*(filePath: string): Image {.inline, raises: [PixieError].} =
  ## Loads an image from a file.
  try:
    decodeImage(readFile(filePath))
  except IOError as e:
    raise newException(PixieError, e.msg, e)

proc readMask*(filePath: string): Mask {.raises: [PixieError].} =
  ## Loads a mask from a file.
  try:
    decodeMask(readFile(filePath))
  except IOError as e:
    raise newException(PixieError, e.msg, e)

proc encodeImage*(image: Image, fileFormat: FileFormat): string {.raises: [PixieError].} =
  ## Encodes an image into memory.
  case fileFormat:
  of ffPng:
    image.encodePng()
  of ffJpg:
    image.encodeJpg()
  of ffBmp:
    image.encodeBmp()
  of ffQoi:
    image.encodeQoi()
  of ffGif:
    raise newException(PixieError, "Unsupported file format")
  of ffPpm:
    image.encodePpm()

proc encodeMask*(mask: Mask, fileFormat: FileFormat): string {.raises: [PixieError].} =
  ## Encodes a mask into memory.
  case fileFormat:
  of ffPng:
    mask.encodePng()
  else:
    raise newException(PixieError, "Unsupported file format")

proc writeFile*(image: Image, filePath: string) {.raises: [PixieError].} =
  ## Writes an image to a file.
  let fileFormat = case splitFile(filePath).ext.toLowerAscii():
    of ".png": ffPng
    of ".bmp": ffBmp
    of ".jpg", ".jpeg": ffJpg
    of ".qoi": ffQoi
    of ".ppm": ffPpm
    else:
      raise newException(PixieError, "Unsupported file extension")

  try:
    writeFile(filePath, image.encodeImage(fileFormat))
  except IOError as e:
    raise newException(PixieError, e.msg, e)

proc writeFile*(mask: Mask, filePath: string) {.raises: [PixieError].} =
  ## Writes a mask to a file.
  let fileFormat = case splitFile(filePath).ext.toLowerAscii():
    of ".png": ffPng
    of ".bmp": ffBmp
    of ".jpg", ".jpeg": ffJpg
    of ".qoi": ffQoi
    of ".ppm": ffPpm
    else:
      raise newException(PixieError, "Unsupported file extension")

  try:
    writeFile(filePath, mask.encodeMask(fileFormat))
  except IOError as e:
    raise newException(PixieError, e.msg, e)