$LOAD_PATH.unshift("#{File.dirname(__FILE__)}")

require 'geo/postcode'
require 'geo/sa1'
require 'geo/sa2'
require 'geo/sa3'
require 'geo/sa4'
require 'geo/state'
require 'geo/suburb'

require 'pr_geohash'

class EnrichmentDb::Geo::Locator < EnrichmentDb::DatumModel
  attr_reader :id
  attr_reader :name
  attr_reader :state
  attr_reader :sa4
  attr_reader :sa3
  attr_reader :sa2
  attr_reader :sa1
  attr_reader :postcode
  attr_reader :suburb
  attr_reader :smallest_region

  def initialize(data)
    self.class.all_regions.each do |region|
      region_name = region.object_type
      region_data = data[region_name]
      region_instance = !!region_data ? region.new(region_data) : nil
      instance_variable_set("@#{region_name}", region_instance)

      if data['smallest_region'] == region_name
        @id = region_data['id']
        @name = region_data['name']
        @smallest_region = region_name
      end
    end
  end

  def self.geo_locate(geohash)
    geohash = format_geo(geohash)
    values = [geohash]

    data = {}
    all_regions.each do |region|
      region_name = region.object_type
      region_id = find_id(data, region_name)

      result = if !region_id.nil?
        region.by_id(region_id)
      else
        region.by_boundary(values)
      end
      data['smallest_region'] ||= region_name if !result.nil?
      data[region_name] = result
    end 
    self.new(data)
  end

  def self.find_id(data, region_type)
    id = "#{region_type}_id"
    data.each do |k, v|
      return v[id] if v.is_a?(Hash) && v.keys.include?(id)
    end
    nil
  end

  def self.all_regions
    all_region_names.collect do |region|
      EnrichmentDb::Geo.const_get(region)
    end.sort_by do |region|
      region.scale
    end
  end

  def self.all_region_names 
    EnrichmentDb::Geo.constants - [:Region, :Locator]
  end

  def self.format_geo(geo)
    return geo if geo.is_a?(String)

  	fail EnrichmentDb::InvalidGeoPointFormat unless valid_geo_point?(geo)
    geo[:lat] ||= geo['lat']
    geo[:lon] ||= geo['lon']
    GeoHash.encode(geo[:lat], geo[:lon])
  end

  def self.valid_geo_point?(geo_point)
  	geo_point.is_a?(Hash) && geo_point.keys.sort.collect(&:to_sym) == [:lat, :lon]
  end

  private

  def self.object_type
    to_s.split(':').last.downcase.to_sym
  end

  def self.make_table_name(object_name)
    "#{object_name}s"
  end
end