package org.msf.records.diagnostics;

import android.app.Application;
import android.content.BroadcastReceiver;
import android.support.annotation.Nullable;

import org.msf.records.utils.Logger;

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

import de.greenrobot.event.EventBus;

/**
 * An individual health check to be performed while the application is active.
 *
 * <p>Subclasses can choose to implement this class however they see fit so long as they call
 * {@link #reportIssue} when a new issue occurs and {@link #resolveIssue} or
 * {@link #resolveAllIssues} when issues are resolved. For example, if a health change can be
 * triggered by an Android OS broadcast, it may register a {@link BroadcastReceiver}
 * in {@link #startImpl}; if it needs to poll another service, it may start a background thread.
 *
 * <p>Subclasses must stop checking (e.g., unregister {@link BroadcastReceiver}s or stop
 * background threads) when {@link #stopImpl} is called.
 */
public abstract class HealthCheck {

    private static final Logger LOG = Logger.create();

    private final Object mLock = new Object();

    protected final Application mApplication;
    protected final Set<HealthIssue> mActiveIssues;

    @Nullable private EventBus mHealthEventBus;

    protected HealthCheck(Application application) {
        mApplication = application;
        mActiveIssues = new HashSet<>();
    }

    /**
     * Starts the health check.
     *
     * <p>After this method is called, the health check must post any health issue events on the
     * specified {@link EventBus}.
     */
    public final void start(EventBus healthEventBus) {
        synchronized (mLock) {
            mHealthEventBus = healthEventBus;
            startImpl();
        }
    }

    /** Stops the health check without clearing the currently active issues. */
    public final void pause() {
        synchronized (mLock) {
            pauseImpl();
        }
    }

    /** Resumes the health check. */
    public final void resume() {
        synchronized (mLock) {
            resumeImpl();
        }
    }

    /**
     * Stops the health check.
     *
     * <p>{@link #start} may be called again to restart checks.
     */
    public final void stop() {
        synchronized (mLock) {
            mHealthEventBus = null;
            stopImpl();
        }
    }

    public final void clear() {
        mActiveIssues.clear();
    }

    protected abstract void startImpl();

    protected abstract void stopImpl();

    protected void pauseImpl() {
        stopImpl();
    }

    protected void resumeImpl() {
        startImpl();
    }

    /**
     * Reports an issue as being active.
     */
    protected final void reportIssue(HealthIssue healthIssue) {
        EventBus eventBus;
        synchronized (mLock) {
            if (mHealthEventBus == null) {
                LOG.w(
                        "A health issue was reported even though no event bus was registered to "
                                + "handle it: %1$s.",
                        healthIssue.toString());
                return;
            }

            mActiveIssues.add(healthIssue);
            eventBus = mHealthEventBus;
        }

        eventBus.post(healthIssue.discovered);
    }

    /**
     * Marks as resolved all issues that have previously been reported.
     */
    protected final void resolveAllIssues() {
        EventBus eventBus;
        Set<HealthIssue> activeIssues;
        synchronized (mLock) {
            if (mHealthEventBus == null) {
                LOG.w(
                        "Health issues were resolved even though no event bus was registered to "
                                + "handle them.");
                return;
            }

            eventBus = mHealthEventBus;
            activeIssues = new HashSet<>(mActiveIssues);
            mActiveIssues.clear();
        }

        for (HealthIssue healthIssue : activeIssues) {
            eventBus.post(healthIssue.resolved);
        }
    }

    /**
     * Marks as resolved the specified issue.
     *
     * <p>If the issue was not previously reported, this method does nothing.
     */
    protected final void resolveIssue(HealthIssue healthIssue) {
        EventBus eventBus;
        boolean wasIssueActive;
        synchronized (mLock) {
            if (mHealthEventBus == null) {
                LOG.w(
                        "A health issue was resolved even though no event bus was registered to "
                                + "handle it: %1$s.",
                        healthIssue.toString());
                return;
            }

            eventBus = mHealthEventBus;
            wasIssueActive = mActiveIssues.remove(healthIssue);
        }

        if (wasIssueActive) {
            eventBus.post(healthIssue.resolved);
        }
    }

    /**
     * Returns true if this HealthCheck knows for certain that the Buendia
     * API is unavailable at this moment.  Implementations of this method
     * should never return true unless they can guarantee that their knowledge
     * of the system state is up to date; for example, if a HealthCheck decides
     * to return true when the network is down, it is responsible for detecting
     * any event that could cause the network to come back up.
     */
    public boolean isApiUnavailable() {
        return false;
    }
}

@@ -1752,337 +1752,8 @@
  %7D%0A%0A
-    /** Stops the health check without clearing the currently active issues. */%0A    public final void pause() %7B%0A        synchronized (mLock) %7B%0A            pauseImpl();%0A        %7D%0A    %7D%0A%0A    /** Resumes the health check. */%0A    public final void resume() %7B%0A        synchronized (mLock) %7B%0A            resumeImpl();%0A        %7D%0A    %7D%0A%0A
     
@@ -1777,32 +1777,60 @@
 the health check
+ without clearing its issues
 .%0A     *%0A     * 
@@ -2029,24 +2029,80 @@
    %7D%0A    %7D%0A%0A
+    /** Clears all the issues for this health check. */%0A
     public f
@@ -2246,130 +2246,8 @@
 );%0A%0A
-    protected void pauseImpl() %7B%0A        stopImpl();%0A    %7D%0A%0A    protected void resumeImpl() %7B%0A        startImpl();%0A    %7D%0A%0A
     
@@ -2929,37 +2929,28 @@
 hat 
-have previously been reported
+are currently active
 .%0A  

Remove unused methods.