package org.broadinstitute.sting.utils.threading;

import org.apache.log4j.Logger;
import org.broadinstitute.sting.utils.exceptions.ReviewedStingException;
import org.broadinstitute.sting.utils.exceptions.UserException;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.*;

/**
 * User: depristo
 * Date: 1/19/11
 * Time: 8:24 AM
 *
 * A reentrant lock that supports multi-threaded locking as well as a shared file lock on a common
 * file in the file system.  It itself a shared memory reenterant lock to managed thread safety and
 * contains a SharedFileLock to handle the file integrity.
 */
public class SharedFileThreadSafeLock extends ClosableReentrantLock {
    private static Logger logger = Logger.getLogger(SharedFileThreadSafeLock.class);
    protected static final boolean DEBUG = false;

    private final SharedFileLock fileLock;

    /**
     * Create a SharedFileThreadSafeLock object locking the file
     * @param file
     */
    public SharedFileThreadSafeLock(File file, int nRetries, long milliSecPerTry, int ID) {
        super();
        this.fileLock = new SharedFileLock(file, nRetries, milliSecPerTry, ID);
    }

    public SharedFileThreadSafeLock(File file, int ID) {
        this(file, SharedFileLock.DEFAULT_N_TRIES, SharedFileLock.DEFAULT_MILLISECONDS_PER_TRY, ID);
    }

    @Override
    public void close() {
        super.close();
        fileLock.close();
    }

    @Override
    public int getHoldCount() {
        if ( super.getHoldCount() != fileLock.getHoldCount() )
            throw new ReviewedStingException("BUG: unequal hold counts.  threadlock = " + super.getHoldCount() + ", filelock = " + fileLock.getHoldCount());
        return super.getHoldCount();
    }

    @Override
    public boolean ownsLock() {
        return super.isHeldByCurrentThread() && fileLock.ownsLock();
    }

    /**
     * Two stage [threading then file] locking mechanism.  Reenterant in that multiple lock calls will be
     * unwound appropriately.  Uses file channel lock *after* thread locking.
     */
    @Override
    public void lock() {
        if ( DEBUG ) logger.warn("Attempting SharedFileThreadSafe lock: " + Thread.currentThread().getName());
        if ( DEBUG ) logger.warn("  going for thread lock: " + Thread.currentThread().getName());
        super.lock();
        if ( DEBUG ) logger.warn("  going for file lock: " + Thread.currentThread().getName());
        fileLock.lock(); // todo -- should this be in a try?
    }

    @Override
    public void unlock() {
        if ( DEBUG ) logger.warn("  releasing filelock: " + Thread.currentThread().getName());
        fileLock.unlock();
        if ( DEBUG ) logger.warn("  releasing threadlock: " + Thread.currentThread().getName());
        super.unlock();
        if ( DEBUG ) logger.warn("  unlock() complete: " + Thread.currentThread().getName());
    }
}
