/*
 * Copyright 2000-2010 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 org.jetbrains.plugins.github;

import com.intellij.ide.BrowserUtil;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.GitLocalBranch;
import git4idea.GitRemoteBranch;
import git4idea.GitUtil;
import git4idea.repo.GitRepository;
import git4idea.repo.GitRepositoryManager;
import icons.GithubIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.github.util.GithubNotifications;
import org.jetbrains.plugins.github.util.GithubUrlUtil;
import org.jetbrains.plugins.github.util.GithubUtil;

import static org.jetbrains.plugins.github.util.GithubUtil.setVisibleEnabled;

/**
 * Created by IntelliJ IDEA.
 *
 * @author oleg
 * @date 12/10/10
 */
public class GithubOpenInBrowserAction extends DumbAwareAction {
  public static final String CANNOT_OPEN_IN_BROWSER = "Cannot open in browser";

  protected GithubOpenInBrowserAction() {
    super("Open on GitHub", "Open corresponding link in browser", GithubIcons.Github_icon);
  }

  @Override
  public void update(final AnActionEvent e) {
    Project project = e.getData(CommonDataKeys.PROJECT);
    VirtualFile virtualFile = e.getData(CommonDataKeys.VIRTUAL_FILE);
    if (project == null || project.isDefault() || virtualFile == null) {
      setVisibleEnabled(e, false, false);
      return;
    }
    GitRepositoryManager manager = GitUtil.getRepositoryManager(project);

    final GitRepository gitRepository = manager.getRepositoryForFile(virtualFile);
    if (gitRepository == null) {
      setVisibleEnabled(e, false, false);
      return;
    }

    if (!GithubUtil.isRepositoryOnGitHub(gitRepository)) {
      setVisibleEnabled(e, false, false);
      return;
    }

    ChangeListManager changeListManager = ChangeListManager.getInstance(project);
    if (changeListManager.isUnversioned(virtualFile)) {
      setVisibleEnabled(e, true, false);
      return;
    }

    Change change = changeListManager.getChange(virtualFile);
    if (change != null && change.getType() == Change.Type.NEW) {
      setVisibleEnabled(e, true, false);
      return;
    }

    setVisibleEnabled(e, true, true);
  }

  @Override
  public void actionPerformed(final AnActionEvent e) {
    final Project project = e.getData(CommonDataKeys.PROJECT);
    final VirtualFile virtualFile = e.getData(CommonDataKeys.VIRTUAL_FILE);
    final Editor editor = e.getData(CommonDataKeys.EDITOR);
    if (virtualFile == null || project == null || project.isDisposed()) {
      return;
    }

    String urlToOpen = getGithubUrl(project, virtualFile, editor);
    if (urlToOpen != null) {
      BrowserUtil.browse(urlToOpen);
    }
  }

  @Nullable
  public static String getGithubUrl(@NotNull Project project, @NotNull VirtualFile virtualFile, @Nullable Editor editor) {

    GitRepositoryManager manager = GitUtil.getRepositoryManager(project);
    final GitRepository repository = manager.getRepositoryForFile(virtualFile);
    if (repository == null) {
      StringBuilder details = new StringBuilder("file: " + virtualFile.getPresentableUrl() + "; Git repositories: ");
      for (GitRepository repo : manager.getRepositories()) {
        details.append(repo.getPresentableUrl()).append("; ");
      }
      GithubNotifications.showError(project, CANNOT_OPEN_IN_BROWSER, "Can't find git repository", details.toString());
      return null;
    }

    final String githubRemoteUrl = GithubUtil.findGithubRemoteUrl(repository);
    if (githubRemoteUrl == null) {
      GithubNotifications.showError(project, CANNOT_OPEN_IN_BROWSER, "Can't find github remote");
      return null;
    }

    final String rootPath = repository.getRoot().getPath();
    final String path = virtualFile.getPath();
    if (!path.startsWith(rootPath)) {
      GithubNotifications
        .showError(project, CANNOT_OPEN_IN_BROWSER, "File is not under repository root", "Root: " + rootPath + ", file: " + path);
      return null;
    }

    String branch = getBranchNameOnRemote(project, repository);
    if (branch == null) {
      return null;
    }

    String relativePath = path.substring(rootPath.length());
    String urlToOpen = makeUrlToOpen(editor, relativePath, branch, githubRemoteUrl);
    if (urlToOpen == null) {
      GithubNotifications.showError(project, CANNOT_OPEN_IN_BROWSER, "Can't create properly url", githubRemoteUrl);
      return null;
    }

    return urlToOpen;
  }

  @Nullable
  private static String makeUrlToOpen(@Nullable Editor editor,
                                      @NotNull String relativePath,
                                      @NotNull String branch,
                                      @NotNull String githubRemoteUrl) {
    final StringBuilder builder = new StringBuilder();
    final String githubRepoUrl = GithubUrlUtil.makeGithubRepoUrlFromRemoteUrl(githubRemoteUrl);
    if (githubRepoUrl == null) {
      return null;
    }
    builder.append(githubRepoUrl).append("/blob/").append(branch).append(relativePath);

    if (editor != null && editor.getDocument().getLineCount() >= 1) {
      // lines are counted internally from 0, but from 1 on github
      SelectionModel selectionModel = editor.getSelectionModel();
      final int begin = editor.getDocument().getLineNumber(selectionModel.getSelectionStart()) + 1;
      final int selectionEnd = selectionModel.getSelectionEnd();
      int end = editor.getDocument().getLineNumber(selectionEnd) + 1;
      if (editor.getDocument().getLineStartOffset(end - 1) == selectionEnd) {
        end -= 1;
      }
      builder.append("#L").append(begin).append('-').append(end);
    }

    return builder.toString();
  }

  @Nullable
  public static String getBranchNameOnRemote(@NotNull Project project, @NotNull GitRepository repository) {
    GitLocalBranch currentBranch = repository.getCurrentBranch();
    if (currentBranch == null) {
      GithubNotifications.showError(project, CANNOT_OPEN_IN_BROWSER,
                                    "Can't open the file on GitHub when repository is on detached HEAD. Please checkout a branch.");
      return null;
    }

    GitRemoteBranch tracked = currentBranch.findTrackedBranch(repository);
    if (tracked == null) {
      GithubNotifications
        .showError(project, CANNOT_OPEN_IN_BROWSER, "Can't open the file on GitHub when current branch doesn't have a tracked branch.",
                   "Current branch: " + currentBranch + ", tracked info: " + repository.getBranchTrackInfos());
      return null;
    }

    return tracked.getNameForRemoteOperations();
  }

}
@@ -975,16 +975,66 @@
 roject;%0A
+import com.intellij.openapi.util.text.StringUtil;%0A
 import c
@@ -5924,24 +5924,164 @@
 null;%0A    %7D%0A
+    if (StringUtil.isEmptyOrSpaces(relativePath)) %7B%0A      builder.append(githubRepoUrl).append(%22/tree/%22).append(branch);%0A    %7D%0A    else %7B%0A  
     builder.
@@ -6155,16 +6155,22 @@
 vePath);
+%0A    %7D
 %0A%0A    if

fix 'open in browser' url