/*
 * Copyright 2003-2011 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package jetbrains.mps.util.test;

import jetbrains.mps.util.FileUtil;
import jetbrains.mps.util.PathUtil;
import jetbrains.mps.util.test.Checker.Result;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.IOError; import java.util.ArrayList; import java.util.List;

public final class CachesUtil {
  private static final String PROPERTY_CONFIG_PATH = "idea.config.path";
  private static final String PROPERTY_SYSTEM_PATH = "idea.system.path";
  public static final String REUSE_CACHES_DIR = "mps.test.reuse_caches_dir";
  public static final String SAVE_CACHES_DIR = "mps.test.save_caches_dir";

  private static final List<File> TO_REMOVE = new ArrayList<File>();

  // we need to check that caches dirs are writable
  // idea does not have the necessary api, @see our PathManager class
  public static void setupCaches() {
    final Checker cachesPathChecker = new CachesPathChecker(System.getProperty(PROPERTY_CONFIG_PATH), System.getProperty(PROPERTY_SYSTEM_PATH));
    final Checker reusableCachesChecker = new MPSReusableCachesChecker();
    final Checker saveCachesChecker = new MPSSaveCachesChecker();
    final Checker oneTimeCachesChecker = new MPSOneTimeCachesChecker();
    Checker[] checkers = {cachesPathChecker, reusableCachesChecker, saveCachesChecker, oneTimeCachesChecker};

    Result result = null;
    for (Checker checker : checkers) {
      result = checker.check();
      if (result.isSuccessful()) break;
    }
    if (result.isSuccessful()) {
      System.out.println("Saving ''config'' caches in the " + result.getConfigPath() + " directory");
      System.out.println("Saving ''system'' caches in the " + result.getSystemPath() + " directory");
      System.setProperty(PROPERTY_CONFIG_PATH, result.getConfigPath());
      System.setProperty(PROPERTY_SYSTEM_PATH, result.getSystemPath());
    } else {
      throw new IOError(new Throwable("Cannot find a place to write caches"));
    }
  }

  public static void cleanupCaches() {
    for (File it : TO_REMOVE) {
      try {
        FileUtil.delete(it);
      } catch (Throwable t) {
        t.printStackTrace();
      }
    }
    TO_REMOVE.clear();
  }

  private static class CachesPathChecker implements Checker {
    private final String myConfigPath;
    private final String mySystemPath;

    public CachesPathChecker(@Nullable String configPath, @Nullable String systemPath) {
      myConfigPath = configPath;
      mySystemPath = systemPath;
    }

    @Override
    public Result check() {
      boolean result = canWrite(myConfigPath);
      result &= canWrite(mySystemPath);
      return Result.create(result, myConfigPath, mySystemPath);
    }

    private static boolean canWrite(@Nullable String path) {
      if (path == null)
        return false;
      path = PathUtil.trimPathQuotes(path);
      path = PathUtil.getAbsolutePath(path);
      return FileUtil.canWrite(new File(path));
    }
  }

  private static class MPSReusableCachesChecker implements Checker {

    public MPSReusableCachesChecker() {
    }

    @Override
    public Result check() {
      final String cachesDir = System.getProperty(REUSE_CACHES_DIR);
      if (cachesDir == null) {
        return Result.UNSUCCESSFUL;
      }
      File testConfigPath = new File(cachesDir, "config");
      File testSystemPath = new File(cachesDir, "system");
      if (FileUtil.canWrite(testConfigPath) && FileUtil.canWrite(testSystemPath)) {
        testConfigPath.mkdirs();
        testSystemPath.mkdir();
        return Result.create(true, testConfigPath.getAbsolutePath(), testSystemPath.getAbsolutePath());
      }
      return Result.UNSUCCESSFUL;
    }
  }

  private static class MPSSaveCachesChecker implements Checker {

    public MPSSaveCachesChecker() {
    }

    @Override
    public Result check() {
      final String cachesDir = System.getProperty(SAVE_CACHES_DIR);
      if (cachesDir == null) {
        return Result.UNSUCCESSFUL;
      }
      File testConfigPath = new File(cachesDir, "config");
      File testSystemPath = new File(cachesDir, "system");
      if (FileUtil.canWrite(testConfigPath) && FileUtil.canWrite(testSystemPath)) {
        testConfigPath.mkdirs();
        testSystemPath.mkdir();
        cleanFolders(testConfigPath, testSystemPath);
        return Result.create(true, testConfigPath.getAbsolutePath(), testSystemPath.getAbsolutePath());
      }
      return Result.UNSUCCESSFUL;
    }

    private void cleanFolders(File testConfigPath, File testSystemPath) {
      System.out.println("Cleaning the cache directory");
      FileUtil.clear(testConfigPath);
      FileUtil.clear(testSystemPath);
    }
  }

  private static class MPSOneTimeCachesChecker implements Checker {
    public MPSOneTimeCachesChecker() {
    }

    @Override
    public Result check() {
      final String configPath = setTempFolder();
      final String systemPath = setTempFolder();

      boolean result = canWrite(configPath);
      result &= canWrite(systemPath);
      return Result.create(result, configPath, systemPath);
    }

    private static boolean canWrite(@Nullable String path) {
      return FileUtil.canWrite(new File(path));
    }

    private static String setTempFolder() {
      File tmpDir = FileUtil.createTmpDir();
      TO_REMOVE.add(tmpDir);
      return tmpDir.getAbsolutePath();
    }
  }
}
