package org.jboss.resteasy.core;

import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.ResteasyAsynchronousResponse;
import org.jboss.resteasy.spi.UnhandledException;

import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
public class SynchronousExecutionContext extends AbstractExecutionContext
{
   protected final CountDownLatch syncLatch = new CountDownLatch(1);
   protected long timeout;
   protected TimeUnit timeoutUnit = TimeUnit.MILLISECONDS;
   protected boolean wasSuspended;
   protected volatile boolean done;
   protected Object responseLock = new Object();
   protected ResteasyAsynchronousResponse asynchronousResponse;

   public SynchronousExecutionContext(SynchronousDispatcher dispatcher, HttpRequest request, HttpResponse response)
   {
      super(dispatcher, request, response);
   }

   @Override
   public ResteasyAsynchronousResponse suspend() throws IllegalStateException
   {
      return suspend(-1);
   }

   @Override
   public ResteasyAsynchronousResponse suspend(long millis) throws IllegalStateException
   {
      return suspend(millis, TimeUnit.MILLISECONDS);
   }

   @Override
   public ResteasyAsynchronousResponse suspend(long time, TimeUnit unit) throws IllegalStateException
   {
      wasSuspended = true;
      asynchronousResponse = new SynchronousAsynchronousResponse(dispatcher, request, response);
      asynchronousResponse.setTimeout(time, unit);
      return asynchronousResponse;
   }

   @Override
   public ResteasyAsynchronousResponse getAsyncResponse()
   {
      return asynchronousResponse;
   }

   @Override
   public boolean isSuspended()
   {
      return wasSuspended;
   }

   protected class SynchronousAsynchronousResponse extends AbstractAsynchronousResponse
   {
      protected boolean cancelled;

      public SynchronousAsynchronousResponse(SynchronousDispatcher dispatcher, HttpRequest request, HttpResponse response)
      {
         super(dispatcher, request, response);
      }



      @Override
      public boolean resume(Object entity)
      {
         synchronized (responseLock)
         {
            if (done) return false;
            if (cancelled) return false;
            try
            {
               return internalResume(entity);
            }
            finally
            {
               done = true;
               syncLatch.countDown();
            }
         }
      }


      @Override
      public boolean resume(Throwable exc) throws IllegalStateException
      {
         synchronized (responseLock)
         {
            if (done) return false;
            if (cancelled) return false;
            try
            {
               return internalResume(exc);
            }
            finally
            {
               done = true;
               syncLatch.countDown();
            }
         }
      }

      @Override
      public void initialRequestThreadFinished()
      {
         if (!wasSuspended) return;

         boolean result = false;
         try
         {
            if (timeout <= 0)
            {
               syncLatch.await();
               result = true;
            }
            else
            {
               result = syncLatch.await(timeout, timeoutUnit);
            }
         }
         catch (InterruptedException e)
         {

         }
         if (result == false)
         {
            synchronized (responseLock)
            {
               if (!done)
               {
                  if (timeoutHandler != null)
                  {
                     timeoutHandler.handleTimeout(this);
                  }
                  if (!done)
                  {
                     try
                     {
                        internalResume(Response.status(503).build());
                     }
                     catch (Exception e)
                     {
                        throw new UnhandledException(e);
                     }
                     finally
                     {
                        done = true;
                     }
                  }
               }
            }
         }
      }

      @Override
      public boolean setTimeout(long time, TimeUnit unit) throws IllegalStateException
      {
         timeout = time;
         timeoutUnit = unit;
         if (done || cancelled) return false;
         return true;
      }

      @Override
      public boolean cancel()
      {
         synchronized (responseLock)
         {
            if (cancelled) return true;
            if (done) return false;
            done = true;
            cancelled = true;
         }
         return internalResume(Response.status(Response.Status.SERVICE_UNAVAILABLE).build());
      }

      @Override
      public boolean cancel(int retryAfter)
      {
         synchronized (responseLock)
         {
            if (cancelled) return true;
            if (done) return false;
            done = true;
            cancelled = true;
         }
         return internalResume(Response.status(Response.Status.SERVICE_UNAVAILABLE).header(HttpHeaders.RETRY_AFTER, retryAfter).build());
      }

      @Override
      public boolean cancel(Date retryAfter)
      {
         synchronized (responseLock)
         {
            if (cancelled) return true;
            if (done) return false;
            done = true;
            cancelled = true;
         }
         return internalResume(Response.status(Response.Status.SERVICE_UNAVAILABLE).header(HttpHeaders.RETRY_AFTER, retryAfter).build());
      }

      @Override
      public boolean isSuspended()
      {
         return !done;
      }

      @Override
      public boolean isCancelled()
      {
         return cancelled;
      }

      @Override
      public boolean isDone()
      {
         return done;
      }

   }


}
