package phex.download;

import java.util.concurrent.locks.AbstractQueuedSynchronizer;

import phex.download.swarming.SwarmingManager;

public class BufferVolumeTracker
{
    private static final DownloadDataWriter dataWriter = 
        SwarmingManager.getInstance().getDownloadDataWriter();
    
    private BufferVolumeTracker parent;
    private Sync sync;
    
    public BufferVolumeTracker( int maxBufferSize )
    {
        this( null, maxBufferSize  );
    }
    
    public BufferVolumeTracker( BufferVolumeTracker parent, int maxBufferSize )
    {
        this.parent = parent;
        sync = new Sync( maxBufferSize );
    }
    
    public int getUsedBufferSize()
    {
        return sync.getUsedStates();
    }
    public int getFreeBufferSize()
    {
        return sync.getFreeStates();
    }
    
    public void addBufferedSize( int amount )
    {
        if ( parent != null )
        {
            parent.addBufferedSize(amount);
        }
        sync.acquireShared(amount);
    }
    
    public void reduceBufferedSize( int amount )
    {
        if ( parent != null )
        {
            parent.reduceBufferedSize(amount);
        }
        sync.releaseShared(amount);
    }
    
    class Sync extends AbstractQueuedSynchronizer 
    {
        private int currentMax;
        
        Sync( int max )
        {
            // TODO modification of this setting are not reflected.
            currentMax = max;
            setState( max );
        }
        
        public int getUsedStates()
        {
            return currentMax - getState();
        }
        
        public int getFreeStates()
        {
            return getState();
        }
        
        protected final int tryAcquireShared( int acquires ) 
        {
            for (;;) 
            {
                int available = getState();
                int remaining = available - acquires;
                if ( remaining < 0 )
                {
                    dataWriter.triggerWriteCycle();
                    return remaining;
                }
                if ( compareAndSetState(available, remaining) )
                {
                    return remaining;
                }
            }
        }
        
        protected final boolean tryReleaseShared(int releases)
        {
            for (;;) {
                int p = getState();
                if (compareAndSetState(p, p + releases)) 
                    return true;
            }
        }
    }
}
