package arez.dom;

import arez.ComputableValue;
import arez.annotations.Action;
import arez.annotations.ArezComponent;
import arez.annotations.ComputableValueRef;
import arez.annotations.DepType;
import arez.annotations.Memoize;
import arez.annotations.Observable;
import arez.annotations.OnActivate;
import arez.annotations.OnDeactivate;
import elemental2.dom.DomGlobal;
import elemental2.dom.EventListener;
import java.util.Date;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
 * An observable model that declares state that tracks when the user is "online".
 * The online state is essentially a reflection of the browsers "navigator.onLine"
 * value. If an observer is observing the model, the model listens for changes from
 * the browser and updates the online state as appropriate. However if there is no
 * observer for the state, the model will not listen to to the browser events so as
 * not to have any significant performance impact.
 *
 * <h1>A very simple example</h1>
 * <pre>{@code
 * import com.google.gwt.core.client.EntryPoint;
 * import elemental2.dom.DomGlobal;
 * import arez.Arez;
 * import arez.networkstatus.NetworkStatus;
 *
 * public class NetworkStatusExample
 *   implements EntryPoint
 * {
 *   public void onModuleLoad()
 *   {
 *     final NetworkStatus networkStatus = NetworkStatus.create();
 *     Arez.context().observer( () -> {
 *       final String message = "Network Status: " + ( networkStatus.isOnLine() ? "Online" : "Offline" );
 *       DomGlobal.console.log( message );
 *       if ( !networkStatus.isOnLine() )
 *       {
 *         DomGlobal.console.log( "Offline since: " + networkStatus.getLastChangedAt() );
 *       }
 *     } );
 *   }
 * }
 * }</pre>
 */
@ArezComponent( nameIncludesId = false )
public abstract class NetworkStatus
{
  private final EventListener _listener;
  @Nullable
  private Date _lastChangedAt;

  /**
   * Create an instance of NetworkStatus.
   *
   * @return the NetworkStatus instance.
   */
  @Nonnull
  public static NetworkStatus create()
  {
    return new Arez_NetworkStatus();
  }

  NetworkStatus()
  {
    _listener = e -> updateOnlineStatus();
    _lastChangedAt = new Date();
  }

  /**
   * Return true if the browser is online, false otherwise.
   *
   * @return true if the browser is online, false otherwise.
   */
  @Memoize( depType = DepType.AREZ_OR_EXTERNAL )
  public boolean isOnLine()
  {
    return DomGlobal.navigator.onLine;
  }

  /**
   * Return the last time at which online status changed.
   * This will default to the time the component was created, otherwise
   * the time at which the online status was changed.
   *
   * @return the last time at which online status changed.
   */
  @Observable
  @Nullable
  public Date getLastChangedAt()
  {
    return _lastChangedAt;
  }

  void setLastChangedAt( @Nullable final Date lastChangedAt )
  {
    _lastChangedAt = lastChangedAt;
  }

  @ComputableValueRef
  abstract ComputableValue<Boolean> getOnLineComputableValue();

  @OnActivate
  final void onOnLineActivate()
  {
    DomGlobal.window.addEventListener( "online", _listener );
    DomGlobal.window.addEventListener( "offline", _listener );
  }

  @OnDeactivate
  final void onOnLineDeactivate()
  {
    DomGlobal.window.removeEventListener( "online", _listener );
    DomGlobal.window.removeEventListener( "offline", _listener );
  }

  @Action
  void updateOnlineStatus()
  {
    getOnLineComputableValue().reportPossiblyChanged();
    setLastChangedAt( new Date() );
  }
}

@@ -461,42 +461,8 @@
 ull;
-%0Aimport javax.annotation.Nullable;
 %0A%0A/*
@@ -1815,51 +1815,8 @@
 ner;
-%0A  @Nullable%0A  private Date _lastChangedAt;
 %0A%0A  
@@ -1997,24 +1997,36 @@
 tworkStatus(
+ new Date() 
 );%0A  %7D%0A%0A  Ne
@@ -2090,41 +2090,8 @@
 ();%0A
-    _lastChangedAt = new Date();%0A
   %7D%0A
@@ -2632,23 +2632,22 @@
 ble%0A  @N
+onn
 ull
-able
 %0A  publi
@@ -2648,16 +2648,25 @@
  public 
+abstract 
 Date get
@@ -2684,46 +2684,21 @@
 At()
-%0A  %7B%0A    return _lastChangedAt;%0A  %7D%0A%0A 
+;%0A%0A  abstract
  voi
@@ -2723,20 +2723,13 @@
 ( @N
-ullable fina
+onnul
 l Da
@@ -2750,52 +2750,9 @@
 At )
-%0A  %7B%0A    _lastChangedAt = lastChangedAt;%0A  %7D
+;
 %0A%0A  

Make lastChangedAt an observable property