package org.commcare.android.tasks;

import org.commcare.android.javarosa.AndroidLogger;
import org.commcare.android.resource.AndroidResourceManager;
import org.commcare.android.resource.AppInstallStatus;
import org.commcare.android.resource.ResourceInstallUtils;
import org.commcare.android.tasks.templates.ManagedAsyncTask;
import org.commcare.android.util.AndroidCommCarePlatform;
import org.commcare.dalvik.application.CommCareApp;
import org.commcare.dalvik.application.CommCareApplication;
import org.commcare.resources.model.ProcessCancelled;
import org.commcare.resources.model.Resource;
import org.commcare.resources.model.ResourceTable;
import org.commcare.resources.model.TableStateListener;
import org.javarosa.core.services.Logger;

import java.util.Vector;

/**
 * Upgrades the seated app in the background. If the user opens the Upgrade
 * activity, this task will report its progress to that activity. Enforces the
 * constraint that only one instance is ever running.
 *
 * @author Phillip Mates (pmates@dimagi.com)
 */
public class UpdateTask
        extends ManagedAsyncTask<String, Integer, AppInstallStatus>
        implements TableStateListener, ProcessCancelled {

    private static final String TAG = UpdateTask.class.getSimpleName();

    private TaskListener<Integer, AppInstallStatus> taskListener = null;

    private static UpdateTask singletonRunningInstance = null;

    private int currentProgress = 0;
    private int maxProgress = 0;

    private final AndroidResourceManager resourceManager;
    private final CommCareApp app;
    private String profileRef;

    private UpdateTask() {
        app = CommCareApplication._().getCurrentApp();
        AndroidCommCarePlatform platform = app.getCommCarePlatform();

        resourceManager =
                new AndroidResourceManager(platform);

        resourceManager.setUpgradeListeners(this, this);
    }

    public static UpdateTask getNewInstance() {
        if (singletonRunningInstance == null) {
            singletonRunningInstance = new UpdateTask();
            return singletonRunningInstance;
        } else {
            throw new IllegalStateException("There is a " + TAG + " instance.");
        }
    }

    public static UpdateTask getRunningInstance() {
        if (singletonRunningInstance != null &&
                singletonRunningInstance.getStatus() == Status.RUNNING) {
            return singletonRunningInstance;
        }
        return null;
    }

    @Override
    protected final AppInstallStatus doInBackground(String... params) {
        profileRef = params[0];

        setupUpgrade();

        try {
            return performUpgrade();
        } catch (Exception e) {
            ResourceInstallUtils.logInstallError(e,
                    "Unknown error ocurred during install|");
            return AppInstallStatus.UnknownFailure;
        }
    }

    private void setupUpgrade() {
        ResourceInstallUtils.recordUpdateAttempt(app);

        app.setupSandbox();

        Logger.log(AndroidLogger.TYPE_RESOURCES,
                "Beginning install attempt for profile " + profileRef);
    }

    private AppInstallStatus performUpgrade() {
        Resource profile = resourceManager.getMasterProfile();
        boolean appInstalled = (profile != null &&
                profile.getStatus() == Resource.RESOURCE_STATUS_INSTALLED);

        if (!appInstalled) {
            return AppInstallStatus.UnknownFailure;
        }

        String profileRefWithParams =
                ResourceInstallUtils.addParamsToProfileReference(profileRef);

        return resourceManager.checkAndPrepareUpgradeResources(profileRefWithParams);
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);

        if (taskListener != null) {
            taskListener.processTaskUpdate(values);
        }
    }

    @Override
    protected void onPostExecute(AppInstallStatus result) {
        super.onPostExecute(result);

        boolean reusePartialTable =
                (result == AppInstallStatus.UnknownFailure ||
                        result == AppInstallStatus.NoLocalStorage);

        boolean inIncompleteState = !(result == AppInstallStatus.UpdateStaged ||
                result == AppInstallStatus.UpToDate);
        if (inIncompleteState && !reusePartialTable) {
            resourceManager.clearUpgradeTable();
        }

        if (taskListener != null) {
            taskListener.processTaskResult(result);
        }

        singletonRunningInstance = null;
    }

    @Override
    protected void onCancelled(AppInstallStatus result) {
        if (android.os.Build.VERSION.SDK_INT >= 11) {
            super.onCancelled(result);
        } else {
            super.onCancelled();
        }

        if (taskListener != null) {
            taskListener.processTaskCancel(result);
        }

        resourceManager.upgradeCancelled();

        singletonRunningInstance = null;
    }

    public void registerTaskListener(TaskListener<Integer, AppInstallStatus> listener)
            throws TaskListenerRegistrationException {
        if (taskListener != null) {
            throw new TaskListenerRegistrationException("This " + TAG +
                    " was already registered with a TaskListener");
        }
        taskListener = listener;
    }

    public void unregisterTaskListener(TaskListener<Integer, AppInstallStatus> listener)
            throws TaskListenerRegistrationException {
        if (listener != taskListener) {
            throw new TaskListenerRegistrationException("The provided listener wasn't " +
                    "registered with this " + TAG);
        }
        taskListener = null;
    }

    @Override
    public void resourceStateUpdated(ResourceTable table) {
        Vector<Resource> resources =
                AndroidResourceManager.getResourceListFromProfile(table);

        currentProgress = 0;
        for (Resource r : resources) {
            int resourceStatus = r.getStatus();
            if (resourceStatus == Resource.RESOURCE_STATUS_UPGRADE ||
                    resourceStatus == Resource.RESOURCE_STATUS_INSTALLED) {
                currentProgress += 1;
            }
        }
        maxProgress = resources.size();
        incrementProgress(currentProgress, maxProgress);
    }

    @Override
    public void incrementProgress(int complete, int total) {
        this.publishProgress(complete, total);
    }

    @Override
    public boolean processWasCancelled() {
        return isCancelled();
    }

    public int getProgress() {
        return currentProgress;
    }

    public int getMaxProgress() {
        return maxProgress;
    }
}

@@ -525,23 +525,23 @@
 s.model.
-Process
+Install
 Cancelle
@@ -776,16 +776,28 @@
 %0A * 
-Upgrades
+Stages an update for
  the
@@ -852,19 +852,18 @@
  the
- Upgrade%0A *
+%0A * Update
  act
@@ -921,16 +921,19 @@
 ctivity.
+%0A *
  Enforce
@@ -937,19 +937,16 @@
 rces the
-%0A *
  constra
@@ -992,16 +992,175 @@
 ing.%0A *%0A
+ * Will be cancelled on user logout, but can still run if no user is logged in.%0A *%0A * TODO PLM: App Manager must cancel this task upon UpdateActivity exit.%0A *%0A
  * @auth
@@ -1335,15 +1335,15 @@
 er, 
-Process
+Install
 Canc
@@ -1440,327 +1440,326 @@
 ate 
-TaskListener%3CInteger, AppInstallStatus%3E taskListener = null;%0A%0A    private static UpdateTask singletonRunningInstance = null;%0A%0A    private int currentProgress = 0;%0A    private int maxProgress = 0;%0A%0A    private final AndroidResourceManager resourceManager;%0A    private final CommCareApp app;%0A    private String profileRef
+static UpdateTask singletonRunningInstance = null;%0A%0A    private final AndroidResourceManager resourceManager;%0A    private final CommCareApp app;%0A%0A    private TaskListener%3CInteger, AppInstallStatus%3E taskListener = null;%0A    private String profileRef;%0A    private int currentProgress = 0;%0A    private int maxProgress = 0
 ;%0A%0A 
@@ -2319,39 +2319,49 @@
 on(%22
-There is a %22 + TAG + %22 instance
+An instance of %22 + TAG + %22 already exists
 .%22);
@@ -6664,18 +6664,18 @@
 ean 
-processWas
+wasInstall
 Canc

Rename install cancelled interface