package com.mapbox.mapboxsdk.offline;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils;
import android.util.Log;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.exceptions.OfflineDatabaseException;
import java.util.Date;

public class OfflineMapDatabase implements MapboxConstants {

    private static final String TAG = "OfflineMapDatabase";

    private Context context;

    private SQLiteDatabase db;

    private String uniqueID;
    private String mapID;
    private boolean includesMetadata;
    private boolean includesMarkers;
    private RasterImageQuality imageQuality;
    private String path;
    private boolean invalid;
    private boolean initializedProperly = false;

    /**
     * Default Constructor
     *
     * @param context Context of Android app
     */
    public OfflineMapDatabase(Context context) {
        super();
        this.context = context;
    }

    /**
     * Constructor
     * @param context Context of Android app
     * @param mapID MapId
     */
    public OfflineMapDatabase(Context context, String mapID) {
        super();
        this.context = context;
        this.mapID = mapID;
    }

    public String getUniqueID() {
        return uniqueID;
    }

    public String getMapID() {
        return mapID;
    }

    public String getPath() {
        return path;
    }

    public RasterImageQuality getImageQuality() {
        return imageQuality;
    }

    public boolean initializeDatabase() {

        String uniqueID = sqliteMetadataForName("uniqueID");
        String mapID = sqliteMetadataForName("mapID");
        String includesMetadata = sqliteMetadataForName("includesMetadata");
        String includesMarkers = sqliteMetadataForName("includesMarkers");
        String imageQuality = sqliteMetadataForName("imageQuality");

        if (TextUtils.isEmpty(uniqueID)) {
            uniqueID = String.format(MAPBOX_LOCALE, "%s-%d", mapID, new Date().getTime() / 1000L);
        }

        if (!TextUtils.isEmpty(mapID) && !TextUtils.isEmpty(includesMetadata) && !TextUtils.isEmpty(includesMarkers) && !TextUtils.isEmpty(imageQuality)) {
            // Reaching this point means that the specified database file at path pointed to an sqlite file which had
            // all the required values in its metadata table. That means the file passed the test for being a valid
            // offline map database.
            //
            this.uniqueID = uniqueID;
            this.mapID = mapID;
            this.includesMetadata = "YES".equalsIgnoreCase(includesMetadata);
            this.includesMarkers = "YES".equalsIgnoreCase(includesMarkers);

            this.imageQuality = RasterImageQuality.getEnumForValue(Integer.parseInt(imageQuality));

            SQLiteDatabase db = database();
            this.path = db.getPath();

            this.initializedProperly = true;
        } else {
            // Reaching this point means the file at path isn't a valid offline map database, so we can't use it.
            Log.w(TAG, "Invalid offline map database.  Can't be used.");
        }
        return initializedProperly;
    }

    public byte[] dataForURL(String url) throws OfflineDatabaseException {
        byte[] data = sqliteDataForURL(url);
/*
        if (data == null || data.length == 0) {
            String reason = String.format("The offline database has no data for %s", url);
            throw new OfflineDatabaseException(reason);
        }
*/
        return data;
    }

    public void invalidate() {
        this.invalid = false;
    }

    public String sqliteMetadataForName(String name) {
        if (mapID == null) {
            return null;
        }

        SQLiteDatabase db = database();
        if (db == null) {
            return null;
        }

        String query = "SELECT " + OfflineDatabaseHandler.FIELD_METADATA_VALUE + " FROM " + OfflineDatabaseHandler.TABLE_METADATA + " WHERE " + OfflineDatabaseHandler.FIELD_METADATA_NAME + "=?;";
        String[] selectionArgs = new String[] { name };
        Cursor cursor = db.rawQuery(query, selectionArgs);
        if (cursor == null) {
            return null;
        }

        String res = null;
        if (cursor.moveToFirst()) {
            res = cursor.getString(cursor.getColumnIndex(OfflineDatabaseHandler.FIELD_METADATA_VALUE));
        }
        cursor.close();
        return res;
    }

    public byte[] sqliteDataForURL(String url) {
        if (mapID == null) {
            return null;
        }
        SQLiteDatabase db = database();
        if (db == null) {
            return null;
        }

        String query = "SELECT " + OfflineDatabaseHandler.FIELD_RESOURCES_DATA + " FROM " + OfflineDatabaseHandler.TABLE_RESOURCES + " WHERE " + OfflineDatabaseHandler.FIELD_RESOURCES_URL + "=?;";
        String[] selectionArgs = new String[] { url };
        Cursor cursor = db.rawQuery(query, selectionArgs);
        if (cursor == null) {
            return null;
        }

        byte[] res = null;
        if (cursor.moveToFirst()) {
            res = cursor.getBlob(cursor.getColumnIndex(OfflineDatabaseHandler.FIELD_RESOURCES_DATA));
        }
        cursor.close();
        return res;
    }

    private SQLiteDatabase database() {
        if (db == null) {
            db = OfflineDatabaseManager.getOfflineDatabaseManager(context).getOfflineDatabaseHandlerForMapId(mapID).getReadableDatabase();
        }
        if (!db.isOpen()) {
            db = null;
        }
        return db;
    }

    public void closeDatabase() {
        if (db != null && db.isOpen()) {
            db.close();
        }
        db = null;
    }
}