package com.github.blindpirate.gogradle.core.cache;

import com.github.blindpirate.gogradle.core.dependency.GolangDependency;
import com.github.blindpirate.gogradle.util.IOUtils;
import org.gradle.wrapper.GradleUserHomeLookup;

import javax.inject.Singleton;
import java.io.File;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.Path;
import java.util.concurrent.Callable;

import static com.github.blindpirate.gogradle.GolangPluginSetting.DEFAULT_CHARSET;
import static com.github.blindpirate.gogradle.util.IOUtils.ensureExsitAndWritable;

@Singleton
public class DefaultCacheManager implements CacheManager {
    public static final String GOPATH_CACHE_PATH = "go/gopath";
    public static final String GO_BINARAY_CACHE_PATH = "go/binary";
    public static final String GO_LOCKFILES_PATH = "go/lockfiles";
    public Path gradleHome = GradleUserHomeLookup.gradleUserHome().toPath();

    @Override
    public void ensureGlobalCacheExistAndWritable() {
        ensureExsitAndWritable(gradleHome, GOPATH_CACHE_PATH);
        ensureExsitAndWritable(gradleHome, GO_BINARAY_CACHE_PATH);
        ensureExsitAndWritable(gradleHome, GO_LOCKFILES_PATH);
    }

    @Override
    public void ensureProjectBuildCacheExist() {
    }

    @Override
    public Path getGlobalCachePath(String packageName) {
        return gradleHome.resolve(GOPATH_CACHE_PATH).resolve(packageName);
    }

    // TODO in case of package a/b/c and package a/b accessed concurrently, we should lock its root package
    @Override
    public synchronized <T> T runWithGlobalCacheLock(GolangDependency dependency, Callable<T> callable)
            throws Exception {
        FileChannel channel = null;
        FileLock lock = null;
        createPackageDirectoryIfNeccessary(dependency);
        File lockFile = createLockFileIfNecessary(dependency);
        try {
            channel = new RandomAccessFile(lockFile, "rw").getChannel();
            lock = channel.lock();
            return callable.call();
        } finally {
            if (lock != null) {
                lock.release();
            }
            if (channel != null) {
                channel.close();
            }
        }
    }

    private File createLockFileIfNecessary(GolangDependency dependency) throws UnsupportedEncodingException {
        String lockFileName = URLEncoder.encode(dependency.getName(), DEFAULT_CHARSET);
        File lockFile = gradleHome
                .resolve(GO_LOCKFILES_PATH)
                .resolve(lockFileName)
                .toFile();
        IOUtils.touch(lockFile);
        return lockFile;
    }

    private void createPackageDirectoryIfNeccessary(GolangDependency dependency) {
        Path path = getGlobalCachePath(dependency.getName());
        IOUtils.ensureExistAndWritable(path);
    }
}

@@ -662,28 +662,31 @@
 Utils.ensure
-Exsi
+DirExis
 tAndWritable
@@ -958,29 +958,30 @@
 iles%22;%0A    p
-ublic
+rivate
  Path gradle
@@ -1110,36 +1110,39 @@
 %7B%0A        ensure
-Exsi
+DirExis
 tAndWritable(gra
@@ -1176,36 +1176,39 @@
 ;%0A        ensure
-Exsi
+DirExis
 tAndWritable(gra
@@ -1258,12 +1258,15 @@
 sure
-Exsi
+DirExis
 tAnd
@@ -2908,16 +2908,19 @@
 s.ensure
+Dir
 ExistAnd

fix method name typo