package org.realityforge.replicant.server.ee;

import java.util.Collection;
import java.util.Collections;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.EntityManager;
import javax.transaction.Status;
import javax.transaction.TransactionSynchronizationRegistry;
import org.realityforge.replicant.server.ChangeSet;
import org.realityforge.replicant.server.EntityMessage;
import org.realityforge.replicant.server.EntityMessageEndpoint;
import org.realityforge.replicant.server.EntityMessageSet;
import org.realityforge.replicant.shared.transport.ReplicantContext;

/**
 * Utility class for interacting with replication request infrastructure.
 */
public final class ReplicationRequestUtil
{
  /**
   * The key used to access the registry to get the current call depth.
   */
  private static final String REPLICATION_TX_DEPTH = "ReplicationTxDepth";

  private ReplicationRequestUtil()
  {
  }

  /**
   * Start a replication context.
   *
   * @param invocationKey the identifier of the element that is initiating replication. (i.e. Method name).
   * @param sessionID the id of the session that initiated change if any.
   * @param requestID the id of the request in the session that initiated change..
   */
  public static void startReplication( @Nonnull final TransactionSynchronizationRegistry registry,
                                       @Nonnull final String invocationKey,
                                       @Nullable final String sessionID,
                                       @Nullable final String requestID )
  {
    final Object existingKey = registry.getResource( ReplicantContext.REPLICATION_INVOCATION_KEY );
    if ( null != existingKey )
    {
      final String message =
        "Attempted to invoke service method '" + invocationKey +
        "' while there is an active replication '" + existingKey + "'";
      throw new IllegalStateException( message );
    }

    registry.putResource( ReplicantContext.REPLICATION_INVOCATION_KEY, invocationKey );
    if ( null != sessionID )
    {
      registry.putResource( ReplicantContext.SESSION_ID_KEY, sessionID );
    }
    else
    {
      registry.putResource( ReplicantContext.SESSION_ID_KEY, null );
    }
    if ( null != requestID )
    {
      registry.putResource( ReplicantContext.REQUEST_ID_KEY, requestID );
    }
    else
    {
      registry.putResource( ReplicantContext.REQUEST_ID_KEY, null );
    }
  }

  /**
   * Complete a replication context and submit changes for replication.
   *
   * @return true if the request is complete and did not generate any change messages, false otherwise.
   */
  public static boolean completeReplication( @Nonnull final TransactionSynchronizationRegistry registry,
                                             @Nonnull final EntityManager entityManager,
                                             @Nonnull final EntityMessageEndpoint endpoint )
  {
    if ( Status.STATUS_ACTIVE == registry.getTransactionStatus() &&
         entityManager.isOpen() &&
         !registry.getRollbackOnly() )
    {
      // Remove invocation key to finalize replication context
      registry.putResource( ReplicantContext.REPLICATION_INVOCATION_KEY, null );

      final String sessionID = (String) registry.getResource( ReplicantContext.SESSION_ID_KEY );
      final String requestID = (String) registry.getResource( ReplicantContext.REQUEST_ID_KEY );
      boolean requestComplete = true;
      entityManager.flush();
      final EntityMessageSet messageSet = EntityMessageCacheUtil.removeEntityMessageSet( registry );
      final ChangeSet changeSet = EntityMessageCacheUtil.removeSessionChanges( registry );
      if ( null != messageSet || null != changeSet )
      {
        final Collection<EntityMessage> messages =
          null == messageSet ? Collections.<EntityMessage>emptySet() : messageSet.getEntityMessages();
        if ( null != changeSet || messages.size() > 0 )
        {
          requestComplete = !endpoint.saveEntityMessages( sessionID, requestID, messages, changeSet );
        }
      }
      final Boolean complete = (Boolean) registry.getResource( ReplicantContext.REQUEST_COMPLETE_KEY );
      return !( null != complete && !complete ) && requestComplete;
    }
    else
    {
      return true;
    }
  }

  /**
   * Set the current call depth in replication request.
   */
  public static void setReplicationCallDepth( final int depth )
  {
    if ( 0 == depth )
    {
      ReplicantContextHolder.remove( REPLICATION_TX_DEPTH );
    }
    else
    {
      ReplicantContextHolder.put( REPLICATION_TX_DEPTH, depth );
    }
  }

  /**
   * Return the current call depth in the replication request.
   */
  public static int getReplicationCallDepth()
  {
    final Integer depth = (Integer) ReplicantContextHolder.get( REPLICATION_TX_DEPTH );
    if ( null == depth )
    {
      return 0;
    }
    else
    {
      return depth;
    }
  }
}

@@ -731,168 +731,8 @@
 l%0A%7B%0A
-  /**%0A   * The key used to access the registry to get the current call depth.%0A   */%0A  private static final String REPLICATION_TX_DEPTH = %22ReplicationTxDepth%22;%0A%0A
   pr
@@ -4133,641 +4133,7 @@
 %0A  %7D
-%0A%0A  /**%0A   * Set the current call depth in replication request.%0A   */%0A  public static void setReplicationCallDepth( final int depth )%0A  %7B%0A    if ( 0 == depth )%0A    %7B%0A      ReplicantContextHolder.remove( REPLICATION_TX_DEPTH );%0A    %7D%0A    else%0A    %7B%0A      ReplicantContextHolder.put( REPLICATION_TX_DEPTH, depth );%0A    %7D%0A  %7D%0A%0A  /**%0A   * Return the current call depth in the replication request.%0A   */%0A  public static int getReplicationCallDepth()%0A  %7B%0A    final Integer depth = (Integer) ReplicantContextHolder.get( REPLICATION_TX_DEPTH );%0A    if ( null == depth )%0A    %7B%0A      return 0;%0A    %7D%0A    else%0A    %7B%0A      return depth;%0A    %7D%0A  %7D
 %0A%7D%0A

Remove support for storing the replication depth as no longer used