/*
 * Copyright 2000-2016 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.vcs.log.impl;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.StartupActivity;
import com.intellij.openapi.util.ClearableLazyValue;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsListener;
import com.intellij.openapi.vcs.VcsRoot;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.vcs.log.data.VcsLogDataManager;
import com.intellij.vcs.log.data.VcsLogTabsProperties;
import com.intellij.vcs.log.ui.VcsLogPanel;
import com.intellij.vcs.log.ui.VcsLogUiImpl;
import org.jetbrains.annotations.CalledInAwt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;

public class VcsLogProjectManager {
  @NotNull private final Project myProject;
  @NotNull private final VcsLogTabsProperties myUiProperties;

  @NotNull
  private final LazyVcsLogManager myLogManager = new LazyVcsLogManager();
  private volatile VcsLogUiImpl myUi;
  @Nullable private Runnable myRecreateMainLogHandler;

  public VcsLogProjectManager(@NotNull Project project, @NotNull VcsLogTabsProperties uiProperties) {
    myProject = project;
    myUiProperties = uiProperties;
  }

  public void init() {
    myLogManager.getValue();
  }

  @Nullable
  public VcsLogDataManager getDataManager() {
    VcsLogManager cached = myLogManager.getCached();
    if (cached == null) return null;
    return cached.getDataManager();
  }

  @NotNull
  private Collection<VcsRoot> getVcsRoots() {
    return Arrays.asList(ProjectLevelVcsManager.getInstance(myProject).getAllVcsRoots());
  }

  @NotNull
  public JComponent initMainLog(@NotNull String contentTabName) {
    myUi = myLogManager.getValue().createLogUi(VcsLogTabsProperties.MAIN_LOG_ID, contentTabName);
    return new VcsLogPanel(myLogManager.getValue(), myUi);
  }

  public void setRecreateMainLogHandler(@Nullable Runnable recreateMainLogHandler) {
    myRecreateMainLogHandler = recreateMainLogHandler;
  }

  /**
   * The instance of the {@link VcsLogUiImpl} or null if the log was not initialized yet.
   */
  @Nullable
  public VcsLogUiImpl getMainLogUi() {
    return myUi;
  }

  @Nullable
  public VcsLogManager getLogManager() {
    return myLogManager.getCached();
  }

  public void disposeLog() {
    myUi = null;
    myLogManager.drop();
  }

  public static VcsLogProjectManager getInstance(@NotNull Project project) {
    return ServiceManager.getService(project, VcsLogProjectManager.class);
  }

  private class LazyVcsLogManager extends ClearableLazyValue<VcsLogManager> {
    @NotNull
    @CalledInAwt
    @Override
    protected VcsLogManager compute() {
      return new VcsLogManager(myProject, myUiProperties, getVcsRoots(), myRecreateMainLogHandler);
    }

    @CalledInAwt
    @Override
    public void drop() {
      if (myValue != null) Disposer.dispose(myValue);
      super.drop();
    }

    @Nullable
    @CalledInAwt
    public VcsLogManager getCached() {
      return myValue;
    }
  }

  public static class InitLogStartupActivity implements StartupActivity {
    @Override
    public void runActivity(@NotNull Project project) {
      if (!PostponableLogRefresher.keepUpToDate()) return;

      VcsLogProjectManager logManager = getInstance(project);
      AtomicBoolean isInitialized = new AtomicBoolean(false);
      MessageBusConnection connection = project.getMessageBus().connect();
      connection.subscribe(ProjectLevelVcsManager.VCS_CONFIGURATION_CHANGED, new VcsListener() {
        @Override
        public void directoryMappingChanged() {
          init(logManager, connection, isInitialized);
        }
      });

      if (!logManager.getVcsRoots().isEmpty()) {
        init(logManager, connection, isInitialized);
      }
    }

    private static void init(@NotNull VcsLogProjectManager logManager, @NotNull MessageBusConnection connection, @NotNull AtomicBoolean isInitialized) {
      if (isInitialized.compareAndSet(false, true)) {
        connection.disconnect();
        ApplicationManager.getApplication().invokeLater(new Runnable() {
          @Override
          public void run() {
            logManager.init();
          }
        });
      }
    }
  }
}

@@ -1861,63 +1861,8 @@
 yUi;
-%0A  @Nullable private Runnable myRecreateMainLogHandler;
 %0A%0A  
@@ -2754,16 +2754,30 @@
 %7B%0A    my
+LogManager.set
 Recreate
@@ -2790,19 +2790,17 @@
 gHandler
- = 
+(
 recreate
@@ -2813,16 +2813,17 @@
 gHandler
+)
 ;%0A  %7D%0A%0A 
@@ -3324,16 +3324,84 @@
 );%0A  %7D%0A%0A
+  @SuppressWarnings(%22NonPrivateFieldAccessedInSynchronizedContext%22)%0A
   privat
@@ -3470,16 +3470,207 @@
 ager%3E %7B%0A
+    @Nullable private Runnable myRecreateMainLogHandler;%0A%0A    @NotNull%0A    @CalledInAwt%0A    @Override%0A    public synchronized VcsLogManager getValue() %7B%0A      return super.getValue();%0A    %7D%0A%0A
     @Not
@@ -3719,16 +3719,29 @@
 otected 
+synchronized 
 VcsLogMa
@@ -3903,24 +3903,37 @@
 %0A    public 
+synchronized 
 void drop() 
@@ -4037,31 +4037,27 @@
     
-@CalledInAwt%0A    public
+public synchronized
  Vcs
@@ -4104,24 +4104,188 @@
 Value;%0A    %7D
+%0A%0A    public synchronized void setRecreateMainLogHandler(@Nullable Runnable recreateMainLogHandler) %7B%0A      myRecreateMainLogHandler = recreateMainLogHandler;%0A    %7D
 %0A  %7D%0A%0A  publ

synchronize LazyVcsLogManager