/*
 * Copyright 2000-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 git4idea.repo;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.messages.Topic;
import git4idea.GitBranch;
import git4idea.branch.GitBranchesCollection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Collection;

/**
 * <p>
 *   GitRepository is a representation of a Git repository stored under the specified directory.
 *   It stores the information about the repository, which is frequently requested by other plugin components.
 *   All get-methods (like {@link #getCurrentRevision()}) are just getters of the correspondent fields and thus are very fast.
 * </p>
 * <p>
 *   The GitRepository is updated "externally" by the {@link git4idea.repo.GitRepositoryUpdater}, when correspondent {@code .git/} service files
 *   change.
 * </p>
 * <p>
 *   To force asynchronous update, it is enough to call {@link VirtualFile#refresh(boolean, boolean) refresh} on the root directory.
 * </p>
 * <p>
 *   To make a synchronous update of the repository call {@link #update(TrackedTopic...)} and specify
 *   which topics should be updated. Updating requires reading from disk, so updating {@link GitRepository.TrackedTopic.ALL} may take some time.
 * </p>
 * <p>
 *   Other components may subscribe to GitRepository changes via the {@link #GIT_REPO_CHANGE} {@link Topic}
 *   or preferably via {@link GitRepositoryManager#addListenerToAllRepositories(GitRepositoryChangeListener)}
 * </p>
 *
 * <p>
 *   Getters and setters (update...()-methods) are not synchronized intentionally - to avoid live- and deadlocks.
 *   GitRepository is updated asynchronously,
 *   so even if the getters would have been synchronized, it wouldn't guarantee that they return actual values (as they are in .git).
 *   <br/>
 *   If one needs an up-to-date value, one should call update(TrackedTopic...) and then get...().
 *   update() is a synchronous read from .git, so it is guaranteed to query the real value.
 * </p>
 *
 * @author Kirill Likhodedov
 */
public interface GitRepository {

  Topic<GitRepositoryChangeListener> GIT_REPO_CHANGE = Topic.create("GitRepository change", GitRepositoryChangeListener.class);

  String toLogString();

  /**
   * Current state of the repository.
   */
  enum State {
    /**
     * HEAD is on branch, no merge process is in progress (and no rebase as well).
     */
    NORMAL,
    /**
     * During merge (for instance, merge failed with conflicts that weren't immediately resolved).
     */
    MERGING {
      @Override public String toString() {
        return "Merging";
      }
    },
    /**
     * During rebase.
     */
    REBASING {
      @Override public String toString() {
        return "Rebasing";
      }
    },
    /**
     * Detached HEAD state, but not during rebase (for example, manual checkout of a commit hash).
     */
    DETACHED
  }

  /**
   * GitRepository tracks the updates of some information about Git repository, caches this information and provides methods to access to
   * it. The pieces of this information are called Topics. They can be used to update the repository.
   */
  enum TrackedTopic {
    STATE,
    CURRENT_REVISION,
    CURRENT_BRANCH,
    BRANCHES,
    CONFIG,
    ALL_CURRENT,
    ALL
  }

  @NotNull
  VirtualFile getRoot();

  @NotNull
  VirtualFile getGitDir();

  @NotNull
  String getPresentableUrl();

  @NotNull
  Project getProject();

  @NotNull
  GitUntrackedFilesHolder getUntrackedFilesHolder();

  @NotNull
  State getState();

  /**
   * Returns the hash of the revision, which HEAD currently points to.
   * Returns null only in the case of a fresh repository, when no commit have been made.
   */
  @Nullable
  String getCurrentRevision();

  /**
   * Returns the current branch of this Git repository.
   * If the repository is being rebased, then the current branch is the branch being rebased (which was current before the rebase
   * operation has started).
   * Returns null, if the repository is not on a branch and not in the REBASING state.
   */
  @Nullable
  GitBranch getCurrentBranch();

  @NotNull
  GitBranchesCollection getBranches();

  @NotNull
  GitConfig getConfig();

  /**
   * Returns remotes defined in this Git repository.
   * It is different from {@link git4idea.repo.GitConfig#getRemotes()} because remotes may be defined not only in {@code .git/config},
   * but in {@code .git/remotes/} or even {@code .git/branches} as well.
   * On the other hand, it is a very old way to define remotes and we are not going to implement this until needed.
   * See <a href="http://thread.gmane.org/gmane.comp.version-control.git/182960">discussion in the Git mailing list</a> that confirms
   * that remotes a defined in {@code .git/config} only nowadays.
   * @return GitRemotes defined for this repository.
   */
  @NotNull
  Collection<GitRemote> getRemotes();

  boolean isMergeInProgress();

  boolean isRebaseInProgress();

  boolean isOnBranch();

  /**
   * @return true if current repository is "fresh", i.e. if no commits have been made yet.
   */
  boolean isFresh();

  void addListener(@NotNull GitRepositoryChangeListener listener);

  /**
   * Synchronously updates the GitRepository by reading information from the specified topics.
   */
  void update(TrackedTopic... topics);

}

@@ -2824,33 +2824,8 @@
 );%0A%0A
-  String toLogString();%0A%0A
   /*
@@ -5899,10 +5899,35 @@
 pics);%0A%0A
+  String toLogString();%0A%0A
 %7D%0A

move the method to the end