import benchy, cairo, pixie, pixie/fileformats/svg {.all.}, pixie/paths {.all.}

type
  Fill = object
    shapes: seq[Polygon]
    transform: Mat3
    paint: Paint
    windingRule: WindingRule

  Benchmark = object
    name: string
    fills: seq[Fill]

var benchmarks: seq[Benchmark]

let
  opaque = newPaint(SolidPaint)
  notOpaque = newPaint(SolidPaint)
opaque.color = color(0, 0, 0, 1)
notOpaque.color = color(0, 0, 0, 0.5)

block: # Basic rect
  let path = newPath()
  path.rect(rect(50, 50, 800, 800))

  let shapes = path.commandsToShapes(true, 1)

  benchmarks.add(Benchmark(
    name: "rect opaque",
    fills: @[Fill(
    shapes: shapes,
    transform: mat3(),
    paint: opaque,
    windingRule: NonZero
  )]))

  benchmarks.add(Benchmark(
    name: "rect not opaque",
    fills: @[Fill(
    shapes: shapes,
    transform: mat3(),
    paint: notOpaque,
    windingRule: NonZero
  )]))

block: # Rounded rect
  let path = newPath()
  path.roundedRect(rect(0, 0, 900, 900), 100, 100, 100, 100)

  let shapes = path.commandsToShapes(true, 1)

  benchmarks.add(Benchmark(
    name: "roundedRect opaque",
    fills: @[Fill(
    shapes: shapes,
    transform: mat3(),
    paint: opaque,
    windingRule: NonZero
    )]))

  benchmarks.add(Benchmark(
    name: "roundedRect not opaque",
    fills: @[Fill(
    shapes: shapes,
    transform: mat3(),
    paint: notOpaque,
    windingRule: NonZero
    )]))

block: # Pentagon
  let path = newPath()
  path.polygon(vec2(450, 450), 400, 5)

  let shapes = path.commandsToShapes(true, 1)

  benchmarks.add(Benchmark(
    name: "pentagon opaque",
    fills: @[Fill(
    shapes: shapes,
    transform: mat3(),
    paint: opaque,
    windingRule: NonZero
  )]))

  benchmarks.add(Benchmark(
    name: "pentagon not opaque",
    fills: @[Fill(
    shapes: shapes,
    transform: mat3(),
    paint: notOpaque,
    windingRule: NonZero
  )]))

block: # Circle
  let path = newPath()
  path.circle(circle(vec2(450, 450), 400))

  let shapes = path.commandsToShapes(true, 1)

  benchmarks.add(Benchmark(
    name: "circle opaque",
    fills: @[Fill(
    shapes: shapes,
    transform: mat3(),
    paint: opaque,
    windingRule: NonZero
  )]))

  benchmarks.add(Benchmark(
    name: "circle not opaque",
    fills: @[Fill(
    shapes: shapes,
    transform: mat3(),
    paint: notOpaque,
    windingRule: NonZero
  )]))

block: # Heart
  let path = parsePath("""
      M 100,300
      A 200,200 0,0,1 500,300
      A 200,200 0,0,1 900,300
      Q 900,600 500,900
      Q 100,600 100,300 z
  """)

  let shapes = path.commandsToShapes(true, 1)

  benchmarks.add(Benchmark(
    name: "heart opaque",
    fills: @[Fill(
    shapes: shapes,
    transform: mat3(),
    paint: opaque,
    windingRule: NonZero
  )]))

  benchmarks.add(Benchmark(
    name: "heart not opaque",
    fills: @[Fill(
    shapes: shapes,
    transform: mat3(),
    paint: notOpaque,
    windingRule: NonZero
  )]))

block: # Tiger
  let
    data = readFile("tests/fileformats/svg/Ghostscript_Tiger.svg")
    parsed = parseSvg(data)

  var fills: seq[Fill]

  for (path, props) in parsed.elements:
    if props.display and props.opacity > 0:
      if props.fill != "none":
        let
          shapes = path.commandsToShapes(true, 1)
          paint = parseSomePaint(props.fill)
        fills.add(Fill(
          shapes: shapes,
          transform: props.transform,
          paint: paint,
          windingRule: props.fillRule
        ))

      if props.stroke != rgbx(0, 0, 0, 0) and props.strokeWidth > 0:
        let strokeShapes = strokeShapes(
          parseSomePath(path, false, props.transform.pixelScale),
          props.strokeWidth,
          props.strokeLineCap,
          props.strokeLineJoin,
          props.strokeMiterLimit,
          props.strokeDashArray,
          props.transform.pixelScale
        )
        let paint = newPaint(props.stroke)
        paint.color.a *= (props.opacity * props.strokeOpacity)
        fills.add(Fill(
          shapes: strokeShapes,
          transform: props.transform,
          paint: paint,
          windingRule: NonZero
        ))

  benchmarks.add(Benchmark(
    name: "tiger",
    fills: fills
  ))

block:
  for benchmark in benchmarks:
    let
      surface = imageSurfaceCreate(FORMAT_ARGB32, 900, 900)
      ctx = surface.create()

    ctx.setLineWidth(1)

    timeIt "[cairo] " & benchmark.name:
      for fill in benchmark.fills:
        if fill.shapes.len > 0:
          ctx.newPath()
          for shape in fill.shapes:
            ctx.moveTo(shape[0].x, shape[0].y)
            for v in shape:
              ctx.lineTo(v.x, v.y)
          let
            color = fill.paint.color
            matrix = Matrix(
              xx: fill.transform[0, 0],
              yx: fill.transform[0, 1],
              xy: fill.transform[1, 0],
              yy: fill.transform[1, 1],
              x0: fill.transform[2, 0],
              y0: fill.transform[2, 1],
            )
          ctx.setSourceRgba(color.r, color.g, color.b, color.a)
          ctx.setMatrix(matrix.unsafeAddr)
          ctx.setFillRule(
            if fill.windingRule == NonZero:
              FillRuleWinding
            else:
              FillRuleEvenOdd
          )
          ctx.fill()
          # ctx.stroke()

    # discard surface.writeToPng(("cairo_" & benchmark.name & ".png").cstring)

block:
  for benchmark in benchmarks:
    let image = newImage(900, 900)

    timeIt "[pixie] " & benchmark.name:
      for fill in benchmark.fills:
        if fill.shapes.len > 0:
          let p = newPath()
          for shape in fill.shapes:
            p.moveTo(shape[0])
            for v in shape:
              p.lineTo(v)
          image.fillPath(
            p,
            fill.paint,
            fill.transform,
            fill.windingRule
          )
          # image.strokePath(
          #   p,
          #   fill.paint,
          #   fill.transform,
          #   1
          # )

    # image.writeFile("pixie_" & benchmark.name & ".png")