/*
 * Copyright 2000-2015 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.ui;

import com.intellij.dvcs.repo.Repository;
import com.intellij.dvcs.repo.VcsRepositoryManager;
import com.intellij.dvcs.repo.VcsRepositoryMappingListener;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.StatusBar;
import com.intellij.openapi.wm.StatusBarWidget;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.openapi.wm.impl.status.EditorBasedWidget;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.CalledInAwt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.awt.event.MouseEvent;

public abstract class DvcsStatusWidget<T extends Repository> extends EditorBasedWidget
  implements StatusBarWidget.MultipleTextValuesPresentation, StatusBarWidget.Multiframe
{
  protected static final Logger LOG = Logger.getInstance(DvcsStatusWidget.class);
  private static final String MAX_STRING = "VCS: Rebasing feature-12345";

  @NotNull private final String myPrefix;

  @Nullable private String myText;
  @Nullable private String myTooltip;

  protected DvcsStatusWidget(@NotNull Project project, @NotNull String prefix) {
    super(project);
    myPrefix = prefix;
  }

  @Nullable
  protected abstract T guessCurrentRepository(@NotNull Project project);

  @NotNull
  protected abstract String getFullBranchName(@NotNull T repository);

  protected abstract boolean isMultiRoot(@NotNull Project project);

  @NotNull
  protected abstract ListPopup getPopup(@NotNull Project project, @NotNull T repository);

  protected abstract void subscribeToRepoChangeEvents(@NotNull Project project);

  protected abstract void rememberRecentRoot(@NotNull String path);

  public void activate() {
    Project project = getProject();
    if (project != null) {
      installWidgetToStatusBar(project, this);
    }
  }

  public void deactivate() {
    Project project = getProject();
    if (project != null) {
      removeWidgetFromStatusBar(project, this);
    }
  }

  public void dispose() {
    deactivate();
    super.dispose();
  }

  @NotNull
  @Override
  public String ID() {
    return getClass().getName();
  }

  @Override
  public WidgetPresentation getPresentation(@NotNull PlatformType type) {
    return this;
  }

  @Override
  public void selectionChanged(@NotNull FileEditorManagerEvent event) {
    LOG.debug("selection changed");
    update();
  }

  @Override
  public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
    LOG.debug("file opened");
    update();
  }

  @Override
  public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
    LOG.debug("file closed");
    update();
  }

  @CalledInAwt
  @Nullable
  @Override
  public String getSelectedValue() {
    return StringUtil.isEmpty(myText) ? "" : myPrefix + ": " + myText;
  }

  @NotNull
  @Override
  @Deprecated
  public String getMaxValue() {
    return "";
  }

  @Nullable
  @Override
  public String getTooltipText() {
    return myTooltip;
  }

  @Nullable
  @Override
  public ListPopup getPopupStep() {
    Project project = getProject();
    if (project == null || project.isDisposed()) return null;
    T repository = guessCurrentRepository(project);
    if (repository == null) return null;

    return getPopup(project, repository);
  }

  @Nullable
  @Override
  public Consumer<MouseEvent> getClickConsumer() {
    // has no effect since the click opens a list popup, and the consumer is not called for the MultipleTextValuesPresentation
    return null;
  }

  protected void updateLater() {
    Project project = getProject();
    if (project != null && !project.isDisposed()) {
      ApplicationManager.getApplication().invokeLater(() -> {
        LOG.debug("update after repository change");
        update();
      }, project.getDisposed());
    }
  }

  @CalledInAwt
  private void update() {
    myText = null;
    myTooltip = null;

    Project project = getProject();
    if (project == null || project.isDisposed()) return;
    T repository = guessCurrentRepository(project);
    if (repository == null) return;

    int maxLength = MAX_STRING.length() - 1; // -1, because there are arrows indicating that it is a popup
    myText = StringUtil.shortenTextWithEllipsis(getFullBranchName(repository), maxLength, 5);
    myTooltip = getToolTip(project);
    if (myStatusBar != null) {
      myStatusBar.updateWidget(ID());
    }
    rememberRecentRoot(repository.getRoot().getPath());
  }

  @Nullable
  @CalledInAwt
  private String getToolTip(@NotNull Project project) {
    T currentRepository = guessCurrentRepository(project);
    if (currentRepository == null) return null;
    String branchName = getFullBranchName(currentRepository);
    if (isMultiRoot(project)) {
      return branchName + "\n" + "Root: " + currentRepository.getRoot().getName();
    }
    return branchName;
  }

  private void installWidgetToStatusBar(@NotNull final Project project, @NotNull final StatusBarWidget widget) {
    ApplicationManager.getApplication().invokeLater(() -> {
      StatusBar statusBar = WindowManager.getInstance().getStatusBar(project);
      if (statusBar != null && !isDisposed()) {
        statusBar.addWidget(widget, "after " + (SystemInfo.isMac ? "Encoding" : "InsertOverwrite"), project);
        subscribeToMappingChanged();
        subscribeToRepoChangeEvents(project);
        update();
      }
    }, project.getDisposed());
  }

  private void removeWidgetFromStatusBar(@NotNull final Project project, @NotNull final StatusBarWidget widget) {
    ApplicationManager.getApplication().invokeLater(() -> {
      StatusBar statusBar = WindowManager.getInstance().getStatusBar(project);
      if (statusBar != null && !isDisposed()) {
        statusBar.removeWidget(widget.ID());
      }
    }, project.getDisposed());
  }

  private void subscribeToMappingChanged() {
    myProject.getMessageBus().connect().subscribe(VcsRepositoryManager.VCS_REPOSITORY_MAPPING_UPDATED, new VcsRepositoryMappingListener() {
      @Override
      public void mappingChanged() {
        LOG.debug("repository mappings changed");
        updateLater();
      }
    });
  }
}
@@ -1970,16 +1970,45 @@
 re-12345
+ in custom development branch
 %22;%0A%0A  @N
@@ -7204,8 +7204,9 @@
 );%0A  %7D%0A%7D
+%0A

Allow Git/Hg branch to have longer name in status bar