package de.apps4ics.mountainnavigation;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.location.Location;

/**
 * This file is part of MountainNavigation.
 *
 * MountainNavigation 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.
 *
 * MountainNavigation 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 MountainNavigation.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @copyright Copyright (c) 2016 Vinzenz Rosenkanz <vinzenz.rosenkranz@gmail.com>
 *
 * @author Vinzenz Rosenkranz <vinzenz.rosenkranz@gmail.com>
 */
public class WeatherDatabase extends SQLiteOpenHelper {
    private static final int DATABASE_VERSION = 1;
    private static final String DATABASE_NAME = "weathers.db";
    private static final String TABLE_WEATHER = "weather";
    private static final String TABLE_LOCATION = "locations";
    private static final String TABLE_WIND = "winds";

    private static final String KEY_ID = "_id";
    private static final String KEY_TIMESTAMP = "_timestamp";
    private static final String KEY_TEMP_MIN = "_temp_min";
    private static final String KEY_TEMP_MAX = "_temp_max";
    private static final String KEY_LOCATION_ID = "_loc_id";
    private static final String KEY_SUNRISE = "_sunrise";
    private static final String KEY_SUNSET = "_sunset";
    private static final String KEY_WEATHER_ID = "_weather_id";
    private static final String KEY_WEATHER_CODE = "_weather_code";
    private static final String KEY_PRESSURE = "_pressure";
    private static final String KEY_HUMIDITY = "_humidity";
    private static final String KEY_MAIN = "_main";
    private static final String KEY_DESC = "_desc";
    private static final String KEY_WIND_ID = "_wind_id";
    private static final String KEY_RAIN = "_rain"; //Rain volume for the last 3 hours
    private static final String KEY_SNOW = "_snow"; //Snow volume for the last 3 hours
    private static final String KEY_CLOUDS = "_clouds"; //cloudiness in %

    private static final String KEY_LAT = "_lat";
    private static final String KEY_LON = "_lon";
    private static final String KEY_NAME = "_name";
    private static final String KEY_COUNTRY = "_country";

    private static final String KEY_SPEED = "_speed";
    private static final String KEY_DIR = "_dir";

    private static final float MAX_WEATHER_DISTANCE = 5.0f; // max distance in km

    public WeatherDatabase(Context context){
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String CREATE_WEATHER_TABLE = "CREATE TABLE " + TABLE_WEATHER + "("
                + KEY_ID + " INTEGER PRIMARY KEY,"
                +  KEY_TIMESTAMP + " INTEGER,"
                +  KEY_TEMP_MIN + " REAL,"
                +  KEY_TEMP_MAX + " REAL,"
                +  KEY_LOCATION_ID + " INTEGER,"
                +  KEY_SUNRISE + " INTEGER,"
                +  KEY_SUNSET + " INTEGER,"
                +  KEY_WEATHER_ID + " INTEGER,"
                +  KEY_WEATHER_CODE + " INTEGER,"
                +  KEY_PRESSURE + " REAL,"
                +  KEY_HUMIDITY + " REAL,"
                +  KEY_MAIN + " TEXT,"
                +  KEY_DESC + " TEXT,"
                +  KEY_WIND_ID + " INTEGER,"
                +  KEY_RAIN + " REAL,"
                +  KEY_SNOW + " REAL,"
                +  KEY_CLOUDS + " INTEGER" + ")";
        db.execSQL(CREATE_WEATHER_TABLE);
        String CREATE_LOCATION_TABLE = "CREATE TABLE " + TABLE_LOCATION + "("
                + KEY_ID + " INTEGER PRIMARY KEY,"
                + KEY_LAT + " REAL,"
                + KEY_LON + " REAL,"
                + KEY_NAME + " TEXT,"
                + KEY_COUNTRY + " TEXT" + ")";
        db.execSQL(CREATE_LOCATION_TABLE);
        String CREATE_WIND_TABLE = "CREATE TABLE " + TABLE_WIND + "("
                + KEY_ID + " INTEGER PRIMARY KEY,"
                + KEY_SPEED + " REAL,"
                + KEY_DIR + " REAL" + ")";
        db.execSQL(CREATE_WIND_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //TODO?
    }

    private MyWeather addLocationToWeather(Cursor cursor, MyWeather weather) {
        Location l = new Location("");
        l.setLatitude(cursor.getDouble(cursor.getColumnIndex(KEY_LAT)));
        l.setLongitude(cursor.getDouble(cursor.getColumnIndex(KEY_LON)));
        weather.setCity(cursor.getString(cursor.getColumnIndex(KEY_NAME)));
        weather.setCountry(cursor.getString(cursor.getColumnIndex(KEY_COUNTRY)));
        weather.setLocation(l);
        return weather;
    }

    private MyWeather addWindToWeather(Cursor cursor, MyWeather weather) {
        weather.setWindSpeed(cursor.getFloat(cursor.getColumnIndex(KEY_SPEED)));
        weather.setWindDir(cursor.getFloat(cursor.getColumnIndex(KEY_DIR)));
        return weather;
    }

    private MyWeather addWeatherToWeather(Cursor cursor, MyWeather weather) {
        weather.setTimestamp(cursor.getLong(cursor.getColumnIndex(KEY_TIMESTAMP)));
        weather.setMinTemp(cursor.getFloat(cursor.getColumnIndex(KEY_TEMP_MIN)));
        weather.setMaxTemp(cursor.getFloat(cursor.getColumnIndex(KEY_TEMP_MAX)));
        weather.setSunrise(cursor.getLong(cursor.getColumnIndex(KEY_SUNRISE)));
        weather.setSunset(cursor.getLong(cursor.getColumnIndex(KEY_SUNSET)));
        weather.setWeatherId(cursor.getInt(cursor.getColumnIndex(KEY_WEATHER_ID)));
        weather.setWeatherCode(cursor.getInt(cursor.getColumnIndex(KEY_WEATHER_CODE)));
        weather.setPressure(cursor.getFloat(cursor.getColumnIndex(KEY_PRESSURE)));
        weather.setHumidity(cursor.getFloat(cursor.getColumnIndex(KEY_HUMIDITY)));
        //cursor.getInt(cursor.getColumnIndex(KEY_MAIN));
        //cursor.getInt(cursor.getColumnIndex(KEY_DESC));
        weather.setRain(cursor.getFloat(cursor.getColumnIndex(KEY_RAIN)));
        weather.setSnow(cursor.getFloat(cursor.getColumnIndex(KEY_SNOW)));
        weather.setClouds(cursor.getInt(cursor.getColumnIndex(KEY_CLOUDS)));
        return weather;
    }

    private long getId(Cursor cursor) {
        return cursor.getLong(cursor.getColumnIndex(KEY_ID));
    }

    private long getWindId(Cursor cursor) {
        return cursor.getLong(cursor.getColumnIndex(KEY_WIND_ID));
    }

    public long insertWeather(MyWeather weather) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues values = getLocationValues(weather);
        long locId = db.insert(TABLE_LOCATION, null, values);
        values = getWindValues(weather);
        long windId = db.insert(TABLE_WIND, null, values);
        values = getWeatherValues(weather);
        values.put(KEY_LOCATION_ID, locId);
        values.put(KEY_WIND_ID, windId);
        long rowId = db.insert(TABLE_WEATHER, null, values);
        db.close();
        return rowId;
    }

    public MyWeather getWeather(double lat, double lon, int offset) {
        SQLiteDatabase db = this.getReadableDatabase();
        Cursor locCursor = db.query(TABLE_LOCATION,
                new String[]{"*", "(6371 * acos(cos(radians(" + lat + ")) * cos(radians(" + KEY_LAT + ")) * cos(radians(" + KEY_LON + ") - radians(" + lon + ")) + sin(radians(" + lat + ")) * sin(radians(" + KEY_LAT + ")))) AS distance"},
                null,
                null,
                null,
                "distance < " + MAX_WEATHER_DISTANCE, //having
                "distance", //orderby
                "1"); //only get closest entry
        MyWeather weather = new MyWeather();
        if(locCursor == null) {
            return null;
        }
        locCursor.moveToFirst();
        long locId = getId(locCursor);
        weather = addLocationToWeather(locCursor, weather);
        locCursor.close();

        Cursor weatherCursor = db.query(TABLE_WEATHER,
                null,
                "WHERE " + KEY_LOCATION_ID + " =?",
                new String[]{String.valueOf(locId)}, null, null, null, "1");
        if(weatherCursor == null) {
            return null;
        }
        weatherCursor.moveToFirst();
        weather = addWeatherToWeather(weatherCursor, weather);
        long windId = getWindId(weatherCursor);
        weatherCursor.close();

        Cursor windCursor = db.query(TABLE_WIND,
                null,
                "WHERE " + KEY_ID + " =?",
                new String[]{String.valueOf(windId)}, null, null, null, "1");
        if(windCursor == null) {
            return null;
        }
        windCursor.moveToFirst();
        weather = addWindToWeather(windCursor, weather);
        windCursor.close();
        db.close();

        return weather;
    }

    private ContentValues getLocationValues(MyWeather weather){
        ContentValues values = new ContentValues();
        values.put(KEY_LAT, weather.getLocation().getLatitude());
        values.put(KEY_LON, weather.getLocation().getLongitude());
        values.put(KEY_NAME, weather.getCity());
        return values;
    }

    private ContentValues getWindValues(MyWeather weather){
        ContentValues values = new ContentValues();
        values.put(KEY_SPEED, weather.getWindSpeed());
        values.put(KEY_DIR, weather.getWindDir());
        return values;
    }

    private ContentValues getWeatherValues(MyWeather weather){
        ContentValues values = new ContentValues();
        values.put(KEY_TIMESTAMP, weather.getTimestamp());
        values.put(KEY_TEMP_MIN, weather.getMinTemp());
        values.put(KEY_TEMP_MAX, weather.getMaxTemp());
        values.put(KEY_SUNRISE, weather.getSunrise());
        values.put(KEY_SUNSET, weather.getSunset());
        values.put(KEY_WEATHER_ID, weather.getWeatherId());
        values.put(KEY_WEATHER_CODE, weather.getWeatherCode());
        values.put(KEY_PRESSURE, weather.getPressure());
        values.put(KEY_HUMIDITY, weather.getHumidity());
        values.put(KEY_MAIN, ""); //TODO what is main?
        values.put(KEY_DESC, ""); //TODO what is desc?
        values.put(KEY_RAIN, weather.getRain());
        values.put(KEY_SNOW, weather.getSnow());
        values.put(KEY_CLOUDS, weather.getClouds());
        return values;
    }
}
