# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  
 #  Geodesy representation conversion functions (c) Chris Veness 2002-2010
 #   - www.movable-type.co.uk/scripts/latlong.html
 #
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  

# Parses string representing degrees/minutes/seconds into numeric degrees
# 
# This is very flexible on formats, allowing signed decimal degrees, or deg-min-sec optionally
# suffixed by compass direction (NSEW). A variety of separators are accepted (eg 3ยบ 37' 09"W) 
# or fixed-width format without separators (eg 0033709W). Seconds and minutes may be omitted. 
# (Note minimal validation is done).
# 
# @param   [String|Number] Degrees or deg/min/sec in variety of formats
# @returns [Number] Degrees as decimal number
# @throws  ArgumentError

require 'sugar-high/numeric'
require 'sweetloader'

module GeoUnits
  autoload_modules :Constants, :Converter, :Maps, :Numeric

  class << self
    attr_accessor :default_coords_order

    def default_coords_order
      @default_coords_order ||= :lng_lat
    end
  end

  def self.included(base)
    [:Maps, :Constants, :"Converter::Units"].each do |module_name| 
      module_name = "GeoUnits::#{module_name.to_s.camelize}".constantize
      base.send :include, module_name
      base.extend module_name
    end
  end

  def self.units
    [:feet, :meters, :kms, :kilometers, :miles, :radians]
  end

  (units - [:radians]).each do |unit_type|
    define_singleton_method "#{unit_type}_to" do |unit, number = 0|
      return 0 if number <= 0        
      unit = normalized(unit)
      
      converter = GeoUnits::Maps::Meters
      from = converter.from_unit[unit_type]
      to = converter.to_unit[unit]

      m = number * from * to
    end
  end

  def self.radians_to unit, number, lat = 0
    unit = normalized(unit)
    # factor = GeoUnits::Converter::Units.units_per_longitude_degree(lat, unit)
    # puts "factor: #{factor} - #{unit}"
    (GeoUnits::Maps::Earth.distance_per_latitude_degree[unit] * number.to_f) 
  end

  module ClassMethods
    def normalized unit = :km
      unit = key(unit)
      return :feet if feet_unit.include? unit
      return :meters if meters_unit.include? unit
      return :kilometers if kms_unit.include? unit
      return :miles if miles_unit.include? unit
      return :radians if radins_unit.include? unit

      raise ArgumentError, "Normalize unit error, unit key: #{unit}"
    end

    def key unit = :km
      unit = unit.to_sym
      methods.grep(/_unit$/).each do |meth|
        return meth.to_s.chomp('_unit').to_sym if send(meth).include? unit
      end
      raise ArgumentError, "Unknown unit key: #{unit}"
    end

    def all_units
      [:miles, :mile, :kms, :km, :feet, :foot, :meter, :meters, :radians, :rad]
    end

    protected

    def feet_unit
      [:ft, :feet, :foot]
    end

    def meters_unit
      [:m, :meter, :meters]
    end

    def kms_unit
      [:km, :kms, :kilometer, :kilometers]
    end

    def miles_unit
      [:mil, :mile, :miles]
    end

    def radians_unit
      [:rad, :radians]
    end
  end

  extend ClassMethods
end

require 'geo_units/core_ext'