package com.smartdevicelink.managers.lockscreen;

import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

import com.smartdevicelink.util.AndroidTools;
import com.smartdevicelink.util.DebugTool;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * <strong>LockScreenDeviceIconManager</strong> <br>
 *
 * The LockScreenDeviceIconManager handles the logic of caching and retrieving cached lock screen icons <br>
 *
 */
class LockScreenDeviceIconManager {

    private Context context;
    private static final String SDL_DEVICE_STATUS_SHARED_PREFS = "sdl.lockScreenIcon";
    private static final String STORED_ICON_DIRECTORY_PATH = "sdl/lock_screen_icon/";

    interface OnIconRetrievedListener {
        void onImageRetrieved(Bitmap icon);
        void onError(String info);
    }

    LockScreenDeviceIconManager(Context context) {
        this.context = context;
        File lockScreenDirectory = new File(context.getCacheDir(), STORED_ICON_DIRECTORY_PATH);
        lockScreenDirectory.mkdirs();
    }

    /**
     * Will try to return a lock screen icon either from cache or downloaded
     * if it fails iconRetrievedListener.OnError will be called with corresponding error message
     * @param iconURL url that the lock screen icon is downloaded from
     * @param iconRetrievedListener an interface that will implement onIconReceived and OnError methods
     */
    void retrieveIcon(String iconURL, OnIconRetrievedListener iconRetrievedListener) {
        Bitmap icon = null;
        try {
            if (isIconCachedAndValid(iconURL)) {
                DebugTool.logInfo("Icon Is Up To Date");
                icon = getFileFromCache(iconURL);
                if (icon == null) {
                    DebugTool.logInfo("Icon from cache was null, attempting to re-download");
                    icon = AndroidTools.downloadImage(iconURL);
                    if (icon != null) {
                        saveFileToCache(icon, iconURL);
                    } else {
                        iconRetrievedListener.onError("Icon downloaded was null");
                        return;
                    }
                }
                iconRetrievedListener.onImageRetrieved(icon);
            } else {
                // The icon is unknown or expired. Download the image, save it to the cache, and update the archive file
                DebugTool.logInfo("Lock Screen Icon Update Needed");
                icon = AndroidTools.downloadImage(iconURL);
                if (icon != null) {
                    saveFileToCache(icon, iconURL);
                    iconRetrievedListener.onImageRetrieved(icon);
                } else {
                    iconRetrievedListener.onError("Icon downloaded was null");
                }
            }
        } catch (IOException e) {
            iconRetrievedListener.onError("device Icon Error Downloading, Will attempt to grab cached Icon even if expired: \n" + e.toString());
            icon = getFileFromCache(iconURL);
            if (icon != null) {
                iconRetrievedListener.onImageRetrieved(icon);
            } else {
                iconRetrievedListener.onError("Unable to retrieve icon from cache");
            }
        }
    }

    /**
     * Will decide if a cached icon is available and up to date
     * @param iconUrl url will be hashed and used to look up last updated timestamp in shared preferences
     * @return True when icon details are in shared preferences and less than 30 days old, False if icon details are too old or not found
     */
    private boolean isIconCachedAndValid(String iconUrl) {
        String iconHash = getMD5HashFromIconUrl(iconUrl);
        SharedPreferences sharedPref = this.context.getSharedPreferences(SDL_DEVICE_STATUS_SHARED_PREFS, Context.MODE_PRIVATE);
        String iconLastUpdatedTime = sharedPref.getString(iconHash, null);
        if(iconLastUpdatedTime == null) {
            DebugTool.logInfo("No Icon Details Found In Shared Preferences");
            return false;
        } else {
            DebugTool.logInfo("Icon Details Found");
            long lastUpdatedTime = 0;
            try {
                lastUpdatedTime = Long.parseLong(iconLastUpdatedTime);
            } catch (NumberFormatException e) {
                DebugTool.logInfo("Invalid time stamp stored to shared preferences, clearing cache and share preferences");
                clearIconDirectory();
                sharedPref.edit().clear().commit();
            }
            long currentTime = System.currentTimeMillis();

            long timeDifference = currentTime - lastUpdatedTime;
            long daysBetweenLastUpdate = timeDifference / (1000 * 60 * 60 * 24);
            return daysBetweenLastUpdate < 30;
        }
    }

    /**
     * Will try to save icon to cache
     * @param icon the icon bitmap that should be saved to cache
     * @param iconUrl the url where the icon was retrieved will be hashed and used for file and file details lookup
     */
    private void saveFileToCache(Bitmap icon, String iconUrl) {
        String iconHash = getMD5HashFromIconUrl(iconUrl);
        File f = new File(this.context.getCacheDir() + "/" + STORED_ICON_DIRECTORY_PATH, iconHash);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        icon.compress(Bitmap.CompressFormat.PNG, 0 /*ignored for PNG*/, bos);
        byte[] bitmapData = bos.toByteArray();

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(f);
            fos.write(bitmapData);
            fos.flush();
            fos.close();
            writeDeviceIconParametersToSharedPreferences(iconHash);
        } catch (Exception e) {
            DebugTool.logError("Failed to save icon to cache");
            e.printStackTrace();
        }
    }

    /**
     * Will try to retrieve icon bitmap from cached directory
     * @param iconUrl the url where the icon was retrieved will be hashed and used to look up file location
     * @return bitmap of device icon or null if it fails to find the icon or read from shared preferences
     */
    private Bitmap getFileFromCache(String iconUrl) {
        String iconHash = getMD5HashFromIconUrl(iconUrl);
        SharedPreferences sharedPref = this.context.getSharedPreferences(SDL_DEVICE_STATUS_SHARED_PREFS, Context.MODE_PRIVATE);
        String iconLastUpdatedTime = sharedPref.getString(iconHash, null);

        if (iconLastUpdatedTime != null) {
            Bitmap cachedIcon = BitmapFactory.decodeFile(this.context.getCacheDir() + "/" + STORED_ICON_DIRECTORY_PATH + "/" + iconHash);
            if(cachedIcon == null) {
                DebugTool.logError("Failed to get Bitmap from decoding file cache");
                clearIconDirectory();
                sharedPref.edit().clear().commit();
                return null;
            } else {
                return cachedIcon;
            }
        } else {
            DebugTool.logError("Failed to get shared preferences");
            return null;
        }
    }

    /**
     * Will write information about the icon to shared preferences
     * icon information will have a look up key of the hashed icon url and the current timestamp to indicated when the icon was last updated.
     * @param iconHash the url where the icon was retrieved will be hashed and used lookup key
     */
    private void writeDeviceIconParametersToSharedPreferences(String iconHash) {
        SharedPreferences sharedPref = this.context.getSharedPreferences(SDL_DEVICE_STATUS_SHARED_PREFS, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPref.edit();
        editor.putString(iconHash, String.valueOf(System.currentTimeMillis()));
        editor.commit();
    }

    /**
     * Create an MD5 hash of the icon url for file storage and lookup/shared preferences look up
     * @param iconUrl the url where the icon was retrieved
     * @return MD5 hash of the icon URL
     */
    private String getMD5HashFromIconUrl(String iconUrl) {
        String iconHash = null;
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] messageDigest = md.digest(iconUrl.getBytes());
            BigInteger no = new BigInteger(1, messageDigest);
            String hashtext = no.toString(16);
            while (hashtext.length() < 32) {
                hashtext = "0" + hashtext;
            }
            iconHash = hashtext;
        } catch (NoSuchAlgorithmException e) {
            DebugTool.logError("Unable to hash icon url");
            e.printStackTrace();
        }
        return iconHash;
    }

    /**
     * Clears all files in the directory where lock screen icons are cached
     */
    private void clearIconDirectory() {
        File iconDir = new File(context.getCacheDir() + "/" + STORED_ICON_DIRECTORY_PATH);
        if (iconDir.listFiles() != null) {
            for (File child : iconDir.listFiles()) {
                child.delete();
            }
        }
    }
}