var cam = {}

/* 
  presentation can be one of 
  letterbox
  overscan
  stretch
  .. or simply 'null' for no presentation
*/

function rect_contains_pt(rect, x, y) {
  return x >= rect.x && x <= rect.x + rect.width &&
         y >= rect.y && y <= rect.y + rect.height
}

var basecam = {
  pos: {x:0,y:0},
  ortho:true,
  width: 1,
  height: 1,
  fov:50,
  near_z:0,
  far_z:1000,
  anchor: {x:0.5,y:0.5},
  rotation:[0,0,0,1],
  presentation: "letterbox",
  background: {r:1,g:1,b:1,a:0},
  viewport: {x:0,y:0,width:1,height:1},
  aspect_ratio() {
    return this.y/this.x;
  },

  screen_rect() {
    var src = { width: this.width, height: this.height }
    var dst = { x: 0, y: 0, width: gameres.width, height: gameres.height }
    return place_rect(src, dst, this.presentation, this.align_x, this.align_y, false)
  },

  rect() {
    return {x:0, y:0, width: this.width, height: this.height}
  },
  
  sensor() {
    return {
      width: gameres.width,
      height: gameres.height,
    }
  },

    // The rectangle (in window pixels) where this camera’s target will be drawn
  screen_rect() {
    var src = { width: this.width, height: this.height }
    var dst = { x: 0, y: 0, width: gameres.width, height: gameres.height }
    return place_rect(src, dst, this.presentation, this.align_x, this.align_y, false)
  },

  // --- Space converters ---------------------------------------------------
  // World -> View UV [0..1] independent of pixels/letterbox
  world_to_view_uv(wx, wy) {
    var ax = this.anchor[0], ay = this.anchor[1]
    var u = (wx - this.pos[0]) / this.width + ax
    var v = (wy - this.pos[1]) / this.height + ay

    // apply viewport (maps camera-local UV into sub-rect)
    var vp = this.viewport
    u = vp.x + u * vp.width
    v = vp.y + v * vp.height
    return { x: u, y: v }
  },

  // View UV -> World
  view_uv_to_world(u, v) {
    var vp = this.viewport
    var uu = (u - vp.x) / vp.width
    var vv = (v - vp.y) / vp.height
    var ax = this.anchor[0], ay = this.anchor[1]
    var wx = this.pos[0] + (uu - ax) * this.width
    var wy = this.pos[1] + (vv - ay) * this.height
    return { x: wx, y: wy }
  },

  // World -> Window pixels (what you want for hit-tests)
  world_to_window(wx, wy) {
    var uv = this.world_to_view_uv(wx, wy)
    var sr = this.screen_rect()
    var sx = sr.x + uv.x * sr.width
    var sy = sr.y + uv.y * sr.height
    return { x: sx, y: sy }
  },

  // Window pixels -> World (mouse picking)
  window_to_world(sx, sy) {
    var sr = this.screen_rect()
    if (!rect_contains_pt(sr, sx, sy)) {
      // outside letterbox bars; clamp or return null
      // return null
    }
    var u = (sx - sr.x) / sr.width
    var v = (sy - sr.y) / sr.height
    return this.view_uv_to_world(u, v)
  },

  // World -> normalized window [0..1]
  world_to_screen_norm(wx, wy) {
    var p = this.world_to_window(wx, wy)
    return { x: p.x / gameres.width, y: p.y / gameres.height }
  },

  // Normalized window [0..1] -> World
  screen_norm_to_world(nx, ny) {
    var sx = nx * gameres.width
    var sy = ny * gameres.height
    return this.window_to_world(sx, sy)
  },

  screen_to_world: function(sx, sy) {
    // sx, sy are normalized screen coordinates (0-1), bottom left [0,0], top right [1,1]
    var screen_rect = this.screen_rect()
    var pixel_x = sx * gameres.width
    var pixel_y = sy * gameres.height
    var rel_x = (pixel_x - screen_rect.x) / screen_rect.width
    var rel_y = (pixel_y - screen_rect.y) / screen_rect.height
    var ax = this.anchor[0]
    var ay = this.anchor[1]
    var world_x = this.pos[0] + (rel_x - ax) * this.width
    var world_y = this.pos[1] + (rel_y - ay) * this.height
    return {x: world_x, y: world_y}
  },
}

cam.make = function()
{
  return meme(basecam)
}

return cam
