package com.millipad.util;

public class SharedLock {

	public static final int OKAY = 0;
	public static final int TIMED_OUT = 1;

	int[] shared, exclusive;

	public SharedLock(int shared, int exclusive) {init(shared, exclusive);}
	
	public SharedLock() {init(0, 0);}

	private static void debug(Object in) {Debug.print(SharedLock.class, in);}

	private void init(int shared, int exclusive) {
		this.shared = new int[] {shared};
		this.exclusive = new int[] {exclusive};
		}

	public int acquireShared() throws InterruptedException {
		return acquireShared(1, Long.MAX_VALUE);
		}

	public int acquireShared(int count, long timeout) throws InterruptedException {
		int taken = 0;
		try {
			timeout += System.currentTimeMillis();
			for(; count>0; count--, taken++) {
				long t1 = timeout - System.currentTimeMillis();
				if(t1<=0 || acquireShared(t1)==TIMED_OUT) {
					releaseShared(taken);
					return TIMED_OUT;
					}
				}
			}
		catch(InterruptedException e) {
			releaseShared(taken);
			throw e;
			}
		return OKAY;
		}

	public int acquireShared(long timeout) throws InterruptedException {

		timeout+=System.currentTimeMillis();
		synchronized(this) {
			down(exclusive, timeout-System.currentTimeMillis());
			up(exclusive, 1);
			up(shared, 1);
			}
		return OKAY;

		}
	
	public void releaseShared() {releaseShared(1);}

	public void releaseShared(int count) {
		try {
			synchronized(this) {
				for(int i=count; i>0; i--) down(shared, Long.MAX_VALUE);
				}
			}
		catch(InterruptedException e) {
			}
		}

	public int acquireExclusive() throws InterruptedException {
		return acquireExclusive(1, Long.MAX_VALUE);
		}

	public int acquireExclusive(int count, long timeout) throws InterruptedException {
		int taken = 0;
		try {
			timeout += System.currentTimeMillis();
			for(; count>0; count--, taken++) {
				long t1 = timeout - System.currentTimeMillis();
				if(t1<=0 || acquireExclusive(t1)==TIMED_OUT) {
					releaseExclusive(taken);
					return TIMED_OUT;
					}
				}
			}
		catch(InterruptedException e) {
			releaseExclusive(taken);
			throw e;
			}
		return OKAY;
		}

	public int acquireExclusive(long timeout) throws InterruptedException {

		timeout += System.currentTimeMillis();
		int result = down(exclusive, timeout-System.currentTimeMillis());
		if(result!=OKAY) return result;
		synchronized(shared) {
			for(; shared[0]>0;) {
				long t1 = timeout - System.currentTimeMillis();
				if(t1<=0) return TIMED_OUT;
				shared.wait(t1);
				}
			}
		return OKAY;

		}

	public void releaseExclusive() {releaseExclusive(1);}

	public void releaseExclusive(int count) {
		up(exclusive, count);
		}

	private int down(int[] counter, long timeout) throws InterruptedException {
		timeout += System.currentTimeMillis();
		synchronized(counter) {
			for(;counter[0]<=0;) {
				long t1 = timeout - System.currentTimeMillis();
				if(t1<=0) return TIMED_OUT;
				counter.wait(t1);
				counter.notify();
				}
			counter[0]--;
			}
		return OKAY;
		}

	private void up(int[] counter, int count) {
		synchronized(counter) {
			counter[0]+=count;
			counter.notify();
			}
		}

	}
