/*
 * Copyright 2011 LMAX Ltd.
 *
 * 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.lmax.disruptor;

import static com.lmax.disruptor.util.Util.getMinimumSequence;

import java.util.concurrent.locks.LockSupport;

import com.lmax.disruptor.util.PaddedLong;
import com.lmax.disruptor.util.Util;


/**
 * Coordinator for claiming sequences for access to a data structure while tracking dependent {@link Sequence}s
 */
class SingleProducerSequencer implements Sequencer
{
    /** Set to -1 as sequence starting point */
    private final PaddedLong minGatingSequence = new PaddedLong(Sequencer.INITIAL_CURSOR_VALUE);
    private final Sequence cursor = new Sequence(SingleProducerSequencer.INITIAL_CURSOR_VALUE);
    private Sequence[] gatingSequences;

    private final WaitStrategy waitStrategy;

    private final int bufferSize;

    /**
     * Construct a Sequencer with the selected wait strategy and buffer size.
     *
     * @param bufferSize the size of the buffer that this will sequence over.
     * @param waitStrategy for those waiting on sequences.
     */
    public SingleProducerSequencer(int bufferSize, final WaitStrategy waitStrategy)
    {
        this.bufferSize = bufferSize;
        this.waitStrategy = waitStrategy;
    }

    @Override
    public void setGatingSequences(final Sequence... sequences)
    {
        this.gatingSequences = sequences;
    }

    @Override
    public int getBufferSize()
    {
        return bufferSize;
    }

    @Override
    public long getCursor()
    {
        return cursor.get();
    }

    @Override
    public boolean hasAvailableCapacity(final int requiredCapacity)
    {
        return hasAvailableCapacity(requiredCapacity, gatingSequences);
    }

    @Override
    public long next()
    {
        if (null == gatingSequences)
        {
            throw new NullPointerException("gatingSequences must be set before claiming sequences");
        }

        long nextSequence = cursor.get() + 1;
        waitForFreeSlotAt(nextSequence, gatingSequences);
        cursor.set(nextSequence);
        
        return nextSequence;
    }

    @Override
    public long tryNext() throws InsufficientCapacityException
    {
        if (null == gatingSequences)
        {
            throw new NullPointerException("gatingSequences must be set before claiming sequences");
        }

        if (!hasAvailableCapacity(1, gatingSequences))
        {
            throw InsufficientCapacityException.INSTANCE;
        }

        long nextSequence = cursor.get() + 1;
        cursor.set(nextSequence);
        
        return nextSequence;
    }

    @Override
    public long claim(final long sequence)
    {
        if (null == gatingSequences)
        {
            throw new NullPointerException("gatingSequences must be set before claiming sequences");
        }

        cursor.set(sequence);
        waitForFreeSlotAt(sequence, gatingSequences);

        return sequence;
    }

    @Override
    public long remainingCapacity()
    {
        long consumed = Util.getMinimumSequence(gatingSequences);
        long produced = cursor.get();
        return getBufferSize() - (produced - consumed);
    }
    
    private void waitForFreeSlotAt(final long sequence, final Sequence[] dependentSequences)
    {
        final long wrapPoint = sequence - bufferSize;
        if (wrapPoint > minGatingSequence.get())
        {
            long minSequence;
            while (wrapPoint > (minSequence = getMinimumSequence(dependentSequences)))
            {
                LockSupport.parkNanos(1L); // TODO: Use waitStrategy to spin?
            }

            minGatingSequence.set(minSequence);
        }
    }

    private boolean hasAvailableCapacity(final int requiredCapacity, final Sequence[] dependentSequences)
    {
        final long wrapPoint = (cursor.get() + requiredCapacity) - bufferSize;
        if (wrapPoint > minGatingSequence.get())
        {
            long minSequence = getMinimumSequence(dependentSequences);
            minGatingSequence.set(minSequence);

            if (wrapPoint > minSequence)
            {
                return false;
            }
        }

        return true;
    }
}

@@ -1115,32 +1115,34 @@
 _CURSOR_VALUE);%0A
+//
     private fina
@@ -1175,29 +1175,74 @@
 quence(S
-ingleProducer
+equencer.INITIAL_CURSOR_VALUE);%0A    private long cursor = 
 Sequence
@@ -1255,33 +1255,32 @@
 IAL_CURSOR_VALUE
-)
 ;%0A    private Se
@@ -2060,32 +2060,34 @@
 tCursor()%0A    %7B%0A
+//
         return c
@@ -2095,24 +2095,47 @@
 rsor.get();%0A
+        return cursor;%0A
     %7D%0A%0A    @
@@ -2524,38 +2524,32 @@
 equence = cursor
-.get()
  + 1;%0A        wa
@@ -2602,37 +2602,35 @@
 ;%0A        cursor
-.set(
+ = 
 nextSequence);%0A 
@@ -2617,33 +2617,32 @@
 r = nextSequence
-)
 ;%0A        %0A     
@@ -3082,22 +3082,16 @@
 = cursor
-.get()
  + 1;%0A  
@@ -3102,21 +3102,19 @@
   cursor
-.set(
+ = 
 nextSequ
@@ -3109,33 +3109,32 @@
 r = nextSequence
-)
 ;%0A        %0A     
@@ -3404,21 +3404,19 @@
 rsor
-.set(
+ = 
 sequence
 );%0A 
@@ -3407,25 +3407,24 @@
 r = sequence
-)
 ;%0A        wa
@@ -3648,30 +3648,24 @@
 ced = cursor
-.get()
 ;%0A        re
@@ -4326,32 +4326,32 @@
 equences)%0A    %7B%0A
+
         final lo
@@ -4372,22 +4372,16 @@
  (cursor
-.get()
  + requi

Use simple long for claim sequence on SingleProducerSequencer