package com.hammurapi.store;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * Lock which acts on behalf of another lock and acquires its own lock only
 *  if the master lock is not held. This lock is used for executing one logical 
 *  store operation in multiple threads.
 * @author Pavel Vlasov
 *
 */
public class DeputyLock implements TrackingLock {
	
	private Lock master;
	private boolean masterLocked;
	private AtomicInteger lockCounter = new AtomicInteger(0);
	private boolean canNotBeLocked;
	private String msg;

	public DeputyLock(Lock master, boolean masterLocked, boolean canNotBeLocked, String msg) {
		this.master = master;
		this.masterLocked = masterLocked;
		this.canNotBeLocked = canNotBeLocked;
		this.msg = msg;
	}
	
	private void checkLockability() {
		if (canNotBeLocked) {
			throw new IllegalStateException(msg);
		}
	}

	@Override
	public void lock() {
		if (!masterLocked) {
			checkLockability();
			master.lock();
			lockCounter.incrementAndGet();
		}
	}

	@Override
	public void lockInterruptibly() throws InterruptedException {
		if (!masterLocked) {
			checkLockability();
			master.lockInterruptibly();
			lockCounter.incrementAndGet();
		}
	}

	@Override
	public boolean tryLock() {
		if (masterLocked) {
			return true;
		}
		checkLockability();
		if (master.tryLock()) {
			lockCounter.incrementAndGet();
			return true;
		}
		return false;
	}

	@Override
	public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
		if (masterLocked) {
			return true;
		}
		checkLockability();
		if (master.tryLock(time, unit)) {
			lockCounter.incrementAndGet();
			return true;
		}
		return false;
	}

	@Override
	public void unlock() {
		if (masterLocked) {
			master.unlock();
			lockCounter.decrementAndGet();
		}
	}
	
	public boolean isLocked() {
		return masterLocked || lockCounter.get()>0;
	}

	@Override
	public Condition newCondition() {
		return master.newCondition();
	}
	
//	public DeputyLock createDeputy() {
//		return new DeputyLock(this, isLocked(), canNotBeLocked, msg);
//	}

}
