/*
 * Copyright 2000-2013 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 com.intellij.dvcs.repo;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Callable;

/**
 * @author Nadya Zabrodina
 */
public class RepositoryUtil {

  private static final Logger LOGGER = Logger.getInstance(RepositoryUtil.class);
  private static final int IO_RETRIES = 3; // number of retries before fail if an IOException happens during file read.

  public static void assertFileExists(File file, String message) {
    if (!file.exists()) {
      throw new RepoStateException(message);
    }
  }

  /**
   * Loads the file content.
   * Tries 3 times, then a {@link RepoStateException} is thrown.
   * Content is then trimmed and line separators get converted.
   *
   * @param file File to read.
   * @return file content.
   */
  @NotNull
  public static String tryLoadFile(@NotNull final File file) {
    return tryOrThrow(new Callable<String>() {
      @Override
      public String call() throws Exception {
        return StringUtil.convertLineSeparators(FileUtil.loadFile(file).trim());
      }
    }, file);
  }

  /**
   * Tries to execute the given action.
   * If an IOException happens, tries again up to 3 times, and then throws a {@link RepoStateException}.
   * If an other exception happens, rethrows it as a {@link RepoStateException}.
   * In the case of success returns the result of the task execution.
   */
  public static <T> T tryOrThrow(Callable<T> actionToTry, File fileToLoad) {
    IOException cause = null;
    for (int i = 0; i < IO_RETRIES; i++) {
      try {
        return actionToTry.call();
      }
      catch (IOException e) {
        LOGGER.info("IOException while loading " + fileToLoad, e);
        cause = e;
      }
      catch (Exception e) {    // this shouldn't happen since only IOExceptions are thrown in clients.
        throw new RepoStateException("Couldn't load file " + fileToLoad, e);
      }
    }
    throw new RepoStateException("Couldn't load file " + fileToLoad, cause);
  }

  public static void visitVcsDirVfs(@NotNull VirtualFile gitDir, @NotNull Collection<String> subDirs) {
    gitDir.getChildren();
    for (String subdir : subDirs) {
      VirtualFile dir = gitDir.findFileByRelativePath(subdir);
      // process recursively, because we need to visit all branches under refs/heads and refs/remotes
      visitAllChildrenRecursively(dir);
    }
  }

  public static void visitAllChildrenRecursively(@Nullable VirtualFile dir) {
    if (dir == null) {
      return;
    }
    VfsUtil.processFilesRecursively(dir, new Processor<VirtualFile>() {
      @Override
      public boolean process(VirtualFile virtualFile) {
        return true;
      }
    });
  }

  public static class Updater implements Consumer<Object> {
    private final Repository myRepository;

    public Updater(Repository repository) {
      myRepository = repository;
    }

    @Override
    public void consume(Object dummy) {
      if (!Disposer.isDisposed(myRepository)) {
        myRepository.update();
      }
    }
  }

  public static <T extends Repository> List<T> sortRepositories(@NotNull Collection<T> repositories) {
    List<T> repos = ContainerUtil.filter(repositories, new Condition<T>() {
      @Override
      public boolean value(T t) {
        return t.getRoot().isValid();
      }
    });
    Collections.sort(repos, new Comparator<Repository>() {
      @Override
      public int compare(Repository o1, Repository o2) {
        return o1.getPresentableUrl().compareTo(o2.getPresentableUrl());
      }
    });
    return repos;
  }
}

@@ -892,24 +892,28 @@
 .vfs.VfsUtil
+Core
 ;%0Aimport com
@@ -3680,17 +3680,17 @@
 if (dir 
-=
+!
 = null) 
@@ -3701,22 +3701,35 @@
     
-return;%0A    %7D%0A
+//noinspection unchecked%0A  
     
@@ -3735,16 +3735,20 @@
  VfsUtil
+Core
 .process
@@ -3768,20 +3768,16 @@
 ely(dir,
- new
  Process
@@ -3782,134 +3782,21 @@
 ssor
-%3CVirtualFile%3E() %7B%0A      @Override%0A      public boolean process(VirtualFile virtualFile) %7B%0A        return true;%0A      %7D%0A    %7D);
+.TRUE);%0A    %7D
 %0A  %7D

use existing constant