// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.collide.clientlibs.invalidation;

import javax.annotation.Nullable;

import com.google.collide.dto.client.DtoUtils;
import com.google.collide.dtogen.shared.ServerToClientDto;
import com.google.collide.shared.invalidations.InvalidationObjectId;
import com.google.collide.shared.util.ListenerRegistrar.Remover;


/**
 * An object which can register and unregister for Tango invalidations.
 */
public interface InvalidationRegistrar {

  /**
   * A handle to the registration for invalidations.
   *
   * <p>
   * Clients that registered with {@code recoverMissingPayloads=true} must call
   * {@link #initializeRecoverer(long)} after receiving the initial object
   * contents.
   *
   */
  public interface RemovableHandle extends Remover {

    /**
     * Sets the next expected version of the object.
     * 
     * <p>
     * This is required to bootstrap the ability to recover missing payloads or
     * squelched invalidations.
     * 
     * <p>
     * This can be called multiple times to tell the tango recoverer that your
     * model is now at a new version.
     */
    void initializeRecoverer(long nextExpectedVersion);
  }

  /**
   * A listener for Tango object invalidation.
   */
  public interface Listener {
    
    /**
     * A handle to notify the caller of {@link Listener#onInvalidated} that
     * this invalidation will be processed asychronously. This means the caller will not call again
     * until the current invalidation has finished being processed.
     */
    public interface AsyncProcessingHandle {
      /**
       * Notifies the caller that the invalidation will be processed asychronously.
       *
       * <p>
       * {@link #finishedAsyncProcessing()} must be called when the invalidation has been processed.
       */
      void startedAsyncProcessing();
      
      /**
       * Notifies the caller of {@link Listener#onInvalidated} that the
       * invalidation has finished being processed.
       */
      void finishedAsyncProcessing();
    }
    
    /**
     * @param payload the invalidation payload, or null if no payload arrived
     * @param asyncProcessingHandle for objects that require payload recovery, this will tell the
     *        recovery stack to allow for async processing of this payload
     */
    void onInvalidated(String objectName, long version, @Nullable String payload,
        AsyncProcessingHandle asyncProcessingHandle);
  }

  /**
   * A listener which handles deserializing a Tango payload into a given DTO
   * type.
   *
   * @param <T> the type of dto.
   */
  public abstract static class DtoListener<T extends ServerToClientDto> implements Listener {
    private final int routingType;

    public DtoListener(int routingType) {      
      this.routingType = routingType;
    }

    @Override
    public void onInvalidated(String objectName, long version, String payload,
        AsyncProcessingHandle asyncProcessingHandle) {
      T dto = null;
      if (payload != null) {
        dto = DtoUtils.parseAsDto(payload, routingType);
      }
      onInvalidatedDto(objectName, version, dto, asyncProcessingHandle);
    }

    /**
     * @see Listener#onInvalidated
     */
    public abstract void onInvalidatedDto(String objectName, long version, T dto,
        AsyncProcessingHandle asyncProcessingHandle);
  }

  /**
   * Registers the client to receive invalidations for the given object.
   *
   * <p>
   * If the client requires recovering from missing payloads ({@code
   * recoverMissingPayloads} is true), the suggested usage pattern is:
   * <ul>
   * <li>Register for Tango invalidations using this method</li>
   * <li>Out-of-band (e.g. via XHR) fetch the initial contents of the object
   * </li>
   * <li>Call {@link RemovableHandle#initializeRecoverer} with the fetched
   * object's version</li>
   * </ul>
   */
  public RemovableHandle register(InvalidationObjectId<?> objectId, Listener eventListener);

  public void unregister(InvalidationObjectId<?> objectId);
}
