module QuadTile
  begin
    require "quad_tile/quad_tile_so"
  rescue MissingSourceFile
    def self.tile_for_point(lat, lon)
      x = ((lon + 180) * 65535 / 360).round
      y = ((lat + 90) * 65535 / 180).round

      return tile_for_xy(x, y)
    end

    def self.tiles_for_area(minlat, minlon, maxlat, maxlon)
      minx = ((minlon + 180) * 65535 / 360).round
      maxx = ((maxlon + 180) * 65535 / 360).round
      miny = ((minlat + 90) * 65535 / 180).round
      maxy = ((maxlat + 90) * 65535 / 180).round
      tiles = []

      minx.upto(maxx) do |x|
        miny.upto(maxy) do |y|
          tiles << tile_for_xy(x, y)
        end
      end

      return tiles
    end

    def self.tile_for_xy(x, y)
      t = 0

      16.times do
        t = t << 1
        t = t | 1 unless (x & 0x8000).zero?
        x <<= 1
        t = t << 1
        t = t | 1 unless (y & 0x8000).zero?
        y <<= 1
      end

      return t
    end
  end

  def self.sql_for_area(minlat, minlon, maxlat, maxlon, prefix)
    sql = Array.new
    single = Array.new

    iterate_tiles_for_area(minlat, minlon, maxlat, maxlon) do |first,last|
      if first == last
        single.push(first)
      else
        sql.push("#{prefix}tile BETWEEN #{first} AND #{last}")
      end
    end

    sql.push("#{prefix}tile IN (#{single.join(',')})") if single.size > 0

    return "( " + sql.join(" OR ") + " )"
  end

  def self.iterate_tiles_for_area(minlat, minlon, maxlat, maxlon)
    tiles = tiles_for_area(minlat, minlon, maxlat, maxlon)
    first = last = nil

    tiles.sort.each do |tile|
      if last.nil?
        first = last = tile
      elsif tile == last + 1
        last = tile
      else
        yield first, last

        first = last = tile
      end
    end

    yield first, last unless last.nil?
  end

  private_class_method :tile_for_xy, :iterate_tiles_for_area
end
