Aircraft.class_eval do
  
  # TODO use http://www.transtats.bts.gov/Download_Lookup.asp?Lookup=L_AIRCRAFT_TYPE
  def self.bts_name_dictionary
    @_bts_dictionary ||= LooseTightDictionary.new RemoteTable.new(:url => 'http://www.transtats.bts.gov/Download_Lookup.asp?Lookup=L_AIRCRAFT_TYPE', :select => lambda { |record| record['Code'].to_i.between?(1, 998) }),
                                                  :tightenings  => RemoteTable.new(:url => 'http://spreadsheets.google.com/pub?key=tiS_6CCDDM_drNphpYwE_iw&single=true&gid=0&output=csv', :headers => false),
                                                  :identities   => RemoteTable.new(:url => 'http://spreadsheets.google.com/pub?key=tiS_6CCDDM_drNphpYwE_iw&single=true&gid=3&output=csv', :headers => false),
                                                  :blockings    => RemoteTable.new(:url => 'http://spreadsheets.google.com/pub?key=tiS_6CCDDM_drNphpYwE_iw&single=true&gid=4&output=csv', :headers => false),
                                                  :blocking_only => true,
                                                  :right_reader => lambda { |record| record['Description'] }
  end
  
  # warning: self-referential, assumes it will be used once first import step is done
  def self.icao_name_dictionary
    @_icao_dictionary ||= LooseTightDictionary.new Aircraft.all,
                                                   :tightenings  => RemoteTable.new(:url => 'http://spreadsheets.google.com/pub?key=tiS_6CCDDM_drNphpYwE_iw&single=true&gid=0&output=csv', :headers => false),
                                                   :identities   => RemoteTable.new(:url => 'http://spreadsheets.google.com/pub?key=tiS_6CCDDM_drNphpYwE_iw&single=true&gid=3&output=csv', :headers => false),
                                                   :blockings    => RemoteTable.new(:url => 'http://spreadsheets.google.com/pub?key=tiS_6CCDDM_drNphpYwE_iw&single=true&gid=4&output=csv', :headers => false),
                                                   :right_reader => lambda { |record| record.manufacturer_name.to_s + ' ' + record.name.to_s }
  end

  class Aircraft::BtsMatcher
    attr_reader :wants
    def initialize(wants)
      @wants = wants
    end
    def match(raw_faa_icao_record)
      @_match ||= Hash.new
      return @_match[raw_faa_icao_record] if @_match.has_key?(raw_faa_icao_record)
      faa_icao_record = [ raw_faa_icao_record['Manufacturer'] + ' ' + raw_faa_icao_record['Model'] ]
      bts_record = Aircraft.bts_name_dictionary.left_to_right faa_icao_record
      retval = case wants
      when :bts_aircraft_type_code
        bts_record['Code']
      when :bts_name
        bts_record['Description']
      end if bts_record
      @_match[raw_faa_icao_record] = retval
    end
  end

  class Aircraft::FuelUseMatcher
    def match(raw_fuel_use_record)
      @_match ||= Hash.new
      return @_match[raw_fuel_use_record] if @_match.has_key?(raw_fuel_use_record)

      aircraft_record = if raw_fuel_use_record['ICAO'] =~ /\A[0-9A-Z]+\z/
        Aircraft.find_by_icao_code raw_fuel_use_record['ICAO']
      end
      
      aircraft_record ||= if raw_fuel_use_record['Aircraft Name'].present?
        Aircraft.icao_name_dictionary.left_to_right [ raw_fuel_use_record['Aircraft Name'] ]
      end
      
      if aircraft_record
        @_match[raw_fuel_use_record] = aircraft_record.icao_code
      else
        raise "Didn't find a match for #{raw_fuel_use_record['Aircraft Name']} (#{raw_fuel_use_record['ICAO']}), which we found in the fuel use spreadsheet"
      end
    end
  end

  class Aircraft::Guru
    # for errata
    def is_a_dc_plane?(row)
      row['Designator'] =~ /^DC\d/i
    end
    
    # def is_a_crj_900?(row)
    #   row['Designator'].downcase == 'crj9'
    # end
    
    def is_a_g159?(row)
      row['Designator'] =~ /^G159$/
    end

    def is_a_galx?(row)
      row['Designator'] =~ /^GALX$/
    end
    
    def method_missing(method_id, *args, &block)
      if method_id.to_s =~ /\Ais_n?o?t?_?attributed_to_([^\?]+)/
        manufacturer_name = $1
        manufacturer_regexp = Regexp.new(manufacturer_name.gsub('_', ' ?'), Regexp::IGNORECASE)
        matches = manufacturer_regexp.match(args.first['Manufacturer']) # row['Manufacturer'] =~ /mcdonnell douglas/i
        method_id.to_s.include?('not_attributed') ? matches.nil? : !matches.nil?
      else
        super
      end
    end
  end

  data_miner do
    schema Earth.database_options do
      string   'icao_code'
      string   'manufacturer_name'
      string   'name'
      string   'bts_name'
      string   'bts_aircraft_type_code'
      string   'brighter_planet_aircraft_class_code'
      string   'fuel_use_aircraft_name'
      float    'm3'
      string   'm3_units'
      float    'm2'
      string   'm2_units'
      float    'm1'
      string   'm1_units'
      float    'endpoint_fuel'
      string   'endpoint_fuel_units'
      float    'seats'
      float    'distance'
      string   'distance_units'
      float    'load_factor'
      float    'freight_share'
      float    'payload'
      float    'weighting'
      index    'bts_aircraft_type_code'
    end
    
    ('A'..'Z').each do |letter|
    # ('Z'..'Z').each do |letter|
      import( "ICAO aircraft codes starting with the letter #{letter} used by the FAA",
              :url => "http://www.faa.gov/air_traffic/publications/atpubs/CNT/5-2-#{letter}.htm",
              :errata => Errata.new(:url => 'http://spreadsheets.google.com/pub?key=tObVAGyqOkCBtGid0tJUZrw',
                                    :responder => Aircraft::Guru.new),
              :encoding => 'windows-1252',
              :row_xpath => '//table/tr[2]/td/table/tr',
              :column_xpath => 'td' ) do
        key 'icao_code', :field_name => 'Designator'
        store 'bts_aircraft_type_code', :matcher => Aircraft::BtsMatcher.new(:bts_aircraft_type_code)
        store 'bts_name', :matcher => Aircraft::BtsMatcher.new(:bts_name)
        store 'manufacturer_name', :field_name => 'Manufacturer'
        store 'name', :field_name => 'Model'
      end
    end
    
    # TODO fixme need to remake aircraft classes dictionary based on ICAO codes
    # sabshere 5/17/10 or maybe we can replace this with typ/weight class from FAA (?)
    import "Brighter Planet's aircraft class codes",
           :url => 'http://static.brighterplanet.com/science/data/transport/air/bts_aircraft_type/bts_aircraft_types-brighter_planet_aircraft_classes.csv' do
      key   'bts_aircraft_type_code', :field_name => 'bts_aircraft_type'
      store 'brighter_planet_aircraft_class_code'
    end

    # EEA
    import "pre-calculated fuel use equation coefficients",
           :url => 'http://static.brighterplanet.com/science/data/transport/air/fuel_use/aircraft_fuel_use_formulae.ods',
           :select => lambda { |row| row['ICAO'].present? or row['Aircraft Name'].present? } do
      key   'icao_code', :matcher => Aircraft::FuelUseMatcher.new
      store 'fuel_use_aircraft_name', :field_name => 'Aircraft Name'
      store 'm3'
      store 'm2'
      store 'm1'
      store 'endpoint_fuel', :field_name => 'b'
    end
    
    process "Derive some average flight characteristics from flight segments" do
      FlightSegment.run_data_miner!
      aircraft = Aircraft.arel_table
      segments = FlightSegment.arel_table

      # non-working joins method
      # update_all "aircraft.distance_1            = (SELECT * FROM (#{FlightSegment.joins(:aircraft).weighted_average_relation(:distance,            :weighted_by => :passengers                                           ).to_sql}) AS anonymous_1)"
      # update_all "aircraft.load_factor_1         = (SELECT * FROM (#{FlightSegment.joins(:aircraft).weighted_average_relation(:load_factor,         :weighted_by => :passengers                                           ).to_sql}) AS anonymous_1)"
      # execute %{
      #   update aircraft as t1
      #   set t1.distance_1 = (SELECT * FROM (#{FlightSegment.joins(:aircraft).weighted_average_relation(:distance,            :weighted_by => :passengers                                           ).where('t1.bts_aircraft_type_code = flight_segments.bts_aircraft_type_code').to_sql}) AS anonymous_1)
      # }

      conditional_relation = aircraft[:bts_aircraft_type_code].eq(segments[:bts_aircraft_type_code])
      update_all "seats         = (#{FlightSegment.weighted_average_relation(:seats,         :weighted_by => :passengers                                           ).where(conditional_relation).to_sql})"
      update_all "distance      = (#{FlightSegment.weighted_average_relation(:distance,      :weighted_by => :passengers                                           ).where(conditional_relation).to_sql})"
      update_all "load_factor   = (#{FlightSegment.weighted_average_relation(:load_factor,   :weighted_by => :passengers                                           ).where(conditional_relation).to_sql})"
      update_all "freight_share = (#{FlightSegment.weighted_average_relation(:freight_share, :weighted_by => :passengers                                           ).where(conditional_relation).to_sql})"
      update_all "payload       = (#{FlightSegment.weighted_average_relation(:payload,       :weighted_by => :passengers, :disaggregate_by => :departures_performed).where(conditional_relation).to_sql})"
      
      update_all "weighting = (#{segments.project(segments[:passengers].sum).where(aircraft[:bts_aircraft_type_code].eq(segments[:bts_aircraft_type_code])).to_sql})"
    end
    
    process "Synthesize AircraftManufacturer" do
      AircraftManufacturer.run_data_miner!
    end
  end
end