package de.apps4ics.mountainnavigation;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Vibrator;
import android.provider.Settings;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.view.GravityCompat;
import android.support.v7.app.ActionBar;
import android.os.Bundle;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.support.v4.widget.DrawerLayout;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;

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.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 org.osmdroid.api.IMapController;
import org.osmdroid.bonuspack.overlays.MapEventsOverlay;
import org.osmdroid.bonuspack.overlays.MapEventsReceiver;
import org.osmdroid.bonuspack.overlays.Marker;
import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.MapView;
import org.osmdroid.views.overlay.Overlay;
import org.osmdroid.views.overlay.OverlayItem;

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

import de.apps4ics.mountainnavigation.pois.AddBreakpointDialog;
import de.apps4ics.mountainnavigation.pois.AddFountainDialog;
import de.apps4ics.mountainnavigation.pois.AddHutDialog;
import de.apps4ics.mountainnavigation.pois.AddPathDialog;
import de.apps4ics.mountainnavigation.pois.AddPeakDialog;
import de.apps4ics.mountainnavigation.pois.CellReception;
import de.apps4ics.mountainnavigation.pois.DbGeoPoint;
import de.apps4ics.mountainnavigation.pois.Fountain;
import de.apps4ics.mountainnavigation.pois.Image;
import de.apps4ics.mountainnavigation.pois.Lift;
import de.apps4ics.mountainnavigation.pois.Path;
import de.apps4ics.mountainnavigation.pois.Poi;
import de.apps4ics.mountainnavigation.pois.TrashBin;
import de.apps4ics.mountainnavigation.pois.Wifi;

public class MainActivity extends AppCompatActivity implements LocationListener {

    public static final String TAG = "MountainNavigation";
    /**
     * Fragment managing the behaviors, interactions and presentation of the navigation drawer.
     */

    private CharSequence mTitle;
    private CharSequence mDrawerTitle;
    private static final int GPS_MIN_TIME = 5000;
    private static final int GPS_MIN_DIST = 5;
    private static final String API_KEY = "fd4034defae557fd5f2fdaaf73c3402c";
    private static ConnectivityManager cm;
    private static TelephonyManager tm;
    private static WifiManager wm;
    private static NetworkInfo activeNetwork;
    private static int networkStrength;
    private static final int MAX_WIFI_LEVELS = 5;
    public static final long VIB_LENGTH = 50;

    private static SimpleDateFormat df_hm;
    private static SimpleDateFormat df_full;

    private Resources res;
    private FloatingActionButton fab;

    private Weather todaysWeather;
    private Weather tomorrowsWeather;
    private HistoricalWeather histWeather;
    private DayForecast todaysForecast;
    private DayForecast tomorrowsForecast;
    private WeatherClient weatherClient;
    private static Integer[] severeWeatherCodes;
    private static float minHotTemp;
    private static float maxColdTemp;
    private static float minWindySpeed;
    private boolean showWeatherHints;

    private DatabaseHandler dbHandler;

    private MapView mapView;
    private IMapController mapController;
    private int ZOOM_LEVEL;

    private ActionBarDrawerToggle drawerToggle;
    private String[] fountainSizes;
    private String[] pathOptions;
    private String[] pathDescs;
    private Integer[] pathOptionImgs;
    private String[] hutTypes;
    private String[] breakPointOptions;
    private Integer[] breakPointOptionsImgs;
    private String[] entries;
    private Integer[] entryImgs;
    private Integer[] fountainImgs;
    private DrawerLayout drawerLayout;
    private ListView listView;

    private List<Marker>[] poiMarkers;

    protected static ArrayList<OverlayItem> pathMarkers;

    private String provider;
    private Location mLocation;
    private LocationManager locationManager;

    private List<Location> foundLocations;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        res = getResources();
        pathMarkers = new ArrayList<>();

        cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
        activeNetwork = cm.getActiveNetworkInfo();
        networkStrength = 0;
        tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
        tm.listen(new MyPhoneStateListener(), PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
        wm = (WifiManager) getSystemService(WIFI_SERVICE);

        df_hm = new SimpleDateFormat(res.getString(R.string.date_format_hm));
        df_full = new SimpleDateFormat(res.getString(R.string.date_format_full));

        RelativeLayout relativeLayout = (RelativeLayout) findViewById(R.id.container);

        dbHandler = new DatabaseHandler(this);

        ZOOM_LEVEL = 18;
        mapView = new MapView(getApplicationContext(), 256);
        mapView.setTileSource(TileSourceFactory.MAPNIK);
        mapView.setBuiltInZoomControls(true);
        mapView.setMultiTouchControls(true);
        mapController = mapView.getController();
        mapController.setZoom(ZOOM_LEVEL);
        mapController.setCenter(new GeoPoint(48.52, 9.055));

        poiMarkers = new ArrayList[Types.SIZE];
        for(int i=0; i<poiMarkers.length; ++i){
            poiMarkers[i] = new ArrayList<>();
        }

        foundLocations = new ArrayList<>();

        relativeLayout.addView(mapView);

        mTitle = mDrawerTitle = getTitle();

        initGps();

        fountainSizes = res.getStringArray(R.array.fountain_size_dialog_options);
        fountainImgs = new Integer[]{
                R.drawable.fountain_size_small,
                R.drawable.fountain_size_medium,
                R.drawable.fountain_size_big,
        };
        pathDescs = res.getStringArray(R.array.path_dialog_descs);
        pathOptions = res.getStringArray(R.array.path_dialog_options);
        pathOptionImgs = new Integer[]{
                R.mipmap.path_exposed,
                R.mipmap.path_difficult_wet,
                R.mipmap.path_giddiness,
                R.mipmap.path_climbing
        };
        hutTypes = res.getStringArray(R.array.hut_dialog_types);
        breakPointOptions = res.getStringArray(R.array.break_point_dialog_options);
        breakPointOptionsImgs = new Integer[]{
                R.mipmap.ic_rock,
                R.mipmap.ic_bench,
                R.mipmap.ic_table,
                R.mipmap.ic_roofed
        };

        entries = res.getStringArray(R.array.toggleEntries);
        entryImgs  = new Integer[]{
                R.drawable.water,
                R.drawable.path,
                R.mipmap.ic_cabin,
                R.mipmap.path_exposed,
                R.drawable.break_point,
                R.drawable.trash,
                R.drawable.image_upload,
                R.drawable.cell_reception,
                R.drawable.wifi,
                R.drawable.lift
        };

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

        minWindySpeed = 8; //http://www.wettergefahren-fruehwarnung.de/Artikel/beaufort.html
        if(weatherConfig.unitSystem.equals(WeatherConfig.UNIT_SYSTEM.I)){
            minHotTemp = 86; //about 30 degree celsius
            maxColdTemp = 50; //about 10 degree celsius
        } else {
            minHotTemp = 30;
            maxColdTemp = 10;
        }

        showWeatherHints = true;

        //TODO split into severe weather and foggy, windy, sunny, ...
        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()
        };

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

        fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(mLocation == null){
                    InformDialog informDialog = new InformDialog(getString(R.string.inform_title), getString(R.string.inform_gps_pos_not_found));
                    informDialog.show(getFragmentManager(), "Inform Dialog");
                    return;
                }
                AddPoiDialog addPoiDialog = new AddPoiDialog();
                addPoiDialog.show(getFragmentManager(), "Add POI Dialog");
            }
        });
        drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        listView = (ListView) findViewById(R.id.navigation_drawer);

        drawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);

//        listView.setItemsCanFocus(false);
        ImageListAdapter adapter = new ImageListAdapter(MainActivity.this, entries, entryImgs);
        listView.setAdapter(adapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                TableLayout layout = (TableLayout) view;
                toggleDrawerItems(layout, position);
            }
        });

        drawerLayout.setDrawerListener(drawerToggle);

        double _lat = 48.52;
        double _lon = 9.055;
        double _alt = 350;
        long _time = System.currentTimeMillis() / 1000;
        DbGeoPoint dbgp = new DbGeoPoint(_lat, _lon, _alt, _time);
        long gp_id = dbHandler.addPoi(dbgp);
        dbgp.setId(gp_id);
        Fountain fountain = new Fountain(0, 2, dbgp);
        long f_id = dbHandler.addPoi(fountain);
        dbgp.setTypeId("fountain_" + f_id);
        dbHandler.updatePoi(dbgp);
    }

    private void toggleDrawerItems(TableLayout layout, int position){
        TableRow tr = (TableRow) layout.getChildAt(0); //TODO is tablerow?
        TextView tv = null;
        ImageView iv = null;
        for(int i=0; i<tr.getChildCount(); ++i){
            View v = tr.getChildAt(i);
            try {
                tv = (TextView) v;
            } catch(ClassCastException cce){
                //Log.e(TAG, "Could not cast " + v.getClass().toString() + " to TextView!");
            }
            try {
                iv = (ImageView) v;
            } catch(ClassCastException cce){
                //Log.e(TAG, "Could not cast " + v.getClass().toString() + " to ImageView!");
            }
        }
        if(tv == null || iv == null){
            Log.e(TAG, "Neither TextView nor ImageView can be null!");
            return;
        }
        int type = getType(position);
        if(iv.getTag() == null || iv.getTag() == "disabled"){
            iv.setColorFilter(null);
            iv.setTag("");
            tv.setTextColor(res.getColor(R.color.text_color_selected));
            Toaster("You enabled " + entries[position]);
            String title = entries[position];
            List<Poi> tempPoiList = dbHandler.getPoiByType(type);
            UpdatePoiIconsAsyncTask asyncTask = new UpdatePoiIconsAsyncTask(type, title);
            poiMarkers[type] = asyncTask.doInBackground(tempPoiList);
            for(int i=0; i<poiMarkers[type].size(); ++i){
                mapView.getOverlays().add(poiMarkers[type].get(i));
            }
            mapView.invalidate();
        } else {
            iv.setColorFilter(res.getColor(R.color.icon_unselected_gray));
            iv.setTag("disabled");
            tv.setTextColor(res.getColor(R.color.text_color));
            Toaster("You disabled " + entries[position]);
            for(int i=0; i<poiMarkers[type].size(); ++i){
                Overlay poi = poiMarkers[type].get(i);
                mapView.getOverlays().remove(poi);
            }
            poiMarkers[type] = new ArrayList<>();
            mapView.invalidate();
        }
    }

    private class UpdatePoiIconsAsyncTask extends AsyncTask<List<Poi>, Void, List<Marker>> {
        int type;
        String title;

        public UpdatePoiIconsAsyncTask(int type, String title){
            this.type = type;
            this.title = title;
        }

        @Override
        protected List<Marker> doInBackground(List<Poi>... params) {
            //TODO get weather and rate POIs (if sunny, smaller fountains, ...)
            List<Marker> result = new ArrayList<>();
            List<Poi> tempPoiList = params[0]; //This is only one list
            for(int i=0; i<tempPoiList.size(); ++i){
                Poi poi = tempPoiList.get(i);
                DbGeoPoint dbgp = poi.getGp();
                GeoPoint gp = new GeoPoint(dbgp.getLat(), dbgp.getLon(), dbgp.getAlt());
                String titleText = title;
                String snippetText = res.getString(R.string.osm_marker_snippet, gp.getLatitude(), gp.getLongitude(), gp.getAltitude(), df_full.format(new Date(dbgp.getTime())));
                String subDescText;

                switch(type){
                    case Types.FOUNTAIN:
                        title += " (" + fountainSizes[((Fountain) poi).getSize()] + ")";
                        break;
                    case Types.PATH: {
                        Path p = (Path) poi;
                        DbGeoPoint pathEnd = p.getEndGp();
                        titleText += " (" + p.getLength() + ")";
                        snippetText += "\n";
                        snippetText += "Exposed: " + ((p.getExposed() == 1) ? "Yes!" : "No!");
                        snippetText += "\n";
                        snippetText += "Difficult: " + ((p.getDiffWet() == 1) ? "Yes!" : "No!");
                        snippetText += "\n";
                        snippetText += "Giddiness: " + ((p.getGiddiness() == 1) ? "Yes!" : "No!");
                        snippetText += "\n";
                        snippetText += "Climbing: " + ((p.getClimbing() == 1) ? "Yes!" : "No!");
                        Marker positionMarker = new Marker(mapView);
                        positionMarker.setPosition(new GeoPoint(pathEnd.getLat(), pathEnd.getLon(), pathEnd.getAlt()));
                        positionMarker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
                        positionMarker.setTitle(titleText);
                        positionMarker.setSnippet(snippetText);
                        positionMarker.setImage(res.getDrawable(entryImgs[getPos(type)]));
                        Drawable[] iconLayer = new Drawable[2];
                        iconLayer[0] = res.getDrawable(R.mipmap.ic_poi);
                        BitmapDrawable bd = (BitmapDrawable) res.getDrawable(entryImgs[getPos(type)]);
                        if(bd == null) continue;
                        Bitmap b = bd.getBitmap();
                        Bitmap bResized = Bitmap.createScaledBitmap(b, b.getWidth()/2, b.getHeight()/2, false);
                        bd = new BitmapDrawable(res, bResized);
                        bd.setGravity(Gravity.CENTER_HORIZONTAL);
                        iconLayer[1] = bd;
                        LayerDrawable icon = new LayerDrawable(iconLayer);
                        positionMarker.setIcon(icon);
                        result.add(positionMarker);
                        poiMarkers[getPos(type)].add(positionMarker);
                        }
                        break;
                }

                Marker positionMarker = new Marker(mapView);
                positionMarker.setPosition(gp);
                positionMarker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
                positionMarker.setTitle(title);
                positionMarker.setSnippet(snippetText);
                positionMarker.setImage(res.getDrawable(entryImgs[getPos(type)]));
                Drawable[] iconLayer = new Drawable[2];
                iconLayer[0] = res.getDrawable(R.mipmap.ic_poi);
                BitmapDrawable bd = (BitmapDrawable) res.getDrawable(entryImgs[getPos(type)]);
                if(bd == null) continue;
                Bitmap b = bd.getBitmap();
                Bitmap bResized = Bitmap.createScaledBitmap(b, b.getWidth()/2, b.getHeight()/2, false);
                bd = new BitmapDrawable(res, bResized);
                bd.setGravity(Gravity.CENTER_HORIZONTAL);
                iconLayer[1] = bd;
                LayerDrawable icon = new LayerDrawable(iconLayer);
                positionMarker.setIcon(icon);
                result.add(positionMarker);
                poiMarkers[getPos(type)].add(positionMarker);
            }
            return result;
        }
    }

    private int getPos(int type){
        int pos;
        switch(type){
            case Types.FOUNTAIN:
                pos = 0;
                break;
            case Types.PATH:
                pos = 1;
                break;
            case Types.HUT:
                pos = 2;
                break;
            case Types.PEAK:
                pos = 3;
                break;
            case Types.BREAK_POINT:
                pos = 4;
                break;
            case Types.TRASH_BIN:
                pos = 5;
                break;
            case Types.PIC:
                pos = 6;
                break;
            case Types.CELL_RECEPTION:
                pos = 7;
                break;
            case Types.WIFI:
                pos = 8;
                break;
            case Types.LIFT:
                pos = 9;
                break;
            default:
                pos = 0;
                break;
        }
        return pos;
    }

    private int getType(int position){
        int type;
        switch (position){
            case 0:
                type = Types.FOUNTAIN;
                break;
            case 1:
                type = Types.PATH;
                break;
            case 2:
                type = Types.HUT;
                break;
            case 3:
                type = Types.PEAK;
                break;
            case 4:
                type = Types.BREAK_POINT;
                break;
            case 5:
                type = Types.TRASH_BIN;
                break;
            case 6:
                type = Types.PIC;
                break;
            case 7:
                type = Types.CELL_RECEPTION;
                break;
            case 8:
                type = Types.WIFI;
                break;
            case 9:
                type = Types.LIFT;
                break;
            case 10:
                type = Types.GP;
                break;
            default:
                type = 0;
                break;
        }
        return type;
    }

    private String getTypeName(int type){
        String name = "";
        switch(type){
            case Types.FOUNTAIN:
                name = "fountain";
                break;
            case Types.PATH:
                name = "path";
                break;
            case Types.HUT:
                name = "hut";
                break;
            case Types.PEAK:
                name = "peak";
                break;
            case Types.BREAK_POINT:
                name = "break_point";
                break;
            case Types.TRASH_BIN:
                name = "trash_bin";
                break;
            case Types.PIC:
                name = "image";
                break;
            case Types.CELL_RECEPTION:
                name = "cell_reception";
                break;
            case Types.WIFI:
                name = "wifi";
                break;
            case Types.LIFT:
                name = "lift";
                break;
        }
        return name;
    }

    private void initGps(){
        locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
        boolean isLocationEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        if(!isLocationEnabled){
            Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
            startActivity(intent);
        }
        Criteria criteria = new Criteria();
        provider = locationManager.getBestProvider(criteria, false);
        mLocation = locationManager.getLastKnownLocation(provider);
    }

    private void Toaster(String text){
        Toaster(text, false);
    }

    private void Toaster(String text, boolean longDuration){
        if(longDuration) Toast.makeText(getApplicationContext(), text, Toast.LENGTH_LONG).show();
        else Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();
    }

    public String[] getBreakPointOptions(){
        return breakPointOptions;
    }

    public Integer[] getBreakPointOptionsImgs(){
        return breakPointOptionsImgs;
    }

    public MapView getMapView(){
        return mapView;
    }

    public DatabaseHandler getDbHandler(){
        return dbHandler;
    }

    public ArrayList<OverlayItem> getPathMarkers(){
        return pathMarkers;
    }

    public void setPathMarkers(ArrayList<OverlayItem> al){
        pathMarkers = al;
    }

    public String[] getFountainSizes(){
        return fountainSizes;
    }

    public Integer[] getFountainImgs(){
        return fountainImgs;
    }

    public String[] getPathOptions(){
        return pathOptions;
    }
    public String[] getPathDescs(){
        return pathDescs;
    }

    public Integer[] getPathOptionImgs(){
        return pathOptionImgs;
    }

    private void setActiveNetwork(){
        activeNetwork = cm.getActiveNetworkInfo();
    }

    public static void setNetworkStrength(int strength){
        networkStrength = strength;
    }

    private void addSimpleMarker(GeoPoint gp){
        Marker positionMarker = new Marker(mapView);
        positionMarker.setPosition(gp);
        positionMarker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
        positionMarker.setTitle(getString(R.string.osm_marker_title));
        positionMarker.setSnippet("Latitude: " + gp.getLatitude() + "\n" + "Longitude: " + gp.getLongitude());
        mapView.getOverlays().add(positionMarker);
        mapView.invalidate();
    }

    @Override
    public void onLocationChanged(Location location) {
        mLocation = location;
        if(mLocation == null) return;
        foundLocations.add(location);

        double lat = location.getLatitude();
        double lon = location.getLongitude();
        GeoPoint gp = new GeoPoint(lat, lon);

        addSimpleMarker(gp);

        mapController.setCenter(gp);

        //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(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,
                        df_hm.format(new Date(todaysWeather.location.getSunrise() * 1000)),
                        df_hm.format(new Date(todaysWeather.location.getSunset() * 1000)),
                        todaysWeather.currentCondition.getWeatherCode().getCode(),
                        todaysWeather.currentCondition.getWeatherId());
                //InformDialog informWeatherDialog = new InformDialog(title, msg);
                //informWeatherDialog.show(getFragmentManager(), "Inform Dialog");
                boolean isSevereWeather = isSevereWeather(todaysWeather.currentCondition.getWeatherCode().getCode());
                Log.d(TAG, title + ": " + msg);
                Log.d(TAG, "isSevereWeather? " + isSevereWeather);
                //TODO add more hints
                if (showWeatherHints) {
                    List<String> titles = new ArrayList<>();
                    List<String> descs = new ArrayList<>();
                    if (isSunny()) {
                        titles.add("Sunny");
                        descs.add("The weather might be sunny. You should take some suncream with you!");
                    }
                    if (isHot()) {
                        titles.add("Hot");
                        descs.add("It is hot outside. Remember to take enough to drink with you.");
                    }
                    if (isCold()) {
                        titles.add("Cold");
                        descs.add("The weather might be cold. You should take some warm clothes with you!");
                    }
                    if (isWindy()) {
                        titles.add("Windy");
                        descs.add("The weather might be windy. You should take some windproof clothes with you!");
                    }
                    if (isAfterSunset()) {
                        titles.add("Sunset");
                        descs.add("It is already after sunset. Better take a head lamp with you.");
                    }
                    if (titles.size() > 0 && descs.size() > 0) {
                        String[] titleArr = new String[titles.size()];
                        String[] descArr = new String[descs.size()];
                        titleArr = titles.toArray(titleArr);
                        descArr = descs.toArray(descArr);
                        WarningListAdapter warningAdapter = new WarningListAdapter(MainActivity.this, titleArr, descArr);
                        InformListDialog weatherHintDialog = new InformListDialog("Hints", warningAdapter);
                        weatherHintDialog.show(getFragmentManager(), "Warning Hint Dialog");
                    }
                }
                if (isSevereWeather) {
                    String warnMsg = "The weather around you in " + todaysWeather.location.getCity() + " is '" + todaysWeather.currentCondition.getWeatherCode().getLabel(getApplicationContext()) + "'!";
                    InformDialog informAlertDialog = new InformDialog("Weather Warning!", warnMsg);
                    informAlertDialog.show(getFragmentManager(), "Inform Dialog - Weather Warning");
                }
            }

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

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

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        Log.d(TAG, "onStatusChanged: " + provider + ", " + status);
    }

    @Override
    public void onProviderEnabled(String provider) {
        Log.d(TAG, "onProviderEnabled: " + provider);
    }

    @Override
    public void onProviderDisabled(String provider) {
        Log.d(TAG, "onProviderDisabled: " + provider);
    }

    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() >= minHotTemp || todaysWeather.temperature.getMaxTemp() >= minHotTemp);
    }

    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() <= maxColdTemp || todaysWeather.temperature.getMinTemp() <= maxColdTemp);
    }

    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);
    }

    private HistoricalWeather getWeather(float lat, float lon, long start, long end){
        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(TAG, "Weather error - parsing data");
                wle.printStackTrace();
            }

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

    public class InformDialog extends DialogFragment {
        private String title;
        private String msg;

        public InformDialog(String title, String msg){
            this.title = title;
            this.msg = msg;
        }

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
            builder.setTitle(title)
                    .setMessage(msg)
                    .setCancelable(false)
                    //.setIcon(res.getDrawable(R.mipmap.ic_info_icon))
                    .setPositiveButton(R.string.inform_positive_button, null);
            return builder.create();
        }
    }

    public class InformListDialog extends DialogFragment {
        private String title;
        private ListAdapter adapter;

        public InformListDialog(String title, ListAdapter adapter){
            this.title = title;
            this.adapter = adapter;
        }

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
            builder.setTitle(title)
                    .setAdapter(adapter, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            //nothing to do...
                        }
                    })
                    .setCancelable(false)
                            //.setIcon(res.getDrawable(R.mipmap.ic_info_icon))
                    .setPositiveButton(R.string.inform_positive_button, null);
            return builder.create();
        }
    }

    public class AddPoiDialog extends DialogFragment {
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
            builder.setTitle(R.string.add_poi_dialog_title)
                    //.setIcon(res.getDrawable(R.mipmap.ic_add_poi))
                    .setAdapter(new ImageListAdapter(MainActivity.this, entries, entryImgs), new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            int type = getType(which);
                            switch (type) {
                                case Types.FOUNTAIN:
                                    addFountain();
                                    break;
                                case Types.PATH:
                                    addPathMap();
                                    break;
                                case Types.HUT:
                                    addHut();
                                    break;
                                case Types.PEAK:
                                    addPeak();
                                    break;
                                case Types.BREAK_POINT:
                                    addBreakpoint();
                                    break;
                                case Types.TRASH_BIN:
                                    addPoi(new TrashBin());
                                    break;
                                case Types.PIC:
                                    //TODO add picture only to existing POI
                                    addPoi(new Image());
                                    break;
                                case Types.CELL_RECEPTION:
                                    addCellReceptionPoi();
                                    break;
                                case Types.WIFI:
                                    addWifiPoi();
                                    break;
                                case Types.LIFT:
                                    //TODO as in path, choose two markers
                                    addPoi(new Lift());
                                    break;
                            }
                        }
                    });
            return builder.create();
        }
    }

    public void addFountain(){
        AddFountainDialog addFountainDialog = new AddFountainDialog(this);
        addFountainDialog.show(getFragmentManager(), "Add fountain POI Dialog");
    }

    public void addBreakpoint(){
        AddBreakpointDialog addBreakpointDialog = new AddBreakpointDialog(this);
        addBreakpointDialog.show(getFragmentManager(), "Add Break point POI Dialog");
    }

    public void addHut(){
        AddHutDialog addHutDialog = new AddHutDialog(this);
        addHutDialog.show(getFragmentManager(), "Add Hut POI Dialog");
    }

    public void addPeak(){
        AddPeakDialog addPeakDialog = new AddPeakDialog(this);
        addPeakDialog.show(getFragmentManager(), "Add Peak POI Dialog");
    }

    public void addPathMap() {
        final MapView popupMapView = new MapView(getApplicationContext(), 256);
        popupMapView.setTileSource(TileSourceFactory.MAPNIK);
        popupMapView.setBuiltInZoomControls(false);
        popupMapView.setMultiTouchControls(true);
        IMapController popupMapController = popupMapView.getController();
        popupMapController.setZoom(ZOOM_LEVEL);
        popupMapController.setCenter(new GeoPoint(mLocation.getLatitude(), mLocation.getLongitude()));

        List<OverlayItem> overlayItemList = new ArrayList<>();
        for(Location l : foundLocations){
            OverlayItem oI = new OverlayItem(String.valueOf(l.getTime()), null, null, new GeoPoint(l.getLatitude(), l.getLongitude(), l.getAltitude()));
            oI.setMarker(res.getDrawable(R.mipmap.ic_poi_red));
            overlayItemList.add(oI);
        }

        List<Poi> poiMarker = dbHandler.getAllPois();
        for(Poi p : poiMarker){
            DbGeoPoint dbgp = p.getGp();
            GeoPoint gp = new GeoPoint(dbgp.getLat(), dbgp.getLon(), dbgp.getAlt());
            OverlayItem oI = new OverlayItem(String.valueOf(dbgp.getTime()) + "_" + String.valueOf(dbgp.getId()), String.valueOf(p.getId()), String.valueOf(p.getType()), gp);
            Drawable[] iconLayer = new Drawable[2];
            iconLayer[0] = res.getDrawable(R.mipmap.ic_poi);
            BitmapDrawable bd = (BitmapDrawable) res.getDrawable(entryImgs[getPos(p.getType())]);
            if(bd == null) continue;
            Bitmap b = bd.getBitmap();
            Bitmap bResized = Bitmap.createScaledBitmap(b, b.getWidth()/2, b.getHeight()/2, false);
            bd = new BitmapDrawable(res, bResized);
            bd.setGravity(Gravity.CENTER_HORIZONTAL);
            iconLayer[1] = bd;
            LayerDrawable icon = new LayerDrawable(iconLayer);
            oI.setMarker(icon);
            overlayItemList.add(oI);
        }

        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setView(popupMapView)
                .setTitle(R.string.path_map_dialog_title)
                .setPositiveButton(R.string.continue_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        AddPathDialog addPathDialog = new AddPathDialog(MainActivity.this);
                        addPathDialog.show(getFragmentManager(), "Add Path POI Dialog");
                    }
                })
                .setNegativeButton(R.string.cancel_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                    }
                });
        AlertDialog pathMapDialog = builder.create();
        pathMapDialog.show();
        pathMapDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
        final MapDialogItemizedIconOverlay overlay = new MapDialogItemizedIconOverlay(this, overlayItemList, pathMapDialog, popupMapView);

        MapEventsReceiver eventsReceiver = new MapEventsReceiver() {
            @Override
            public boolean singleTapConfirmedHelper(GeoPoint geoPoint) {
                return false;
            }

            @Override
            public boolean longPressHelper(GeoPoint geoPoint) {
                Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
                v.vibrate(VIB_LENGTH);
                OverlayItem oI = new OverlayItem("", null, null, geoPoint);
                oI.setMarker(getApplicationContext().getResources().getDrawable(R.mipmap.ic_poi_red));
                overlay.addItem(oI);
                popupMapView.invalidate();
                return true;
            }
        };

        popupMapView.getOverlays().add(new MapEventsOverlay(getApplicationContext(), eventsReceiver));
        popupMapView.getOverlays().add(overlay);
    }

    private void addCellReceptionPoi(){
        setActiveNetwork();
        if(activeNetwork != null && activeNetwork.isConnected()){
            if(tm != null && networkStrength != -1){
                String opName = tm.getNetworkOperatorName();
                String opCountry = tm.getNetworkCountryIso();
                String nwType = activeNetwork.getSubtypeName();
                int roaming = (tm.isNetworkRoaming()) ? 1 : 0;
                int strength = networkStrength;
                addPoi(new CellReception(0, strength, opName, opCountry, roaming, nwType, null));
            } else {
                Log.d(TAG, "You are not connected to a phone network");
            }
        } else {
            Log.d(TAG, "You are not connected to any network");
        }
    }

    private void addWifiPoi(){
        if(!wm.isWifiEnabled()){
            AlertDialog.Builder wifiIntentDialogBuilder = new AlertDialog.Builder(MainActivity.this);
            wifiIntentDialogBuilder
                    .setTitle(R.string.wifi_disabled_title)
                    .setMessage(R.string.wifi_disabled_text)
                    .setPositiveButton(R.string.enable_wifi_button, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS);
                            startActivity(intent);
                        }
                    })
                    .setNegativeButton(R.string.dont_enable_wifi_button, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                        }
                    });
            AlertDialog wifiIntentDialog = wifiIntentDialogBuilder.create();
            wifiIntentDialog.show();
        } else {
            setActiveNetwork();
            if (activeNetwork != null && activeNetwork.isConnected()) {
                if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
                    String ssid = wm.getConnectionInfo().getSSID();
                    int rssi = wm.getConnectionInfo().getRssi();
                    int levels = WifiManager.calculateSignalLevel(rssi, MAX_WIFI_LEVELS);
                    Log.d(TAG, "You are currently connected to '" + ssid + "' with " + levels + " (" + rssi + ")");
                    addPoi(new Wifi(0, ssid, levels, null));
                } else {
                    Log.d(TAG, "Wifi network not available/connected");
                }
            } else {
                Log.d(TAG, "You are not connected to any network");
            }
        }
    }

    public long addGp(DbGeoPoint dbgp){
        return dbHandler.addPoi(dbgp);
    }

    public long addPoi(Poi poi){
        DbGeoPoint dbgp = poi.getGp();
        if(dbgp == null){
            double _lat = mLocation.getLatitude();
            double _lon = mLocation.getLongitude();
            double _alt = mLocation.getAltitude();
            long _time = System.currentTimeMillis() / 1000;

            dbgp = new DbGeoPoint(_lat, _lon, _alt, _time);
            long gp_id = dbHandler.addPoi(dbgp);
            dbgp.setId(gp_id);

            poi.setGp(dbgp);
        }
        long rowId = dbHandler.addPoi(poi);
        if(rowId >= 0) Toaster(getString(R.string.add_poi_success), true);
        dbgp.setTypeId(getTypeName(poi.getType()) + "_" + rowId);
        dbHandler.updatePoi(dbgp);

        if(poi.getType() == Types.PATH){
            Path path = (Path) poi;
            path.getEndGp().setTypeId(getTypeName(poi.getType()) + "_" + rowId);
            dbHandler.updatePoi(path.getEndGp());
        }/* else if(poi.getType() == Types.LIFT){ //TODO implement addLiftPoi method
            Lift lift = (Lift) poi;
            lift.getEndGp().setTypeId(getTypeName(poi.getType()) + "_" + rowId);
            dbHandler.updatePoi(lift.getEndGp());
        }*/

        return rowId;
    }

    private class WarningListAdapter extends ArrayAdapter<String> {

        private final Activity context;
        private final String[] texts;
        private final String[] descs;

        public WarningListAdapter(Activity context, String[] texts, String[] descs) {
            super(context, R.layout.warning_list_item, texts);
            this.context = context;
            this.texts = texts;
            this.descs = descs;
        }

        @Override
        public View getView(int position, View view, ViewGroup parent) {
            LayoutInflater inflater = context.getLayoutInflater();
            View rowView= inflater.inflate(R.layout.warning_list_item, null, true);
            TextView txtTitle = (TextView) rowView.findViewById(R.id.warning_text_title);

            TextView txtDesc = (TextView) rowView.findViewById(R.id.warning_text_desc);
            txtTitle.setText(texts[position]);
            txtDesc.setText(descs[position]);
            return rowView;
        }
    }

    @Override
    public void setTitle(CharSequence title){
        mTitle = title;
        ActionBar actionBar = getSupportActionBar();
        if(actionBar != null) actionBar.setTitle(mTitle);
    }

    @Override
    protected void onStart() {
        super.onStart();
    }

    @Override
    protected void onResume() {
        super.onResume();
        locationManager.requestLocationUpdates(provider, GPS_MIN_TIME, GPS_MIN_DIST, this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        locationManager.removeUpdates(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
}
