Newer
Older
mountainnavigation / app / src / main / java / de / apps4ics / mountainnavigation / handlers / WeatherHandler.java
package de.apps4ics.mountainnavigation.handlers;

import android.app.Activity;
import android.app.FragmentManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.util.Log;

import com.survivingwithandroid.weather.lib.WeatherClient;
import com.survivingwithandroid.weather.lib.WeatherCode;
import com.survivingwithandroid.weather.lib.WeatherConfig;
import com.survivingwithandroid.weather.lib.exception.WeatherLibException;
import com.survivingwithandroid.weather.lib.exception.WeatherProviderInstantiationException;
import com.survivingwithandroid.weather.lib.model.DayForecast;
import com.survivingwithandroid.weather.lib.model.HistoricalHourWeather;
import com.survivingwithandroid.weather.lib.model.HistoricalWeather;
import com.survivingwithandroid.weather.lib.model.Weather;
import com.survivingwithandroid.weather.lib.model.WeatherForecast;
import com.survivingwithandroid.weather.lib.provider.openweathermap.OpenweathermapProviderType;
import com.survivingwithandroid.weather.lib.request.WeatherRequest;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import de.apps4ics.mountainnavigation.InformDialog;
import de.apps4ics.mountainnavigation.InformListDialog;
import de.apps4ics.mountainnavigation.MainActivity;
import de.apps4ics.mountainnavigation.MyWeather;
import de.apps4ics.mountainnavigation.R;
import de.apps4ics.mountainnavigation.WeatherDatabase;
import de.apps4ics.mountainnavigation.adapters.WarningListAdapter;

/**
 * Created by Vinz on 06.03.2016.
 */
public class WeatherHandler {
    private static final String API_KEY = "fd4034defae557fd5f2fdaaf73c3402c"; //OpenWeatherMap API Key
    private Context context;
    private WeatherDatabase weatherDb;
    private WeatherClient weatherClient;
    private Weather todaysWeather;
    private Weather tomorrowsWeather;
    private HistoricalWeather histWeather;
    private DayForecast todaysForecast;
    private DayForecast tomorrowsForecast;
    private final static float MIN_HOT_TEMP_DEFAULT = 30.0f;
    private final static float MAX_COLD_TEMP_DEFAULT = 10.0f;
    private static float MIN_HOT_TEMP;
    private static float MAX_COLD_TEMP;
    private static float minWindySpeed;

    private static Activity activity;
    private static FragmentManager fragmentManager;

    //TODO split into severe weather and foggy, windy, sunny, ...
    private static final Integer[] severeWeatherCodes = new Integer[]{
        WeatherCode.TORNADO.getCode(),
                WeatherCode.TROPICAL_STORM.getCode(),
                WeatherCode.HURRICANE.getCode(),
                WeatherCode.SEVERE_THUNDERSTORMS.getCode(),
                WeatherCode.THUNDERSTORMS.getCode(),
                WeatherCode.MIXED_RAIN_SNOW.getCode(),
                WeatherCode.MIXED_RAIN_SLEET.getCode(),
                WeatherCode.MIXED_SNOW_SLEET.getCode(),
                WeatherCode.FREEZING_DRIZZLE.getCode(),
                WeatherCode.FREEZING_RAIN.getCode(),
                WeatherCode.HEAVY_SHOWERS.getCode(),
                WeatherCode.SNOW_FLURRIES.getCode(),
                WeatherCode.LIGHT_SNOW_SHOWERS.getCode(),
                WeatherCode.BLOWING_SNOW.getCode(),
                WeatherCode.SNOW.getCode(),
                WeatherCode.HAIL.getCode(),
                WeatherCode.SLEET.getCode(),
                WeatherCode.FOGGY.getCode(),
                WeatherCode.HAZE.getCode(),
                WeatherCode.WINDY.getCode(),
                WeatherCode.COLD.getCode(),
                WeatherCode.SUNNY.getCode(),
                WeatherCode.MIXED_RAIN_AND_HAIL.getCode(),
                WeatherCode.ISOLATED_THUNDERSTORMS.getCode(),
                WeatherCode.SCATTERED_THUNDERSTORMS.getCode(),
                WeatherCode.HEAVY_SNOW.getCode(),
                WeatherCode.SCATTERED_SNOW_SHOWERS.getCode(),
                WeatherCode.THUNDERSHOWERS.getCode(),
                WeatherCode.SNOW_SHOWERS.getCode(),
                WeatherCode.ISOLATED_THUDERSHOWERS.getCode(),
                WeatherCode.TORNADO.getCode()
    };

    public WeatherHandler(Context context) {
        weatherDb = new WeatherDatabase(context);
        this.context = context;

        try {
            activity = (Activity) context;
            fragmentManager = activity.getFragmentManager();
        } catch(ClassCastException e) {
            Log.e(MainActivity.TAG, "Could not get fragment manager!");
            //TODO close app?
        }

        WeatherClient.ClientBuilder weatherBuilder = new WeatherClient.ClientBuilder();
        WeatherConfig weatherConfig = new WeatherConfig();
        weatherConfig.unitSystem = WeatherConfig.UNIT_SYSTEM.M;
        weatherConfig.ApiKey = API_KEY;
        weatherConfig.lang = MainActivity.getRes().getString(R.string.lang_identifier);
        weatherConfig.numDays = 2;

        SharedPreferences sharedPrefs = MainActivity.getSharedPrefs();
        Resources res = MainActivity.getRes();

        MIN_HOT_TEMP = Float.parseFloat(sharedPrefs.getString(res.getString(R.string.settings_hint_min_hot_temp_key), String.valueOf(MIN_HOT_TEMP_DEFAULT)));
        MAX_COLD_TEMP = Float.parseFloat(sharedPrefs.getString(res.getString(R.string.settings_hint_max_cold_temp_key), String.valueOf(MAX_COLD_TEMP_DEFAULT)));

        minWindySpeed = 8; //http://www.wettergefahren-fruehwarnung.de/Artikel/beaufort.html
        if(weatherConfig.unitSystem.equals(WeatherConfig.UNIT_SYSTEM.I)){
            MIN_HOT_TEMP = celsiusToFahrenheit(MIN_HOT_TEMP);
            MAX_COLD_TEMP = celsiusToFahrenheit(MAX_COLD_TEMP);
        }

        weatherClient = null;
        try {
            weatherClient = weatherBuilder.attach(context)
                    .provider(new OpenweathermapProviderType())
                    .httpClient(com.survivingwithandroid.weather.lib.client.okhttp.WeatherDefaultClient.class)
                    .config(weatherConfig)
                    .build();
        } catch (WeatherProviderInstantiationException e) {
            e.printStackTrace();
        }
    }

    /**
     *
     * @param offset number of days (in the past) to search for result in the cache, only for getCurrentWeatherCached
     */
    public MyWeather getCurrentWeather(double lat, double lon, int offset) {
        if(MainActivity.hasInternet()) return getCurrentWeatherOnline(lat, lon);
        else return getCurrentWeatherCached(lat, lon, offset);
    }

    private MyWeather getCurrentWeatherOnline(double lat, double lon) {
        //return new MyWeather();
        return null;
    }

    private MyWeather getCurrentWeatherCached(double lat, double lon, int offset) {
        //return new MyWeather();
        return null;
    }

    public void getForecast(double lat, double lon, int days) {
        getForecast(lat, lon, days, 0);
    }

    public void getForecast(double lat, double lon, int days, int offset) {
//TODO Move this to the not-yet-implemented upload method
        weatherClient.getForecastWeather(new WeatherRequest(lon, lat), new WeatherClient.ForecastWeatherEventListener() {
            @Override
            public void onWeatherRetrieved(WeatherForecast forecast) {
                todaysForecast = forecast.getForecast(0);
                tomorrowsForecast = forecast.getForecast(1);
                todaysWeather = todaysForecast.weather;
                tomorrowsWeather = tomorrowsForecast.weather;
                Log.d(MainActivity.TAG, "City [" + todaysWeather.location.getCity() + "] Current temp: " + todaysWeather.temperature.getTemp());

                String title = String.format("Weather for %s in %s", todaysWeather.location.getCity(), todaysWeather.location.getCountry());
                String msg = String.format("%1$.1f%2$s (%3$.1f%4$s/%5$.1f%6$s)\nSunrise: %7$s\nSunset: %8$s\nWeatherCode: %9$d\nWeatherId: %10$d",
                        todaysForecast.forecastTemp.day,
                        forecast.getUnit().tempUnit,
                        todaysForecast.forecastTemp.min,
                        forecast.getUnit().tempUnit,
                        todaysForecast.forecastTemp.max,
                        forecast.getUnit().tempUnit,
                        MainActivity.df_hm.format(new Date(todaysWeather.location.getSunrise() * 1000)),
                        MainActivity.df_hm.format(new Date(todaysWeather.location.getSunset() * 1000)),
                        todaysWeather.currentCondition.getWeatherCode().getCode(),
                        todaysWeather.currentCondition.getWeatherId());
                Log.d(MainActivity.TAG, title + ": " + msg);
                displayHints();
                displaySevereWeather();
            }

            @Override
            public void onWeatherError(WeatherLibException wle) {
                Log.e(MainActivity.TAG, "Weather error - parsing data");
                wle.printStackTrace();
            }

            @Override
            public void onConnectionError(Throwable t) {
                Log.e(MainActivity.TAG, "Connection Error");
                t.printStackTrace();
            }
        });
    }

    public List<MyWeather> getForecastOnline(double lat, double lon, int days, int step) {
        return null;
    }

    public List<MyWeather> getForecastCached(double lat, double lon, int days, int step, int offset) {
        return null;
    }

    public List<MyWeather> getHistoricalWeather(float lat, float lon, long start, long end){
        return getHistoricalWeather(lat, lon, start, end, 3);
    }

    public List<MyWeather> getHistoricalWeather(float lat, float lon, long start, long end, int step){
        ArrayList<MyWeather> weathers = new ArrayList<>();
        Date startDate = new Date(start / 1000); //OWM uses seconds instead of milliseconds
        Date endDate = new Date(end / 1000);
        histWeather = null;
        weatherClient.getHistoricalWeather(new WeatherRequest(lon, lat), startDate, endDate, new WeatherClient.HistoricalWeatherEventListener() {
            @Override
            public void onWeatherRetrieved(HistoricalWeather historicalWeather) {
                //TODO check if it was sunny/rainy and set fountain options based on this
                histWeather = historicalWeather;
            }

            @Override
            public void onWeatherError(WeatherLibException wle) {
                Log.e(MainActivity.TAG, "Weather error - parsing data");
                wle.printStackTrace();
            }

            @Override
            public void onConnectionError(Throwable t) {
                Log.e(MainActivity.TAG, "Connection Error");
                t.printStackTrace();
            }
        });
        for(HistoricalHourWeather hhw : histWeather.getHoistoricalData()) {
            weathers.add(new MyWeather(hhw.weather, hhw.timestamp));
        }
        return weathers;
    }

    private void insertIntoDatabase(Weather weather) {
        insertIntoDatabase(weather, 0);
    }

    private void insertIntoDatabase(Weather weather, long timestamp) {
        insertIntoDatabase(new MyWeather(weather, timestamp));
    }

    private void insertIntoDatabase(MyWeather weather) {
        weatherDb.insertWeather(weather);
    }

    private void displayHints() {
        Resources res = MainActivity.getRes();
        //TODO add more hints
        if (MainActivity.showWeatherHints()) {
            List<String> titles = new ArrayList<>();
            List<String> descs = new ArrayList<>();
            List<String> infos = new ArrayList<>();
            if (isSunny()) {
                titles.add(res.getString(R.string.hint_dialog_titles_sunny));
                descs.add(res.getString(R.string.hint_dialog_msgs_sunny));
                infos.add("");
            }
            if (isHot()) {
                titles.add(res.getString(R.string.hint_dialog_titles_hot));
                descs.add(res.getString(R.string.hint_dialog_msgs_hot));
                infos.add(res.getString(R.string.hint_dialog_info_hot));
            }
            if (isCold()) {
                titles.add(res.getString(R.string.hint_dialog_titles_cold));
                descs.add(res.getString(R.string.hint_dialog_msgs_cold));
                infos.add("");
            }
            if (isWindy()) {
                titles.add(res.getString(R.string.hint_dialog_titles_windy));
                descs.add(res.getString(R.string.hint_dialog_msgs_windy));
                infos.add("");
            }
            if (isAfterSunset()) {
                titles.add(res.getString(R.string.hint_dialog_titles_sunset));
                descs.add(res.getString(R.string.hint_dialog_msgs_sunset));
                infos.add("");
            }
            if (titles.size() > 0 && descs.size() > 0) {
                String[] titleArr = new String[titles.size()];
                String[] descArr = new String[descs.size()];
                String[] infosArr = new String[infos.size()];
                titleArr = titles.toArray(titleArr);
                descArr = descs.toArray(descArr);
                infosArr = infos.toArray(infosArr);
                WarningListAdapter warningAdapter = new WarningListAdapter(activity, titleArr, descArr, infosArr);
                InformListDialog weatherHintDialog = new InformListDialog(MainActivity.getRes().getString(R.string.hint_dialog_title), warningAdapter);
                weatherHintDialog.show(fragmentManager, "Warning Hint Dialog");
            }
        }
    }

    private void displaySevereWeather() {
        if (isSevereWeather(todaysWeather.currentCondition.getWeatherCode().getCode())) {
            Log.d(MainActivity.TAG, "It is severe weather! :(");
            Resources res = context.getResources();
            String warnMsg = String.format(res.getString(R.string.weather_warning_dialog_msg), todaysWeather.location.getCity(), todaysWeather.currentCondition.getWeatherCode().getLabel(context));
            InformDialog informAlertDialog = new InformDialog(res.getString(R.string.weather_warning_dialog_title), warnMsg);
            informAlertDialog.show(fragmentManager, "Inform Dialog - Weather Warning");
        }
    }

    private boolean isSevereWeather(int weatherCode){
        return Arrays.asList(severeWeatherCodes).contains(weatherCode);
    }

    private boolean isAfterSunset(){
        return isAfterSunset(System.currentTimeMillis());
    }

    private boolean isAfterSunset(long time){
        return time > (todaysWeather.location.getSunset() * 1000) && time < (tomorrowsWeather.location.getSunrise() * 1000);
    }

    private boolean isHot(){
        return todaysWeather.currentCondition.getWeatherCode().getCode() == WeatherCode.SUNNY.getCode() ||
                (todaysWeather.temperature.getTemp() >= MIN_HOT_TEMP || todaysWeather.temperature.getMaxTemp() >= MIN_HOT_TEMP);
    }

    private boolean isSunny(){
        return todaysWeather.currentCondition.getWeatherCode().getCode() == WeatherCode.SUNNY.getCode();
    }

    private boolean isCold(){
        return todaysWeather.currentCondition.getWeatherCode().getCode() == WeatherCode.COLD.getCode() ||
                (todaysWeather.temperature.getTemp() <= MAX_COLD_TEMP || todaysWeather.temperature.getMinTemp() <= MAX_COLD_TEMP);
    }

    private boolean isWindy(){
        return todaysWeather.currentCondition.getWeatherCode().getCode() == WeatherCode.WINDY.getCode() ||
                (todaysWeather.wind.getSpeed() >= minWindySpeed);
    }

    private boolean isDark(long length){
        return isDark(System.currentTimeMillis(), length);
    }

    private boolean isDark(long time, long length){
        return isAfterSunset(time) || isAfterSunset(time + length);
    }

    public static float celsiusToFahrenheit(float c) {
        return c * 9.0f/5.0f + 32;
    }

    public static float fahrenheitToCelsius(float f) {
        return (f - 32.0f) * 5.0f/9.0f;
    }
}