/**
 * Rasterization module for converting shapes to pixels/rects
 * Used for software rendering of complex shapes
 */

var math = use('math')

var rasterize = {}

function within_wedge(dx, dy, start, end, full_circle) {
  if (full_circle) return true

  var ang = math.arc_tangent(dy, dx)
  if (ang < 0) ang += pi * 2
  var t = ang / (pi * 2)

  if (start <= end) return t >= start && t <= end
  return t >= start || t <= end
}

rasterize.ellipse = function ellipse(pos, radii, opt) {
  opt = opt || {}
  var rx = radii[0], ry = radii[1]
  if (rx <= 0 || ry <= 0) return []

  var cx = pos[0], cy = pos[1]
  var raw_start = opt.start || 0
  var raw_end = opt.end || 1
  var full_circle = abs(raw_end - raw_start) >= 1 - 1e-9
  var start = (raw_start % 1 + 1) % 1
  var end = (raw_end % 1 + 1) % 1
  var thickness = max(1, opt.thickness || 1)

  var rx_i = rx - thickness,
      ry_i = ry - thickness
  var hole = (rx_i > 0 && ry_i > 0)

  if (!hole && thickness == 1) {
    var points = []
    var rx_sq = rx * rx, ry_sq = ry * ry
    var two_rx_sq = rx_sq << 1, two_ry_sq = ry_sq << 1
    var x = 0, y = ry, px = 0, py = two_rx_sq * y
    var p = ry_sq - rx_sq * ry + 0.25 * rx_sq

    function add_pts(x, y) {
      var pts = [
        [cx + x, cy + y], [cx - x, cy + y],
        [cx + x, cy - y], [cx - x, cy - y]
      ]
      points = array(points, filter(pts, pt => within_wedge(pt[0]-cx, pt[1]-cy, start, end, full_circle)))
    }

    while (px < py) {
      add_pts(x, y)
      ++x; px += two_ry_sq
      if (p < 0) p += ry_sq + px
      else { --y; py -= two_rx_sq; p += ry_sq + px - py }
    }
    p = ry_sq*(x+.5)*(x+.5) + rx_sq*(y-1)*(y-1) - rx_sq*ry_sq
    while (y >= 0) {
      add_pts(x, y)
      --y; py -= two_rx_sq
      if (p > 0) p += rx_sq - py
      else { ++x; px += two_ry_sq; p += rx_sq - py + px }
    }
    return {type: 'points', data: points}
  }

  var strips = []
  var rx_sq  = rx * rx,  ry_sq  = ry * ry
  var rx_i_sq = rx_i * rx_i, ry_i_sq = ry_i * ry_i

  for (var dy = -ry; dy <= ry; ++dy) {
    var yy = dy * dy
    var x_out = floor(rx * math.sqrt(1 - yy / ry_sq))
    var y_screen = cy + dy

    var x_in = hole ? floor(rx_i * math.sqrt(1 - yy / ry_i_sq)) : -1

    var run_start = null
    for (var dx = -x_out; dx <= x_out; ++dx) {
      if (hole && abs(dx) <= x_in) { run_start = null; continue }
      if (!within_wedge(dx, dy, start, end, full_circle)) { run_start = null; continue }

      if (run_start == null) run_start = cx + dx

      var last = (dx == x_out)
      var next_in_ring =
        !last &&
        !(hole && abs(dx+1) <= x_in) &&
        within_wedge(dx+1, dy, start, end, full_circle)

      if (last || !next_in_ring) {
        push(strips, {
          x: run_start,
          y: y_screen,
          width: (cx + dx) - run_start + 1,
          height: 1
        })
        run_start = null
      }
    }
  }
  return {type: 'rects', data: strips}
}

rasterize.circle = function circle(pos, radius, opt) {
  return rasterize.ellipse(pos, [radius, radius], opt)
}

rasterize.outline_rect = function outline_rect(rect, thickness) {
  if (thickness <= 0) {
    return {type: 'rect', data: rect}
  }

  if ((thickness << 1) >= rect.width ||
      (thickness << 1) >= rect.height) {
    return {type: 'rect', data: rect}
  }

  var x0 = rect.x,
      y0 = rect.y,
      x1 = rect.x + rect.width,
      y1 = rect.y + rect.height

  return {type: 'rects', data: [
    { x:x0,         y:y0,          width:rect.width, height:thickness },
    { x:x0,         y:y1-thickness, width:rect.width, height:thickness },
    { x:x0,         y:y0+thickness, width:thickness,
      height:rect.height - (thickness<<1) },
    { x:x1-thickness, y:y0+thickness, width:thickness,
      height:rect.height - (thickness<<1) }
  ]}
}

rasterize.round_rect = function round_rect(rect, radius, thickness) {
  thickness = thickness || 1
  
  if (thickness <= 0) {
    return rasterize.fill_round_rect(rect, radius)
  }

  radius = min(radius, rect.width >> 1, rect.height >> 1)

  if ((thickness << 1) >= rect.width ||
      (thickness << 1) >= rect.height ||
      thickness >= radius) {
    return rasterize.fill_round_rect(rect, radius)
  }

  var x0 = rect.x,
      y0 = rect.y,
      x1 = rect.x + rect.width  - 1,
      y1 = rect.y + rect.height - 1

  var cx_l = x0 + radius,  cx_r = x1 - radius
  var cy_t = y0 + radius,  cy_b = y1 - radius
  var r_out = radius
  var r_in  = radius - thickness

  var rects = [
    { x:x0 + radius, y:y0, width:rect.width - (radius << 1), height:thickness },
    { x:x0 + radius, y:y1 - thickness + 1, width:rect.width - (radius << 1), height:thickness },
    { x:x0, y:y0 + radius, width:thickness, height:rect.height - (radius << 1) },
    { x:x1 - thickness + 1, y:y0 + radius, width:thickness, height:rect.height - (radius << 1) }
  ]

  var strips = []

  for (var dy = 0; dy < radius; ++dy) {
    var dy_sq   = dy * dy
    var dx_out  = floor(math.sqrt(r_out * r_out - dy_sq))
    var dx_in   = (r_in > 0 && dy < r_in)
                  ? floor(math.sqrt(r_in * r_in - dy_sq))
                  : -1
    var w = dx_out - dx_in
    if (w <= 0) continue

    push(strips,
      { x:cx_l - dx_out,      y:cy_t - dy, width:w, height:1 },
      { x:cx_r + dx_in + 1,   y:cy_t - dy, width:w, height:1 },
      { x:cx_l - dx_out,      y:cy_b + dy, width:w, height:1 },
      { x:cx_r + dx_in + 1,   y:cy_b + dy, width:w, height:1 }
    )
  }

  return {type: 'rects', data: array(rects, strips)}
}

rasterize.fill_round_rect = function fill_round_rect(rect, radius) {
  radius = min(radius, rect.width >> 1, rect.height >> 1)

  var x0 = rect.x,
      y0 = rect.y,
      x1 = rect.x + rect.width  - 1,
      y1 = rect.y + rect.height - 1

  var rects = [
    { x:x0 + radius, y:y0, width:rect.width - (radius << 1), height:rect.height },
    { x:x0, y:y0 + radius, width:radius, height:rect.height - (radius << 1) },
    { x:x1 - radius + 1, y:y0 + radius, width:radius, height:rect.height - (radius << 1) }
  ]

  var cx_l = x0 + radius,  cx_r = x1 - radius
  var cy_t = y0 + radius,  cy_b = y1 - radius
  var caps = []

  for (var dy = 0; dy < radius; ++dy) {
    var dx = floor(math.sqrt(radius * radius - dy * dy))
    var w  = (dx << 1) + 1

    push(caps, 
      { x:cx_l - dx, y:cy_t - dy, width:w, height:1 },
      { x:cx_r - dx, y:cy_t - dy, width:w, height:1 },
      { x:cx_l - dx, y:cy_b + dy, width:w, height:1 },
      { x:cx_r - dx, y:cy_b + dy, width:w, height:1 }
    )
  }

  return {type: 'rects', data: array(rects, caps)}
}

return rasterize