package org.redcross.openmapkit.deployments;


import android.app.Activity;
import android.app.DownloadManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;

import org.json.JSONArray;
import org.json.JSONObject;
import org.redcross.openmapkit.ExternalStorage;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class DeploymentDownloader extends AsyncTask<Void, Void, Void> {
    private Set<DeploymentDownloaderListener> listeners = new HashSet<>();
    private DownloadManager downloadManager;
    private Deployment deployment;
    private long[] downloadIds;

    private boolean downloading = false;
    private boolean canceled = false;
    private long bytesDownloaded = 0;
    private int filesCompleted = 0;

    public DeploymentDownloader(Deployment deployment, Activity activity) {
        downloadManager = (DownloadManager)activity.getSystemService(Activity.DOWNLOAD_SERVICE);
        this.deployment = deployment;
        downloadIds = new long[deployment.fileCount()];
        if (activity instanceof DeploymentDownloaderListener) {
            addListener((DeploymentDownloaderListener)activity);
        }
    }

    public void addListener(DeploymentDownloaderListener listener) {
        listeners.add(listener);
    }

    public void cancel() {
        canceled = true;
        downloading = false;
        for (long id : downloadIds) {
            if (id > -1) {
                downloadManager.remove(id);
            }
        }
        notifyDeploymentDownloadCanceled();
    }

    public boolean isDownloading() {
        return downloading;
    }

    @Override
    protected void onPreExecute() {
        canceled = false;
        downloading = true;
        String msg = progressMsg();
        notifyDeploymentDownloadProgressUpdate(msg, 0);
    }

    @Override
    protected Void doInBackground(Void... nothing) {
        // We want to start fresh for a download, because we don't want duplicates of a file.
        ExternalStorage.deleteDeployment(deployment.name());
        deployment.writeJSONToDisk();
        String deploymentDir = ExternalStorage.deploymentDirRelativeToExternalDir(deployment.name());

        int idx = 0;
        List<JSONObject> files = deployment.filesToDownload();
        for (JSONObject f : files) {
            String url = f.optString("url");
            if (url == null) continue;
            DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
            request.setDestinationInExternalPublicDir(deploymentDir, Deployment.fileNameFromUrl(url));
            long downloadId = downloadManager.enqueue(request);
            downloadIds[idx++] = downloadId;
        }
        pollDownloadManager();
        return null;
    }

    @Override
    protected void onProgressUpdate(Void... nothing) {
        String msg = progressMsg();
        notifyDeploymentDownloadProgressUpdate(msg, bytesDownloaded);
    }

    @Override
    protected void onPostExecute(Void nothing) {
        if (canceled) return;
        notifyDeploymentDownloadComplete();
    }

    private String progressMsg() {
        return "Downloading deployment. "
                + filesCompleted + "/"
                + deployment.fileCount()
                + " files. "
                + ((double)bytesDownloaded) / 1000000.0 + " MB.";
    }

    private void notifyDeploymentDownloadProgressUpdate(String msg, long bytesDownloaded) {
        for (DeploymentDownloaderListener listener : listeners) {
            if (listener != null) {
                listener.onDeploymentDownloadProgressUpdate(msg, bytesDownloaded);
            }
        }
    }

    private void notifyDeploymentDownloadComplete() {
        for (DeploymentDownloaderListener listener : listeners) {
            if (listener != null) {
                listener.onDeploymentDownloadComplete();
            }
        }
    }

    private void notifyDeploymentDownloadCanceled() {
        for (DeploymentDownloaderListener listener : listeners) {
            if (listener != null) {
                listener.onDeploymentDownloadCancel();
            }
        }
    }

    private void pollDownloadManager() {
        while (downloading) {
            DownloadManager.Query q = new DownloadManager.Query();
            q.setFilterById(downloadIds);
            Cursor cursor = downloadManager.query(q);
            bytesDownloaded = 0;
            filesCompleted = 0;
            while(cursor.moveToNext()) {
                bytesDownloaded += cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
                int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
                if (status != DownloadManager.STATUS_PENDING && status != DownloadManager.STATUS_RUNNING) {
                    ++filesCompleted;
                }
            }
            if (!canceled) {
                publishProgress();
            }
            if (deployment.fileCount() == filesCompleted) {
                downloading = false;
            }
            // throttle the thread
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}