package io.github.hcoona.utils;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;

import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.ReentrantLock;

public class NamedMutexFileLockImpl extends NamedMutex {
  private static final ConcurrentHashMap<String, ReentrantLock> INTERNAL_LOCK_CACHE =
      new ConcurrentHashMap<>();

  private final FileChannel lockFileChannel;
  private final ScheduledExecutorService executor;
  private FileLock lock;
  private final ReentrantLock internal_lock;
  private boolean disposed = false;

  @VisibleForTesting
  NamedMutexFileLockImpl(String name) throws IOException {
    this(false, name);
  }

  @VisibleForTesting
  NamedMutexFileLockImpl(boolean initiallyOwned, String name) throws IOException {
    Path lockFilePath = Paths.get(System.getProperty("java.io.tmpdir"), name + ".lock");
    this.executor = Executors.newSingleThreadScheduledExecutor();

    if (!INTERNAL_LOCK_CACHE.containsKey(name)) {
      INTERNAL_LOCK_CACHE.putIfAbsent(name, new ReentrantLock());
    }
    this.internal_lock = INTERNAL_LOCK_CACHE.get(name);

    this.lockFileChannel = FileChannel.open(lockFilePath,
        StandardOpenOption.CREATE,
        StandardOpenOption.READ,
        StandardOpenOption.WRITE);
    if (internal_lock.tryLock()) {
      try {
        this.lock = lockFileChannel.tryLock();
      } finally {
        if (lock == null || !lock.isValid()) {
          internal_lock.unlock();
        }
      }
    }
  }

  @Override
  public boolean waitOne() throws Exception {
    Preconditions.checkState(!disposed);

    internal_lock.lockInterruptibly();
    try {
      if (lock == null) {
        lock = lockFileChannel.lock();
      } else if (!lock.isValid()) {
        lock = lockFileChannel.lock();
      } else {
        internal_lock.unlock();
      }

      return true;
    } finally {
      if (lock == null || !lock.isValid()) {
        internal_lock.unlock();
      }
    }
  }

  @Override
  public boolean waitOne(long interval, TimeUnit intervalTimeUnit) throws Exception {
    Preconditions.checkState(!disposed);
    Preconditions.checkArgument(interval >= -1);

    if (internal_lock.tryLock(interval, intervalTimeUnit)) {
      try {
        if (lock != null && lock.isValid()) {
          internal_lock.unlock();
          return true;
        } else {
          ScheduledFuture<FileLock> lockFuture = executor.schedule(
              (Callable<FileLock>) lockFileChannel::lock,
              interval, intervalTimeUnit);

          try {
            lock = lockFuture.get(interval, intervalTimeUnit);
            return lock.isValid();
          } catch (TimeoutException ignored) {
            lockFuture.cancel(true);
            return false;
          }
        }
      } finally {
        if (lock == null || !lock.isValid()) {
          internal_lock.unlock();
        }
      }
    } else {
      return false;
    }
  }

  @Override
  public void release() throws Exception {
    Preconditions.checkState(!disposed);

    if (internal_lock.tryLock()) {
      try {
        if (lock != null && lock.isValid()) {
          lock.release();
          internal_lock.unlock();
        }
      } finally {
        internal_lock.unlock();
      }
    }
  }

  @Override
  public void close() throws Exception {
    if (!disposed) {
      release();
      lockFileChannel.close();
      disposed = true;
    }
  }
}

@@ -1018,18 +1018,17 @@
 internal
-_l
+L
 ock;%0A  p
@@ -1277,16 +1277,22 @@
 n %7B%0A    
+final 
 Path loc
@@ -1368,16 +1368,17 @@
 lock%22);%0A
+%0A
     this
@@ -1575,18 +1575,17 @@
 internal
-_l
+L
 ock = IN
@@ -1783,34 +1783,33 @@
     if (internal
-_l
+L
 ock.tryLock()) %7B
@@ -1943,34 +1943,33 @@
         internal
-_l
+L
 ock.unlock();%0A  
@@ -2103,26 +2103,25 @@
     internal
-_l
+L
 ock.lockInte
@@ -2306,34 +2306,33 @@
         internal
-_l
+L
 ock.unlock();%0A  
@@ -2426,34 +2426,33 @@
         internal
-_l
+L
 ock.unlock();%0A  
@@ -2665,34 +2665,33 @@
     if (internal
-_l
+L
 ock.tryLock(inte
@@ -2785,34 +2785,33 @@
         internal
-_l
+L
 ock.unlock();%0A  
@@ -3339,34 +3339,33 @@
         internal
-_l
+L
 ock.unlock();%0A  
@@ -3533,26 +3533,25 @@
 if (internal
-_l
+L
 ock.tryLock(
@@ -3649,34 +3649,33 @@
         internal
-_l
+L
 ock.unlock();%0A  
@@ -3720,10 +3720,9 @@
 rnal
-_l
+L
 ock.

Fix CheckStyle complain.