text: require 'csv'
  
  class Districts
  
    # Generates a mapping of zip codes to districts.
    # This is done, in theory, just once after redistricting until the next one
    #
    # This mapping is generated by using our boundary services loaded ZCTAs,
    # and congressional districts. It looks up all ZCTAs in the system, then
    # checks each one to see which districts it intersects, and saves to CSV.
  
    # options:
    #   zip: do a particular zip code
  
    def self.run(options = {})
      # failsafe, should be many less records than this
      maximum = 100000
  
      page = 1
      per_page = options[:per_page] ? options[:per_page].to_i : 100
  
      zip_count = 0
  
      dest = Environment.config['location']['zips']
      FileUtils.mkdir_p File.dirname(dest)
      if File.exists?(dest)
      	FileUtils.rm dest
      end
  
      # Zip.delete_all
  
      errors = []
  
      CSV.open(dest, "w") do |csv|
        while page < (maximum / per_page)
          puts "Fetching page #{page}..."
  
          if options[:zip]
            zips = [options[:zip]]
          else
            zips = zips_for page, per_page, options
  
            if zips.nil?
              Report.failure self, "Failure paging through zips on page #{page}, aborting"
              return
            end
          end
  
          zips.each do |zip|
            puts "[#{zip}] Finding districts..."
  
            districts = districts_for zip, options
            if districts.nil?
              errors << {zip: zip, message: "Couldn't load districts intersecting this zip"}
              next
            end
  
            districts.each do |district|
              csv << [zip, district[:state], district[:district]]
            end
  
            # cache in DB for quick lookup in exactly the format we want
  	  if not Zip.where(zip: zip, districts: districts).first
            	Zip.create! zip: zip, districts: districts
  		puts "Creating zip for #{zip} because it doesn't exist in the database"
  	  end
  
            puts "[#{zip}] Wrote #{districts.size} districts."
            zip_count += 1
          end
  
          if zips.size < per_page
            break
          else
            page += 1
          end
        end
      end
  
      if errors.any?
        Report.warning self, "Found #{errors.size} district lookup errors for zips", errors: errors
      end
  
      Report.success self, "Wrote #{zip_count} zips to #{dest}."
    end
  
    def self.zips_for(page, per_page, options = {})
      offset = (page - 1) * per_page
      limit = per_page
  
      host = Environment.config['location']['host']
      url = "http://#{host}/boundaries/?sets=zcta&limit=#{limit}&offset=#{offset}"
  
      response = Utils.download url, {json: true}.merge(options)
      return nil unless response
  
      response['objects'].map {|object| object['name']}
    end
  
    def self.districts_for(zip, options = {})
      host = Environment.config['location']['host']
      url = "http://#{host}/boundaries/?intersects=zcta/#{zip}&sets=cd&limit=1000"
  
      begin
        response = Utils.download url, {json: true, destination: destination_for(zip)}.merge(options)
      rescue Yajl::ParseError
        return nil
      end
      return nil unless response
  
      response['objects'].map do |object|
        pieces = object['name'].split " "
        if object['name'] =~ /at Large/i
          {state: pieces[0], district: 0}
        else
          {state: pieces[0], district: pieces[1].to_i}
        end
      end
    end
  
    def self.destination_for(zip)
      "data/districts/zips/#{zip}/districts.json"
    end
  
    def self.page_dest_for(page)
      "data/districts/pages/#{page}.json"
    end
  
  end
  