var math = use('math')
var color = use('color')
var prosperon = use('prosperon')

var draw = {}

var current_list = []

// Clear current list
draw.clear = function() {
  current_list = []
}

// Get commands from current list
draw.get_commands = function() {
  return current_list
}

// Helper to add a command
function add_command(type, data) {
  data.cmd = type
  current_list.push(data)
}

// Default geometry definitions
var ellipse_def = {
  start: 0,
  end: 1,
  mode: 'fill',
  thickness: 1,
}

var line_def = {
  thickness: 1,
  cap:"butt", 
}

var rect_def = {
  thickness:1,
  radius: 0
}

var image_info = {
  tile_x: false,
  tile_y: false,
  flip_x: false,
  flip_y: false,
  mode: 'linear'
}

var circle_def = {
  inner_radius:1, // percentage: 1 means filled circle
  start:0,
  end: 1,
}

// Drawing functions
draw.point = function(pos, size, opt = {}, material) {
  add_command("draw_point", {
    pos: pos,
    size: size,
    opt: opt,
    material: material
  })
}

draw.ellipse = function(pos, radii, defl, material) {
  var opt = defl ? {...ellipse_def, ...defl} : ellipse_def
  if (opt.thickness <= 0) opt.thickness = number.max(radii[0], radii[1])
  
  add_command("draw_ellipse", {
    pos: pos,
    radii: radii,
    opt: opt,
    material: material
  })
}

draw.line = function(points, defl, material)
{
  var opt = defl ? {...line_def, ...defl} : line_def
    
  add_command("draw_line", {
    points: points,
    opt: opt,
    material: material
  })
}

draw.cross = function render_cross(pos, size, defl, material) {
  var a = [pos.add([0, size]), pos.add([0, -size])]
  var b = [pos.add([size, 0]), pos.add([-size, 0])]
  draw.line(a, defl, material)
  draw.line(b, defl, material)
}

draw.arrow = function render_arrow(start, end, wingspan = 4, wingangle = 10, defl, material) {
  var dir = math.norm(end.sub(start))
  var wing1 = [math.rotate(dir, wingangle).scale(wingspan).add(end), end]
  var wing2 = [math.rotate(dir, -wingangle).scale(wingspan).add(end), end]
  draw.line([start, end], defl, material)
  draw.line(wing1, defl, material)
  draw.line(wing2, defl, material)
}

draw.rectangle = function render_rectangle(rect, defl, material = {color:{r:1,g:1,b:1,a:1}}) {
  var opt = defl ? {...rect_def, ...defl} : rect_def
  
  add_command("draw_rect", {
    rect,
    opt,
    material
  })
}

var slice9_info = {
  tile_top:true,
  tile_bottom:true,
  tile_left:true,
  tile_right:true,
  tile_center_x:true,
  tile_center_right:true,
}

draw.slice9 = function slice9(image, rect = [0,0], slice = 0, info = slice9_info, material) {
  if (!image) throw Error('Need an image to render.')
  
  add_command("draw_slice9", {
    image,
    rect,
    slice,
    info,
    material
  })
}

draw.image = function image(image, rect, scale = {x:1,y:1}, anchor, shear, info, material) {
  if (!rect) throw Error('Need rectangle to render image.')
  if (!image) throw Error('Need an image to render.')
  
  if (!('x' in rect && 'y' in rect)) throw Error('Must provide X and Y for image.')
  
  add_command("draw_image", {
    image,
    rect,
    scale,
    anchor,
    shear,
    info,
    material
  })
}

draw.circle = function render_circle(pos, radius, defl, material) {
  draw.ellipse(pos, [radius,radius], defl, material)
}

// wrap is the width before wrapping
// config is any additional config to pass to the text renderer
var text_base_config = {
  align: 'left', // left, right, center, justify
  break: 'word', // word, character
}
draw.text = function text(text, pos, font = 'fonts/c64.8', color = {r:1,g:1,b:1,a:1}, wrap = 0, config = {}) {
  config.align ??= text_base_config.align
  config.break ??= text_base_config.break
  
  add_command("draw_text", {
    text,
    pos,
    font,
    wrap,
    material: {color},
    config
  })
}

draw.grid = function grid(rect, spacing, thickness = 1, offset = {x: 0, y: 0}, material) {
  if (!rect || rect.x == null || rect.y == null || 
      rect.width == null || rect.height == null) {
    throw Error('Grid requires rect with x, y, width, height')
  }
  if (!is_object(spacing)|| is_null(spacing.x) || is_null(spacing.y)) {
    throw Error('Grid requires spacing with x and y')
  }
  
  var left = rect.x
  var right = rect.x + rect.width
  var top = rect.y
  var bottom = rect.y + rect.height
  
  // Apply offset and align to grid
  var start_x = number.floor((left - offset.x) / spacing.x) * spacing.x + offset.x
  var end_x = number.ceiling((right - offset.x) / spacing.x) * spacing.x + offset.x
  var start_y = number.floor((top - offset.y) / spacing.y) * spacing.y + offset.y
  var end_y = number.ceiling((bottom - offset.y) / spacing.y) * spacing.y + offset.y
  
  // Draw vertical lines
  for (var x = start_x; x <= end_x; x += spacing.x) {
    if (x >= left && x <= right) {
      var line_top = number.max(top, start_y)
      var line_bottom = number.min(bottom, end_y)
      draw.line([[x, line_top], [x, line_bottom]], {thickness: thickness}, material)
    }
  }
  
  // Draw horizontal lines
  for (var y = start_y; y <= end_y; y += spacing.y) {
    if (y >= top && y <= bottom) {
      var line_left = number.max(left, start_x)
      var line_right = number.min(right, end_x)
      draw.line([[line_left, y], [line_right, y]], {thickness: thickness}, material)
    }
  }
}

draw.scissor = function(rect)
{
  var screen_rect = null
  if (rect && prosperon.camera) {
    var bottom_left = prosperon.camera.world_to_window(rect.x, rect.y)
    var top_right = prosperon.camera.world_to_window(rect.x + rect.width, rect.y + rect.height)
    var screen_left = bottom_left.x
    var screen_top = bottom_left.y
    var screen_right = top_right.x
    var screen_bottom = top_right.y

    screen_rect = {
      x: number.round(screen_left),
      y: number.round(screen_top),
      width: number.round(screen_right - screen_left),
      height: number.round(screen_bottom - screen_top)
    }

    // TODO: must be a better way than manually inverting here. Some camera specific function.
    var sensor = prosperon.camera.sensor()
    screen_rect.y = sensor.height - screen_rect.y - screen_rect.height
  }

  current_list.push({
    cmd: "scissor",
    rect: screen_rect
  })
}

draw.add_command = function(cmd)
{
  current_list.push(cmd)
}

return draw