/* jshint esnext:true */
/*
 *
 *  Weather extension for GNOME Shell
 *  - Displays a small weather information on the top panel.
 *  - On click, gives a popup with details about the weather.
 *
 * Copyright (C) 2011 - 2013
 *     ecyrbe <ecyrbe+spam@gmail.com>,
 *     Timur Kristof <venemo@msn.com>,
 *     Elad Alfassa <elad@fedoraproject.org>,
 *     Simon Legner <Simon.Legner@gmail.com>,
 *     Christian METZLER <neroth@xeked.com>,
 *     Mark Benjamin weather.gnome.Markie1@dfgh.net,
 *     Mattia Meneguzzo odysseus@fedoraproject.org,
 *     Meng Zhuo <mengzhuo1203+spam@gmail.com>,
 *     Jens Lody <jens@jenslody.de>
 * Copyright (C) 2014 -2015
 *     Jens Lody <jens@jenslody.de>,
 *
 *
 * This file is part of gnome-shell-extension-openweather.
 *
 * gnome-shell-extension-openweather is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * gnome-shell-extension-openweather is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with gnome-shell-extension-openweather.  If not, see <http://www.gnu.org/licenses/>.
 *
 */


const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const OpenweathermapOrg = Me.imports.openweathermap_org;
const Gettext = imports.gettext.domain('gnome-shell-extension-openweather');
const _ = Gettext.gettext;
const ngettext = Gettext.ngettext;

const OPENWEATHER_URL_HOST = 'api.openweathermap.org';
const OPENWEATHER_URL_BASE = 'http://' + OPENWEATHER_URL_HOST + '/data/2.5/';
const OPENWEATHER_URL_CURRENT = OPENWEATHER_URL_BASE + 'weather';
const OPENWEATHER_URL_FORECAST = OPENWEATHER_URL_BASE + 'forecast/daily';

function getWeatherIcon(code, night) {

    let iconname = ['weather-severe-alert'];
    // see http://bugs.openweathermap.org/projects/api/wiki/Weather_Condition_Codes
    // fallback icons are: weather-clear-night weather-clear weather-few-clouds-night weather-few-clouds weather-fog weather-overcast weather-severe-alert weather-showers weather-showers-scattered weather-snow weather-storm
    /*
weather-clouds-night.png
weather-freezing-rain.png
weather-hail.png
weather-many-clouds.png
weather-showers-day.png
weather-showers-night.png
weather-showers-scattered-day.png
weather-showers-scattered-night.png
weather-snow-rain.png
weather-snow-scattered-day.png
weather-snow-scattered-night.png
weather-snow-scattered.png
weather-storm-day.png
weather-storm-night.png

weather-severe-alert-symbolic.svg


weather-clear-night.png = weather-clear-night-symbolic.svg
weather-clear.png = weather-clear-symbolic.svg
weather-clouds.png = weather-overcast-symbolic.svg
weather-few-clouds-night.png = weather-few-clouds-night-symbolic.svg
weather-few-clouds.png = weather-few-clouds-symbolic.svg
weather-mist.png = weather-fog-symbolic.svg
weather-showers-scattered.png = weather-showers-scattered-symbolic.svg
weather-showers.png = weather-showers-symbolic.svg
weather-snow.png = weather-snow-symbolic.svg
weather-storm.png = weather-storm-symbolic.svg

*/
    switch (parseInt(code, 10)) {
        case 200: //Thunderstorm with light rain
        case 201: //Thunderstorm with rain
        case 202: //Thunderstorm with heavy rain
        case 210: //Light thunderstorm
        case 211: //Thunderstorm
        case 212: //Heavy thunderstorm
        case 221: //Ragged thunderstorm
        case 230: //Thunderstorm with light drizzle
        case 231: //Thunderstorm with drizzle
        case 232: //Thunderstorm with heavy drizzle
            iconname = ['weather-storm'];
            break;
        case 300: //Light intensity drizzle
        case 301: //drizzle
        case 302: //Heavy intensity drizzle
        case 310: //Light intensity drizzle rain
        case 311: //drizzle rain
        case 312: //Heavy intensity drizzle rain
        case 313: //Shower rain and drizzle
        case 314: //Heavy shower rain and drizzle
        case 321: //Shower drizzle
            iconname = ['weather-showers'];
            break;
        case 500: //Light rain
        case 501: //Moderate rain
        case 502: //Heavy intensity rain
        case 503: //Very heavy rain
        case 504: //Extreme rain
            iconname = ['weather-showers-scattered', 'weather-showers'];
            break;
        case 511: //Freezing rain
            iconname = ['weather-freezing-rain', 'weather-showers'];
            break;
        case 520: //Light intensity shower rain
        case 521: //Shower rain
        case 522: //Heavy intensity shower rain
        case 531: //Ragged shower rain
            iconname = ['weather-showers'];
            break;
        case 600: //Light snow
        case 601: //Snow
        case 602: //Heavy snow
        case 611: //Sleet
        case 612: //Shower sleet
        case 615: //Light rain and snow
        case 616: //Rain and snow
        case 620: //Light shower snow
        case 621: //Shower snow
        case 622: //Heavy shower snow
            iconname = ['weather-snow'];
            break;
        case 701: //Mist
        case 711: //Smoke
        case 721: //Haze
        case 741: //Fog
            iconname = ['weather-fog'];
            break;
        case 731: //Sand/Dust Whirls
        case 751: //Sand
        case 761: //Dust
        case 762: //VOLCANIC ASH
        case 771: //SQUALLS
        case 781: //TORNADO
            iconname = ['weather-severe-alert'];
            break;
        case 800: //Sky is clear
            iconname = ['weather-clear'];
            break;
        case 801: //Few clouds
        case 802: //Scattered clouds
            iconname = ['weather-few-clouds'];
            break;
        case 803: //Broken clouds
            iconname = ['weather-many-clouds', 'weather-overcast'];
            break;
        case 804: //Overcast clouds
            iconname = ['weather-overcast'];
            break;
    }

    for (let i = 0; i < iconname.length; i++) {
        if (night && this.hasIcon(iconname[i] + '-night'))
            return iconname[i] + '-night' + this.getIconType();
        if (this.hasIcon(iconname[i]))
            return iconname[i] + this.getIconType();
    }
    return 'weather-severe-alert' + this.getIconType();
}

function getWeatherCondition(code) {
    switch (parseInt(code, 10)) {
        case 200: //Thunderstorm with light rain
            return _('Thunderstorm with light rain');
        case 201: //Thunderstorm with rain
            return _('Thunderstorm with rain');
        case 202: //Thunderstorm with heavy rain
            return _('Thunderstorm with heavy rain');
        case 210: //Light thunderstorm
            return _('Light thunderstorm');
        case 211: //Thunderstorm
            return _('Thunderstorm');
        case 212: //Heavy thunderstorm
            return _('Heavy thunderstorm');
        case 221: //Ragged thunderstorm
            return _('Ragged thunderstorm');
        case 230: //Thunderstorm with light drizzle
            return _('Thunderstorm with light drizzle');
        case 231: //Thunderstorm with drizzle
            return _('Thunderstorm with drizzle');
        case 232: //Thunderstorm with heavy drizzle
            return _('Thunderstorm with heavy drizzle');
        case 300: //Light intensity drizzle
            return _('Light intensity drizzle');
        case 301: //Drizzle
            return _('Drizzle');
        case 302: //Heavy intensity drizzle
            return _('Heavy intensity drizzle');
        case 310: //Light intensity drizzle rain
            return _('Light intensity drizzle rain');
        case 311: //Drizzle rain
            return _('Drizzle rain');
        case 312: //Heavy intensity drizzle rain
            return _('Heavy intensity drizzle rain');
        case 313: //Shower rain and drizzle
            return _('Shower rain and drizzle');
        case 314: //Heavy shower rain and drizzle
            return _('Heavy shower rain and drizzle');
        case 321: //Shower drizzle
            return _('Shower drizzle');
        case 500: //Light rain
            return _('Light rain');
        case 501: //Moderate rain
            return _('Moderate rain');
        case 502: //Heavy intensity rain
            return _('Heavy intensity rain');
        case 503: //Very heavy rain
            return _('Very heavy rain');
        case 504: //Extreme rain
            return _('Extreme rain');
        case 511: //Freezing rain
            return _('Freezing rain');
        case 520: //Light intensity shower rain
            return _('Light intensity shower rain');
        case 521: //Shower rain
            return _('Shower rain');
        case 522: //Heavy intensity shower rain
            return _('Heavy intensity shower rain');
        case 531: //Ragged shower rain
            return _('Ragged shower rain');
        case 600: //Light snow
            return _('Light snow');
        case 601: //Snow
            return _('Snow');
        case 602: //Heavy snow
            return _('Heavy snow');
        case 611: //Sleet
            return _('Sleet');
        case 612: //Shower sleet
            return _('Shower sleet');
        case 615: //Light rain and snow
            return _('Light rain and snow');
        case 616: //Rain and snow
            return _('Rain and snow');
        case 620: //Light shower snow
            return _('Light shower snow');
        case 621: //Shower snow
            return _('Shower snow');
        case 622: //Heavy shower snow
            return _('Heavy shower snow');
        case 701: //Mist
            return _('Mist');
        case 711: //Smoke
            return _('Smoke');
        case 721: //Haze
            return _('Haze');
        case 731: //Sand/Dust Whirls
            return _('Sand/Dust Whirls');
        case 741: //Fog
            return _('Fog');
        case 751: //Sand
            return _('Sand');
        case 761: //Dust
            return _('Dust');
        case 762: //VOLCANIC ASH
            return _('VOLCANIC ASH');
        case 771: //SQUALLS
            return _('SQUALLS');
        case 781: //TORNADO
            return _('TORNADO');
        case 800: //Sky is clear
            return _('Sky is clear');
        case 801: //Few clouds
            return _('Few clouds');
        case 802: //Scattered clouds
            return _('Scattered clouds');
        case 803: //Broken clouds
            return _('Broken clouds');
        case 804: //Overcast clouds
            return _('Overcast clouds');
        default:
            return _('Not available');
    }
}

function parseWeatherCurrent() {
    if (this.currentWeatherCache === undefined) {
        // this is a reentrency guard
        this.currentWeatherCache = "in refresh";
        this.refreshWeatherCurrent();
        return;
    }

    if (this.currentWeatherCache == "in refresh")
        return;

    this.checkAlignment();
    this.checkPositionInPanel();

    let json = this.currentWeatherCache;

    this.owmCityId = json.id;
    // Refresh current weather
    let location = this.extractLocation(this._city);

    let comment = json.weather[0].description;
    if (this._translate_condition)
        comment = OpenweathermapOrg.getWeatherCondition(json.weather[0].id);

    let temperature = this.formatTemperature(json.main.temp);
    let sunrise = new Date(json.sys.sunrise * 1000);
    let sunset = new Date(json.sys.sunset * 1000);
    let now = new Date();

    let iconname = this.getWeatherIcon(json.weather[0].id, now < sunrise || now > sunset);

    if (this.lastBuildId === undefined)
        this.lastBuildId = 0;

    if (this.lastBuildDate === undefined)
        this.lastBuildDate = 0;

    if (this.lastBuildId != json.dt || !this.lastBuildDate) {
        this.lastBuildId = json.dt;
        this.lastBuildDate = new Date(this.lastBuildId * 1000);
    }

    let lastBuild = '-';

    if (this._clockFormat == "24h") {
        sunrise = sunrise.toLocaleFormat("%R");
        sunset = sunset.toLocaleFormat("%R");
        lastBuild = this.lastBuildDate.toLocaleFormat("%R");
    } else {
        sunrise = sunrise.toLocaleFormat("%I:%M %p");
        sunset = sunset.toLocaleFormat("%I:%M %p");
        lastBuild = this.lastBuildDate.toLocaleFormat("%I:%M %p");
    }

    let beginOfDay = new Date(new Date().setHours(0, 0, 0, 0));
    let d = Math.floor((this.lastBuildDate.getTime() - beginOfDay.getTime()) / 86400000);
    if (d < 0) {
        lastBuild = _("Yesterday");
        if (d < -1)
            lastBuild = ngettext("%d day ago", "%d days ago", -1 * d).format(-1 * d);
    }

    this._currentWeatherIcon.icon_name = this._weatherIcon.icon_name = iconname;

    let weatherInfoC = "";
    let weatherInfoT = "";

    if (this._comment_in_panel)
        weatherInfoC = comment;

    if (this._text_in_panel)
        weatherInfoT = temperature;

    this._weatherInfo.text = weatherInfoC + ((weatherInfoC && weatherInfoT) ? _(", ") : "") + weatherInfoT;

    this._currentWeatherSummary.text = comment + _(", ") + temperature;
    this._currentWeatherLocation.text = location;
    this._currentWeatherCloudiness.text = json.clouds.all + ' %';
    this._currentWeatherHumidity.text = json.main.humidity + ' %';
    this._currentWeatherPressure.text = this.formatPressure(json.main.pressure);
    this._currentWeatherSunrise.text = sunrise;
    this._currentWeatherSunset.text = sunset;
    this._currentWeatherBuild.text = lastBuild;
    this._currentWeatherWind.text = this.formatWind(json.wind.speed, this.getWindDirection(json.wind.deg));

    this.parseWeatherForecast();
    this.recalcLayout();
}

function refreshWeatherCurrent() {
    this.oldLocation = this.extractCoord(this._city);

    if (this.oldLocation.search(",") == -1)
        return;

    let params = {
        lat: this.oldLocation.split(",")[0],
        lon: this.oldLocation.split(",")[1],
        units: 'metric'
    };
    if (this._appid)
        params.APPID = this._appid;

    this.load_json_async(OPENWEATHER_URL_CURRENT, params, function(json) {
        if (json && (Number(json.cod) == 200)) {

            if (this.currentWeatherCache != json)
                this.currentWeatherCache = json;

            this.rebuildSelectCityItem();

            this.parseWeatherCurrent();
        } else {
            // we are connected, but get no (or no correct) data, so try to reload
            // after 10 minutes (recommendded by openweathermap.org)
            this.reloadWeatherCurrent(600);
        }
    });
    this.reloadWeatherCurrent(this._refresh_interval_current);
}

function parseWeatherForecast() {
    if (this.forecastWeatherCache === undefined) {
        // this is a reentrency guard
        this.forecastWeatherCache = "in refresh";
        this.refreshWeatherForecast();
        return;
    }

    if (this.forecastWeatherCache == "in refresh")
        return;

    let forecast = this.forecastWeatherCache;
    let beginOfDay = new Date(new Date().setHours(0, 0, 0, 0));

    // Refresh forecast
    for (let i = 0; i < this._days_forecast; i++) {
        let forecastUi = this._forecast[i];
        let forecastData = forecast[i];
        if (forecastData === undefined)
            continue;

        let t_low = this.formatTemperature(forecastData.temp.min);
        let t_high = this.formatTemperature(forecastData.temp.max);

        let comment = forecastData.weather[0].description;
        if (this._translate_condition)
            comment = OpenweathermapOrg.getWeatherCondition(forecastData.weather[0].id);

        let forecastDate = new Date(forecastData.dt * 1000);
        let dayLeft = Math.floor((forecastDate.getTime() - beginOfDay.getTime()) / 86400000);

        let date_string = _("Today");
        if (dayLeft == 1)
            date_string = _("Tomorrow");
        else if (dayLeft > 1)
            date_string = ngettext("In %d day", "In %d days", dayLeft).format(dayLeft);
        else if (dayLeft == -1)
            date_string = _("Yesterday");
        else if (dayLeft < -1)
            date_string = ngettext("%d day ago", "%d days ago", -1 * dayLeft).format(-1 * dayLeft);

        forecastUi.Day.text = date_string + ' (' + this.getLocaleDay(forecastDate.getDay()) + ')\n' + forecastDate.toLocaleDateString();
        forecastUi.Temperature.text = '\u2193 ' + t_low + '    \u2191 ' + t_high;
        forecastUi.Summary.text = comment;
        forecastUi.Icon.icon_name = this.getWeatherIcon(forecastData.weather[0].id);
    }
}

function refreshWeatherForecast() {


    this.oldLocation = this.extractCoord(this._city);

    if (this.oldLocation.search(",") == -1)
        return;

    let params = {
        lat: this.oldLocation.split(",")[0],
        lon: this.oldLocation.split(",")[1],
        units: 'metric',
        cnt: '13'
    };
    if (this._appid)
        params.APPID = this._appid;

    this.load_json_async(OPENWEATHER_URL_FORECAST, params, function(json) {
        if (json && (Number(json.cod) == 200)) {
            if (this.forecastWeatherCache != json.list) {
                this.owmCityId = json.city.id;
                this.forecastWeatherCache = json.list;
            }

            this.parseWeatherForecast();
        } else {
            // we are connected, but get no (or no correct) data, so try to reload
            // after 10 minutes (recommendded by openweathermap.org)
            this.reloadWeatherForecast(600);
        }
    });
    this.reloadWeatherForecast(this._refresh_interval_forecast);
}
