require 'date'
require 'cgi'

module Agents
  class WeatherAgent < Agent
    cannot_receive_events!

    description <<-MD
      The WeatherAgent creates an event for the day's weather at a given `location`.

      You also must select `which_day` you would like to get the weather for where the number 0 is for today and 1 is for tomorrow and so on. Weather is only returned for 1 week at a time.

      The weather can be provided by either Wunderground or ForecastIO. To choose which `service` to use, enter either `forecastio` or `wunderground`.

      The `location` can be a US zipcode, or any location that Wunderground supports. To find one, search [wunderground.com](http://wunderground.com) and copy the location part of the URL.  For example, a result for San Francisco gives `http://www.wunderground.com/US/CA/San_Francisco.html` and London, England gives `http://www.wunderground.com/q/zmw:00000.1.03772`.  The locations in each are `US/CA/San_Francisco` and `zmw:00000.1.03772`, respectively.

      If you plan on using ForecastIO, the `location` must be a set of GPS coordinates.

      You must setup an [API key for Wunderground](http://www.wunderground.com/weather/api/) in order to use this Agent with Wunderground.

      You must setup an [API key for Forecast](https://developer.forecast.io/) in order to use this Agent with ForecastIO.
    MD

    event_description <<-MD
      Events look like this:

          {
            "location": 12345,
            "date": {
              "epoch": "1357959600",
              "pretty": "10:00 PM EST on January 11, 2013"
            },
            "high": {
              "fahrenheit": "64",
              "celsius": "18"
            },
            "low": {
              "fahrenheit": "52",
              "celsius": "11"
            },
            "conditions": "Rain Showers",
            "icon": "rain",
            "icon_url": "http://icons-ak.wxug.com/i/c/k/rain.gif",
            "skyicon": "mostlycloudy",
            ...
          }
    MD

    default_schedule "8pm"

    def working?
      event_created_within?(2) && !recent_error_logs?
    end

    def key_setup?
      options['api_key'] && options['api_key'] != "your-key"
    end

    def default_options
      {
        'service' => 'wunderground',
        'api_key' => 'your-key',
        'location' => '94103',
        'which_day' => '0'
      }
    end

    def validate_options
      errors.add(:base, "service is required") unless options['service'].present?
      errors.add(:base, "service must be set to 'forecastio' or 'wunderground'") unless ["forecastio", "wunderground"].include?(options['service'])
      errors.add(:base, "location is required") unless options['location'].present?
      errors.add(:base, "api_key is required") unless options['api_key'].present?
      errors.add(:base, "which_day selection is required") unless options['which_day'].present?
    end

    def wunderground
      data = Wunderground.new(options['api_key']).forecast_for(options['location'])['forecast']['simpleforecast']['forecastday'] if key_setup?
      return data
    end

    def forecastio
      ForecastIO.api_key = options['api_key'] if key_setup?
      data = ForecastIO.forecast(options['location'].split(',')[0],options['location'].split(',')[1])['daily']['data']
      return data
    end

    def model(data,service,which_day)
      day = Hash.new
      if service == "wunderground"
        day =  data[which_day.to_i]
      elsif service == "forecastio"
        data.each do |value|
          timestamp = Time.at(value.time)
          if (timestamp.to_date - Time.now.to_date).to_i == which_day.to_i
            day = {
              'date' => {
                'epoch' => value.time.to_s,
                'pretty' => timestamp.strftime("%l:%M %p %Z on %B %d, %Y"),
                'day' => timestamp.day,
                'month' => timestamp.month,
                'year' => timestamp.year,
                'yday' => timestamp.yday,
                'hour' => timestamp.hour,
                'min' => timestamp.strftime("%M"),
                'sec' => timestamp.sec,
                'isdst' => timestamp.isdst ? 1 : 0 ,
                'monthname' => timestamp.strftime("%B"),
                'monthname_short' => timestamp.strftime("%b"),
                'weekday_short' => timestamp.strftime("%a"),
                'weekday' => timestamp.strftime("%A"),
                'ampm' => timestamp.strftime("%p"),
                'tz_short' => timestamp.zone
              },
              'period' => which_day.to_i,
              'high' => {
                'fahrenheit' => value.temperatureMax.round().to_s,
                'epoch' => value.temperatureMaxTime.to_s,
                'fahrenheit_apparent' => value.apparentTemperatureMax.round().to_s,
                'epoch_apparent' => value.apparentTemperatureMaxTime.to_s,
                'celsius' => ((5*(Float(value.temperatureMax) - 32))/9).round().to_s
              },
              'low' => {
                'fahrenheit' => value.temperatureMin.round().to_s,
                'epoch' => value.temperatureMinTime.to_s,
                'fahrenheit_apparent' => value.apparentTemperatureMin.round().to_s,
                'epoch_apparent' => value.apparentTemperatureMinTime.to_s,
                'celsius' => ((5*(Float(value.temperatureMin) - 32))/9).round().to_s
              },
              'conditions' => value.summary,
              'icon' => value.icon,
              'avehumidity' => (value.humidity * 100).to_i,
              'sunriseTime' => value.sunriseTime.to_s,
              'sunsetTime' => value.sunsetTime.to_s,
              'moonPhase' => value.moonPhase.to_s,
              'precip' => {
                'intensity' => value.precipIntensity.to_s,
                'intensity_max' => value.precipIntensityMax.to_s,
                'intensity_max_epoch' => value.precipIntensityMaxTime.to_s,
                'probability' => value.precipProbability.to_s,
                'type' => value.precipType
              },
              'dewPoint' => value.dewPoint.to_s,
              'avewind' => {
                'mph' => value.windSpeed.round().to_s,
                'kph' =>  (Float(value.windSpeed) * 1.609344).round().to_s,
                'degrees' => value.windBearing.to_s
              },
              'visibility' => value.visibility.to_s,
              'cloudCover' => value.cloudCover.to_s,
              'pressure' => value.pressure.to_s,
              'ozone' => value.ozone.to_s
            }
              end
            end
      end
      return day.merge('location' => options['location'])
    end

    def check
      if key_setup?
        if options['service'] == 'forecastio'
          weather = model(forecastio,options['service'],options['which_day'])
        elsif options['service'] == 'wunderground'
          weather = model(wunderground,options['service'],options['which_day'])
        end
        create_event :payload => weather
      end
    end

  end
end