 _ = require "underscore"
 Glyph = require "../glyph/glyph"
+
+class ImagePool
+
+  constructor: () ->
+    @images = []
+
+  pop: () ->
+    img = @images.pop()
+    if img?
+      return img
+    else
+      return new Image()
+
+  push: (img) ->
+    if img.constructor == Array
+      Array::push.apply(@images, img)
+    else
+      @images.push(img)
 
 class ProjectionUtils
 

    quadKey = @tile_xyz_to_quadkey(x, y, z)
    return @url.replace("{QUADKEY}", quadKey)


class TileLayerView extends Glyph.View

  _index_data: () ->
    @_xy_index()

  _set_data: () ->
    @need_load = (true for img in @url)
    @loaded = (false for img in @url)

    @_xy_index()

  _map_data: () ->
    @sw = @sdist(@renderer.xmapper, @x, @tile_size, 'edge', @mget('dilate'))
    @sh = @sdist(@renderer.ymapper, @y, @tile_size, 'edge', @mget('dilate'))

  _render: (ctx, indices, {url, image, need_load, sx, sy, sw, sh, angle}) ->
    for i in indices

      if isNaN(sx[i]+sy[i]+angle[i])
        continue

      if need_load[i]
        img = new Image()
        img.onload = do (img, i) =>
          return () =>
            @loaded[i] = true
            @renderer.request_render()

        img.src = url[i]
        need_load[i] = false
      else if @loaded[i]
        @_render_image(ctx, i, image[i], sx, sy, @tile_size, @tile_size, angle)

  _final_sx_sy: (anchor, sx, sy, sw, sh) ->
    switch anchor
      when "top_left"      then [sx       , sy       ]
      when "top_center"    then [sx - sw/2, sy       ]
      when "top_right"     then [sx - sw  , sy       ]
      when "right_center"  then [sx - sw  , sy - sh/2]
      when "bottom_right"  then [sx - sw  , sy - sh  ]
      when "bottom_center" then [sx - sw/2, sy - sh  ]
      when "bottom_left"   then [sx       , sy - sh  ]
      when "left_center"   then [sx       , sy - sh/2]
      when "center"        then [sx - sw/2, sy - sh/2]

  _render_image: (ctx, i, image, sx, sy, sw, sh, angle) ->

    anchor = @mget('anchor') or "top_left"
    [sx, sy] = @_final_sx_sy(anchor, sx[i], sy[i], 256, 256)

    ctx.save()

    ctx.globalAlpha = @mget("global_alpha")

    if angle[i]
      ctx.translate(sx, sy)
      ctx.rotate(angle[i])
      ctx.drawImage(image, 0, 0, 256, 256)
      ctx.rotate(-angle[i])
      ctx.translate(-sx, -sy)
    else
      ctx.drawImage(image, sx, sy, 256, 256)
    ctx.restore()

class TileLayer extends Glyph.Model
  default_view: TileLayerView
  type: 'TileLayer'
  tile_width: 256
  tile_height: 256
  visuals: []
  distances: ['w', 'h']
  angles: ['angle']
  fields: ['url:string']

  defaults: ->
    return _.extend {}, super(), {
      angle: 0
      global_alpha: 1.0
    }

  display_defaults: ->
    return _.extend {}, super(), {
    }

module.exports =

    [xmax, ymax] = @meters_to_geographic(xmax, ymax)
    return [xmin, ymin, xmax, ymax]

class GridLayer

  tile_xyz_to_key: (x, y, z) ->
    return x.toString() + ":" + y.toString() + ":" + z.toString()

  key_to_tile_xyz: (key) ->
    return (parseInt(c) for c in key.split(':'))

class MercatorTileProvider extends GridLayer



  constructor: (@url, @tile_size=256) ->
    @utils = new ProjectionUtils()
    @pool = new ImagePool()
    @origin_shift = 2 * Math.PI * 6378137 / 2.0
    @initial_resolution = 2 * Math.PI * 6378137 / @tile_size
    @resolutions = (@get_resolution(z) for z in [0..30])
    @tiles = {}

  get_image_url: () ->
    throw Error "Unimplemented Method"

  get_resolution: (level) ->
    return @initial_resolution / Math.pow(2, level)

  get_level_by_extent: (extent, height, width) ->
    x_rs = (extent[2] - extent[0]) / width
    y_rs = (extent[3] - extent[1]) / height
    res = Math.max(x_rs, y_rs)

    closest = @resolutions.reduce (previous, current) ->
      return current if (Math.abs(current - res) < Math.abs(previous - res))
      return previous

    return @resolutions.indexOf(closest)

  snap_to_zoom: (extent, height, width, level) ->
    desired_res = @resolutions[level]
    desired_x_delta = width * desired_res
    desired_y_delta = height * desired_res

    [xmin, ymin, xmax, ymax] = extent
    x_adjust = (desired_x_delta - (xmax - xmin)) / 2
    y_adjust = (desired_y_delta - (ymax - ymin)) / 2

    return [xmin - x_adjust, ymin - y_adjust, xmax + x_adjust, ymax + y_adjust]

  tms_to_wmts: (x, y, z) ->
    '''Note this works both ways'''
