using MediaBrowser.Common.Serialization;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Weather;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Weather;
using System;
using System.ComponentModel.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.WorldWeatherOnline
{
    /// 
    /// Based on http://www.worldweatheronline.com/free-weather-feed.aspx
    /// The classes in this file are a reproduction of the json output, which will then be converted to our weather model classes
    /// 
    [Export(typeof(IWeatherProvider))]
    public class WeatherProvider : IWeatherProvider
    {
        /// 
        /// Gets or sets the logger.
        /// 
        /// The logger.
        private ILogger Logger { get; set; }
        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// The logger.
        /// logger
        [ImportingConstructor]
        public WeatherProvider([Import("logger")] ILogger logger)
        {
            if (logger == null)
            {
                throw new ArgumentNullException("logger");
            }
            Logger = logger;
        }
        /// 
        /// The _weather semaphore
        /// 
        private readonly SemaphoreSlim _weatherSemaphore = new SemaphoreSlim(10, 10);
        /// 
        /// Gets the weather info async.
        /// 
        /// The location.
        /// The cancellation token.
        /// Task{WeatherInfo}.
        /// location
        public async Task GetWeatherInfoAsync(string location, CancellationToken cancellationToken)
        {
            if (string.IsNullOrWhiteSpace(location))
            {
                throw new ArgumentNullException("location");
            }
            if (cancellationToken == null)
            {
                throw new ArgumentNullException("cancellationToken");
            }
            const int numDays = 5;
            const string apiKey = "24902f60f1231941120109";
            var url = "http://free.worldweatheronline.com/feed/weather.ashx?q=" + location + "&format=json&num_of_days=" + numDays + "&key=" + apiKey;
            Logger.Info("Accessing weather from " + url);
            using (var stream = await Kernel.Instance.HttpManager.Get(url, _weatherSemaphore, cancellationToken).ConfigureAwait(false))
            {
                var data = JsonSerializer.DeserializeFromStream(stream).data;
                return GetWeatherInfo(data);
            }
        }
        /// 
        /// Converst the json output to our WeatherInfo model class
        /// 
        /// The data.
        /// WeatherInfo.
        private WeatherInfo GetWeatherInfo(WeatherData data)
        {
            var info = new WeatherInfo();
            if (data.current_condition != null)
            {
                var condition = data.current_condition.FirstOrDefault();
                if (condition != null)
                {
                    info.CurrentWeather = condition.ToWeatherStatus();
                }
            }
            if (data.weather != null)
            {
                info.Forecasts = data.weather.Select(w => w.ToWeatherForecast()).ToArray();
            }
            return info;
        }
    }
    /// 
    /// Class WeatherResult
    /// 
    class WeatherResult
    {
        /// 
        /// Gets or sets the data.
        /// 
        /// The data.
        public WeatherData data { get; set; }
    }
    /// 
    /// Class WeatherData
    /// 
    public class WeatherData
    {
        /// 
        /// Gets or sets the current_condition.
        /// 
        /// The current_condition.
        public WeatherCondition[] current_condition { get; set; }
        /// 
        /// Gets or sets the weather.
        /// 
        /// The weather.
        public DailyWeatherInfo[] weather { get; set; }
    }
    /// 
    /// Class WeatherCondition
    /// 
    public class WeatherCondition
    {
        /// 
        /// Gets or sets the temp_ C.
        /// 
        /// The temp_ C.
        public string temp_C { get; set; }
        /// 
        /// Gets or sets the temp_ F.
        /// 
        /// The temp_ F.
        public string temp_F { get; set; }
        /// 
        /// Gets or sets the humidity.
        /// 
        /// The humidity.
        public string humidity { get; set; }
        /// 
        /// Gets or sets the weather code.
        /// 
        /// The weather code.
        public string weatherCode { get; set; }
        /// 
        /// To the weather status.
        /// 
        /// WeatherStatus.
        public WeatherStatus ToWeatherStatus()
        {
            return new WeatherStatus
            {
                TemperatureCelsius = int.Parse(temp_C),
                TemperatureFahrenheit = int.Parse(temp_F),
                Humidity = int.Parse(humidity),
                Condition = DailyWeatherInfo.GetCondition(weatherCode)
            };
        }
    }
    /// 
    /// Class DailyWeatherInfo
    /// 
    public class DailyWeatherInfo
    {
        /// 
        /// Gets or sets the date.
        /// 
        /// The date.
        public string date { get; set; }
        /// 
        /// Gets or sets the precip MM.
        /// 
        /// The precip MM.
        public string precipMM { get; set; }
        /// 
        /// Gets or sets the temp max C.
        /// 
        /// The temp max C.
        public string tempMaxC { get; set; }
        /// 
        /// Gets or sets the temp max F.
        /// 
        /// The temp max F.
        public string tempMaxF { get; set; }
        /// 
        /// Gets or sets the temp min C.
        /// 
        /// The temp min C.
        public string tempMinC { get; set; }
        /// 
        /// Gets or sets the temp min F.
        /// 
        /// The temp min F.
        public string tempMinF { get; set; }
        /// 
        /// Gets or sets the weather code.
        /// 
        /// The weather code.
        public string weatherCode { get; set; }
        /// 
        /// Gets or sets the winddir16 point.
        /// 
        /// The winddir16 point.
        public string winddir16Point { get; set; }
        /// 
        /// Gets or sets the winddir degree.
        /// 
        /// The winddir degree.
        public string winddirDegree { get; set; }
        /// 
        /// Gets or sets the winddirection.
        /// 
        /// The winddirection.
        public string winddirection { get; set; }
        /// 
        /// Gets or sets the windspeed KMPH.
        /// 
        /// The windspeed KMPH.
        public string windspeedKmph { get; set; }
        /// 
        /// Gets or sets the windspeed miles.
        /// 
        /// The windspeed miles.
        public string windspeedMiles { get; set; }
        /// 
        /// To the weather forecast.
        /// 
        /// WeatherForecast.
        public WeatherForecast ToWeatherForecast()
        {
            return new WeatherForecast
            {
                Date = DateTime.Parse(date),
                HighTemperatureCelsius = int.Parse(tempMaxC),
                HighTemperatureFahrenheit = int.Parse(tempMaxF),
                LowTemperatureCelsius = int.Parse(tempMinC),
                LowTemperatureFahrenheit = int.Parse(tempMinF),
                Condition = GetCondition(weatherCode)
            };
        }
        /// 
        /// Gets the condition.
        /// 
        /// The weather code.
        /// WeatherConditions.
        public static WeatherConditions GetCondition(string weatherCode)
        {
            switch (weatherCode)
            {
                case "362":
                case "365":
                case "320":
                case "317":
                case "182":
                    return WeatherConditions.Sleet;
                case "338":
                case "335":
                case "332":
                case "329":
                case "326":
                case "323":
                case "377":
                case "374":
                case "371":
                case "368":
                case "395":
                case "392":
                case "350":
                case "227":
                case "179":
                    return WeatherConditions.Snow;
                case "314":
                case "311":
                case "308":
                case "305":
                case "302":
                case "299":
                case "296":
                case "293":
                case "284":
                case "281":
                case "266":
                case "263":
                case "359":
                case "356":
                case "353":
                case "185":
                case "176":
                    return WeatherConditions.Rain;
                case "260":
                case "248":
                    return WeatherConditions.Fog;
                case "389":
                case "386":
                case "200":
                    return WeatherConditions.Thunderstorm;
                case "230":
                    return WeatherConditions.Blizzard;
                case "143":
                    return WeatherConditions.Mist;
                case "122":
                    return WeatherConditions.Overcast;
                case "119":
                    return WeatherConditions.Cloudy;
                case "115":
                    return WeatherConditions.PartlyCloudy;
                default:
                    return WeatherConditions.Sunny;
            }
        }
    }
}