package com.shahul3d.indiasatelliteweather.service;

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.widget.Toast;

import com.crashlytics.android.Crashlytics;
import com.noveogroup.android.log.Log;
import com.shahul3d.indiasatelliteweather.R;
import com.shahul3d.indiasatelliteweather.controllers.WeatherApplication;
import com.shahul3d.indiasatelliteweather.data.AppConstants;
import com.shahul3d.indiasatelliteweather.helpers.MAPDownloadBroadcastHelper;
import com.shahul3d.indiasatelliteweather.utils.CrashUtils;
import com.shahul3d.indiasatelliteweather.utils.HttpClient;
import com.shahul3d.indiasatelliteweather.utils.PreferenceUtil;
import com.shahul3d.indiasatelliteweather.utils.StorageUtils;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Response;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;

import de.greenrobot.event.EventBus;

public class MAPDownloadController {
    MAPDownloadBroadcastHelper mapDownloadBroadcastHelper;
    Boolean activeDownloadsList[] = new Boolean[AppConstants.MAP_URL.size()];
    Context context = WeatherApplication.getContext();
    private SharedPreferences preference_General = null;

    public MAPDownloadController(EventBus eventBus) {
        mapDownloadBroadcastHelper = new MAPDownloadBroadcastHelper(eventBus);
        preference_General = WeatherApplication.getContext().getSharedPreferences("BackgroundPreference", Activity.MODE_PRIVATE);
    }

    public void downloadMAPRequest(int mapID, int mapType) {
        Log.d("downloadMAPRequest called");
        if (!performDuplicateDownloadCheck(mapID)) {
            Log.e("Duplicate download request for the same map type");
            return;
        }
        String mapFileName = AppConstants.getMapType(mapID, mapType);
        Log.d("Download initiated for fileName:" + mapFileName + "  mapType:" + mapType + " mapID:" + mapID);

        //Marking current map type as in progress.
        activeDownloadsList[mapID] = true;

        //Notifying UI about download is starting, so start rotating the loader..
        mapDownloadBroadcastHelper.broadcastDownloadProgress(mapType, mapID, 0);

        //Downloading the map.
        String URL = AppConstants.getMapURL(mapFileName);
        final HashMap<String, Object> downloadResult = downloadMap(URL, mapID, mapType);
        if (downloadResult == null || downloadResult.get("map") == null) {
            //Download failed
            Log.e("Download Failed:" + mapFileName + "  mapType:" + mapType + " mapID:" + mapID);
            markDownloadComplete(mapType, mapID, false);
            return;
        }
        Log.a("Download Success:" + mapFileName + "  mapType:" + mapType + " mapID:" + mapID);
        Bitmap downloadedMAP = (Bitmap) downloadResult.get("map");
        String lastModifiedHeader = (String) downloadResult.get("lost_modified");

        //Trimming the borders of some map types that are not meaningful to the user.
        mapDownloadBroadcastHelper.broadcastDownloadProgress(mapType, mapID, 92);
        downloadedMAP = removeMapBorders(mapFileName, downloadedMAP);
        mapDownloadBroadcastHelper.broadcastDownloadProgress(mapType, mapID, 95);

        //Save downloaded image for offline use.
        try {
            saveDownloadedMap(mapFileName, downloadedMAP);
        } catch (Exception e) {
            //Store Map failed
            Log.e("Storing MAPS failed:" + mapFileName + "  mapType:" + mapType + " mapID:" + mapID);
            CrashUtils.trackException("Error while storing map on DISK", e);
            markDownloadComplete(mapType, mapID, false);
            return;
        }
        //Download & Everything successful.
        Log.d("Storing MAPS success:" + mapFileName + "  mapType:" + mapType + " mapID:" + mapID);
        mapDownloadBroadcastHelper.broadcastDownloadProgress(mapType, mapID, 100);

        PreferenceUtil.updateLastModifiedTime(mapFileName, lastModifiedHeader);
        markDownloadComplete(mapType, mapID, true);

        //check for .nomeida file and create it if it is not available.
        StorageUtils.createNoMediaFile();
    }

    public boolean performDuplicateDownloadCheck(int mapID) {
        if (activeDownloadsList[mapID] != null && activeDownloadsList[mapID]) {
            return false;
        }
        return true;
    }

    public HashMap<String, Object> downloadMap(String URL, int mapID, int mapType) {
        Bitmap downloadedBitmap = null;
        String lastModifiedHeader = "";
        try {
            //Invoking Http call to download the URL.
            //Calling through synchronous mode, since async wont suits on our requirement.
            OkHttpClient httpClient = HttpClient.getInstance();
            Call call = httpClient.newCall(HttpClient.generateRequest(URL));
            Response response = call.execute();

            //These caching headers should be stored on preferences.
            lastModifiedHeader = response.header("Last-Modified", "");
            Log.d("last modified: " + lastModifiedHeader);

//            Log.d("\nN/W counts: " + httpClient.getCache().getNetworkCount() + "\nReq Counts: " + httpClient.getCache().getRequestCount() + "\nCache Hits: " + httpClient.getCache().getHitCount());

            if (response.code() == 200) {
                InputStream inputStream;
                ByteArrayOutputStream outArrrayIPStream;
                try {
                    inputStream = response.body().byteStream();
                    outArrrayIPStream = new ByteArrayOutputStream();
                    byte[] buffer = new byte[1024];
                    int statusUpdateTrigger = 0;
                    long downloaded = 0;
                    long responseSize = response.body().contentLength();

                    Log.d("Total file size to download: " + responseSize);

                    for (int count; (count = inputStream.read(buffer)) != -1; ) {
                        outArrrayIPStream.write(buffer, 0, count);
                        downloaded += count;
                        statusUpdateTrigger++;
//                        Log.d(String.format("%d / %d", downloaded, responseSize));

                        if (statusUpdateTrigger > AppConstants.STATUS_UPDATE_THRESHOLD) {
                            statusUpdateTrigger = 0;
                            Long downloadedPercent = downloaded * AppConstants.MAX_DOWNLOAD_PROGRESS / responseSize;
                            Log.d("downloaded percent: " + downloadedPercent);
                            mapDownloadBroadcastHelper.broadcastDownloadProgress(mapType, mapID, downloadedPercent.intValue());
                        }
                    }
                    byte[] responseImage = outArrrayIPStream.toByteArray();
                    BitmapFactory.Options options = new BitmapFactory.Options();
                    options.inMutable = true;
                    downloadedBitmap = BitmapFactory.decodeByteArray(responseImage, 0, responseImage.length, options);
                } catch (Exception e) {
                    CrashUtils.trackException("MAP download IO exception", e);
                    return null;
                }
            } else {
                CrashUtils.trackException("res code other than 200: " + response.code(), null);
                return null;
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        final HashMap<String, Object> result = new HashMap<>();
        result.put("map", downloadedBitmap);
        result.put("lost_modified", lastModifiedHeader);

        return result;
    }

    private Bitmap removeMapBorders(String mapFileName, Bitmap bmp) {
        //Try to remove unwanted parts from MAP. return the original MAP in case of any errors.
        try {
            //Trim the unwanted area from the Ultra Violet Map.
            if (mapFileName.equals(AppConstants.MAP_UV) || mapFileName.equals(AppConstants.MAP_HEAT)) {
                return Bitmap.createBitmap(bmp, 0, 180, 1250, 1400);
            }
        } catch (Exception e) {
            Crashlytics.log("trim MAP Error");
            Crashlytics.setString("MapType", mapFileName);
            try {
                if (bmp != null) {
                    Crashlytics.setInt("mapWidth", bmp.getWidth());
                    Crashlytics.setInt("mapHeight", bmp.getHeight());
                }
            } catch (Exception e1) {
                Crashlytics.logException(e1);
            }
            Crashlytics.logException(e);
        }
        return bmp;
    }

    private void saveDownloadedMap(String mapType, Bitmap bmp) throws Exception {
        File temp_file = new File(StorageUtils.getAppSpecificFolder() + File.separator + mapType + "_temp.jpg");
        FileOutputStream fileOutStream = new FileOutputStream(temp_file.getPath());

        // Compression Quality set to 100. ie. NO COMPRESSION.
        bmp.compress(Bitmap.CompressFormat.JPEG, 100, fileOutStream);
        fileOutStream.flush();
        fileOutStream.close();

        boolean success = temp_file.renameTo(new File(StorageUtils.getAppSpecificFolder(), mapType + ".jpg"));
        Log.d("Map  saved to: " + temp_file.getAbsolutePath() + ". Overwritten? = " + success);
    }

    public void networkUnavailableHandling(int mapID, int mapType) {
        Toast.makeText(context, context.getResources().getString(R.string.no_internet_connection), Toast.LENGTH_SHORT).show();
        markDownloadComplete(mapType, mapID, false);
    }

    public void markDownloadComplete(int mapType, int mapID, boolean status) {
        Log.d("Marking Download as complete for mapType:" + mapType + " mapID:" + mapID + " status:" + status);
        //Updating the controller flag about the specific map type is no more on progress.
        activeDownloadsList[mapID] = false;
        mapDownloadBroadcastHelper.broadcastDownloadStatus(mapType, mapID, status);
    }
}