package org.realityforge.gwt.appcache.client;

import com.google.gwt.core.shared.GWT;
import com.google.web.bindery.event.shared.EventBus;
import com.google.web.bindery.event.shared.HandlerRegistration;
import javax.annotation.Nonnull;
import org.realityforge.gwt.appcache.client.event.CachedEvent;
import org.realityforge.gwt.appcache.client.event.CheckingEvent;
import org.realityforge.gwt.appcache.client.event.DownloadingEvent;
import org.realityforge.gwt.appcache.client.event.ErrorEvent;
import org.realityforge.gwt.appcache.client.event.NoUpdateEvent;
import org.realityforge.gwt.appcache.client.event.ObsoleteEvent;
import org.realityforge.gwt.appcache.client.event.ProgressEvent;
import org.realityforge.gwt.appcache.client.event.UpdateReadyEvent;
import org.realityforge.gwt.appcache.client.html5.Html5ApplicationCache;

public abstract class ApplicationCache
{
  public static enum Status
  {
    UNCACHED,
    IDLE,
    CHECKING,
    DOWNLOADING,
    UPDATEREADY,
    OBSOLETE
  }

  private static ApplicationCache g_cache;

  private final EventBus _eventBus;

  public static ApplicationCache get()
  {
    if ( null == g_cache )
    {
      if ( GWT.isClient() && Html5ApplicationCache.isSupported() && Html5ApplicationCache.hasManifest() )
      {
        register( new Html5ApplicationCache() );
      }
    }
    return g_cache;
  }

  public static void register( @Nonnull final ApplicationCache applicationCache )
  {
    g_cache = applicationCache;
  }

  public static boolean deregister( @Nonnull final ApplicationCache applicationCache )
  {
    if ( g_cache != applicationCache )
    {
      return false;
    }
    else
    {
      g_cache = null;
      return true;
    }
  }

  protected ApplicationCache( final EventBus eventBus )
  {
    _eventBus = eventBus;
  }

  @Nonnull
  public abstract Status getStatus();

  /**
   * Cancels the application cache download process.
   * The call is ignored if no download in progress.
   */
  public abstract void abort();

  /**
   * Request that the browser swap in the new application cache.
   * This may fail if there is no update or no application cache.
   *
   * @return false if unable swap in cache.
   */
  public abstract boolean swapCache();

  /**
   * Request that the browser update the application cache.
   * This may fail if the application is not cached.
   *
   * @return false if unable to request update.
   */
  public abstract boolean requestUpdate();

  /**
   * Attempt to remove the application from the cache.
   * This is achieved by forcing the server to return a 404 or 410 when updating the manifest.
   *
   * @return true if request succeeded.
   */
  public abstract boolean removeCache();

  @Nonnull
  public final HandlerRegistration addCheckingHandler( @Nonnull CheckingEvent.Handler handler )
  {
    return _eventBus.addHandler( CheckingEvent.getType(), handler );
  }

  @Nonnull
  public final HandlerRegistration addCachedHandler( @Nonnull CachedEvent.Handler handler )
  {
    return _eventBus.addHandler( CachedEvent.getType(), handler );
  }

  @Nonnull
  public final HandlerRegistration addDownloadingHandler( @Nonnull DownloadingEvent.Handler handler )
  {
    return _eventBus.addHandler( DownloadingEvent.getType(), handler );
  }

  @Nonnull
  public final HandlerRegistration addErrorHandler( @Nonnull ErrorEvent.Handler handler )
  {
    return _eventBus.addHandler( ErrorEvent.getType(), handler );
  }

  @Nonnull
  public final HandlerRegistration addNoUpdateHandler( @Nonnull final NoUpdateEvent.Handler handler )
  {
    return _eventBus.addHandler( NoUpdateEvent.getType(), handler );
  }

  @Nonnull
  public final HandlerRegistration addObsoleteHandler( @Nonnull ObsoleteEvent.Handler handler )
  {
    return _eventBus.addHandler( ObsoleteEvent.getType(), handler );
  }

  @Nonnull
  public final HandlerRegistration addProgressHandler( @Nonnull ProgressEvent.Handler handler )
  {
    return _eventBus.addHandler( ProgressEvent.getType(), handler );
  }

  @Nonnull
  public final HandlerRegistration addUpdateReadyHandler( @Nonnull UpdateReadyEvent.Handler handler )
  {
    return _eventBus.addHandler( UpdateReadyEvent.getType(), handler );
  }

  protected final void onChecking()
  {
    _eventBus.fireEventFromSource( new CheckingEvent(), this );
  }

  protected final void onError()
  {
    _eventBus.fireEventFromSource( new ErrorEvent(), this );
  }

  protected final void onNoUpdate()
  {
    _eventBus.fireEventFromSource( new NoUpdateEvent(), this );
  }

  protected final void onDownloading()
  {
    _eventBus.fireEventFromSource( new DownloadingEvent(), this );
  }

  protected final void onProgress( final int loaded, final int total )
  {
    _eventBus.fireEventFromSource( new ProgressEvent( loaded, total ), this );
  }

  protected final void onUpdateReady()
  {
    _eventBus.fireEventFromSource( new UpdateReadyEvent(), this );
  }

  protected final void onCached()
  {
    _eventBus.fireEventFromSource( new CachedEvent(), this );
  }

  protected final void onObsolete()
  {
    _eventBus.fireEventFromSource( new ObsoleteEvent(), this );
  }
}

@@ -4174,32 +4174,72 @@
 handler );%0A  %7D%0A%0A
+  /**%0A   * Fire a Checking event.%0A   */%0A
   protected fina
@@ -4254,24 +4254,24 @@
 nChecking()%0A
-
   %7B%0A    _eve
@@ -4323,32 +4323,70 @@
 ), this );%0A  %7D%0A%0A
+  /**%0A   * Fire an Error event.%0A   */%0A
   protected fina
@@ -4464,32 +4464,72 @@
 ), this );%0A  %7D%0A%0A
+  /**%0A   * Fire a NoUpdate event.%0A   */%0A
   protected fina
@@ -4613,32 +4613,75 @@
 ), this );%0A  %7D%0A%0A
+  /**%0A   * Fire a Downloading event.%0A   */%0A
   protected fina
@@ -4771,32 +4771,72 @@
 ), this );%0A  %7D%0A%0A
+  /**%0A   * Fire a Progress event.%0A   */%0A
   protected fina
@@ -4970,32 +4970,75 @@
 ), this );%0A  %7D%0A%0A
+  /**%0A   * Fire a UpdateReady event.%0A   */%0A
   protected fina
@@ -5128,32 +5128,73 @@
 ), this );%0A  %7D%0A%0A
+  /**%0A   * Fire an Obsolete event.%0A   */%0A
   protected fina
@@ -5278,24 +5278,65 @@
 his );%0A  %7D%0A%0A
+  /**%0A   * Fire an Obsolete event.%0A   */%0A
   protected 

Add some basic javadocs on methods that raise events