/*
 * 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.MessageBus;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.messages.Topic;
import com.intellij.vcs.log.data.VcsLogData;
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.CalledInAny;
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;

public class VcsProjectLog {
  public static final Topic<ProjectLogListener> VCS_PROJECT_LOG_CHANGED =
    Topic.create("Project Vcs Log Created or Disposed", ProjectLogListener.class);
  @NotNull private final Project myProject;
  @NotNull private final MessageBus myMessageBus;
  @NotNull private final VcsLogTabsProperties myUiProperties;

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

  public VcsProjectLog(@NotNull Project project, @NotNull VcsLogTabsProperties uiProperties) {
    myProject = project;
    myMessageBus = project.getMessageBus();
    myUiProperties = uiProperties;
  }

  @Nullable
  public VcsLogData 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);
  }

  /**
   * 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();
  }

  @CalledInAny
  private void recreateLog() {
    ApplicationManager.getApplication().invokeLater(new Runnable() {
      @Override
      public void run() {
        disposeLog();

        if (hasDvcsRoots()) {
          createLog();
        }
      }
    });
  }

  @CalledInAwt
  private void disposeLog() {
    if (myLogManager.getCached() != null) myMessageBus.syncPublisher(VCS_PROJECT_LOG_CHANGED).logDisposed();
    myUi = null;
    myLogManager.drop();
  }

  @CalledInAwt
  private void createLog() {
    VcsLogManager logManager = myLogManager.getValue();

    myMessageBus.syncPublisher(VCS_PROJECT_LOG_CHANGED).logCreated();

    if (PostponableLogRefresher.keepUpToDate()) {
      new HeavyAwareExecutor(myProject).execute(new Runnable() {
        @Override
        public void run() {
          logManager.scheduleInitialization();
        }
      });
    }
  }

  public void scheduleInitialization() {
    VcsLogManager cached = myLogManager.getCached();
    if (cached != null) cached.scheduleInitialization();
  }

  private boolean hasDvcsRoots() {
    return !VcsLogManager.findLogProviders(getVcsRoots(), myProject).isEmpty();
  }

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

  @SuppressWarnings("NonPrivateFieldAccessedInSynchronizedContext")
  private class LazyVcsLogManager extends ClearableLazyValue<VcsLogManager> {

    @NotNull
    @CalledInAwt
    @Override
    public synchronized VcsLogManager getValue() {
      return super.getValue();
    }

    @NotNull
    @CalledInAwt
    @Override
    protected synchronized VcsLogManager compute() {
      return new VcsLogManager(myProject, myUiProperties, getVcsRoots(), false, new Runnable() {
        @Override
        public void run() {
          recreateLog();
        }
      });
    }

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

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

  public static class InitLogStartupActivity implements StartupActivity {
    @Override
    public void runActivity(@NotNull Project project) {
      VcsProjectLog projectLog = getInstance(project);

      MessageBusConnection connection = project.getMessageBus().connect(project);
      connection.subscribe(ProjectLevelVcsManager.VCS_CONFIGURATION_CHANGED, new VcsListener() {
        @Override
        public void directoryMappingChanged() {
          projectLog.recreateLog();
        }
      });
      if (projectLog.hasDvcsRoots()) {
        ApplicationManager.getApplication().invokeLater(new Runnable() {
          @Override
          public void run() {
            projectLog.createLog();
          }
        });
      }
    }
  }

  public interface ProjectLogListener {
    @CalledInAwt
    void logCreated();

    @CalledInAwt
    void logDisposed();
  }
}

@@ -3284,69 +3284,16 @@
 ter(
-new Runnable() %7B%0A      @Override%0A      public void run()
+() -%3E
  %7B%0A
-  
     
@@ -3305,26 +3305,24 @@
 poseLog();%0A%0A
-  
       if (ha
@@ -3345,18 +3345,16 @@
         
-  
 createLo
@@ -3354,34 +3354,24 @@
 reateLog();%0A
-        %7D%0A
       %7D%0A    
@@ -3854,81 +3854,8 @@
 ute(
-new Runnable() %7B%0A        @Override%0A        public void run() %7B%0A          
 logM
@@ -3860,17 +3860,18 @@
 gManager
-.
+::
 schedule
@@ -3884,37 +3884,16 @@
 lization
-();%0A        %7D%0A      %7D
 );%0A    %7D
@@ -5500,104 +5500,8 @@
 GED,
- new VcsListener() %7B%0A        @Override%0A        public void directoryMappingChanged() %7B%0A         
  pro
@@ -5507,17 +5507,18 @@
 ojectLog
-.
+::
 recreate
@@ -5520,37 +5520,16 @@
 reateLog
-();%0A        %7D%0A      %7D
 );%0A     
@@ -5622,87 +5622,8 @@
 ter(
-new Runnable() %7B%0A          @Override%0A          public void run() %7B%0A            
 proj
@@ -5628,17 +5628,18 @@
 ojectLog
-.
+::
 createLo
@@ -5643,33 +5643,8 @@
 eLog
-();%0A          %7D%0A        %7D
 );%0A 

use lambdas and method references in VcsProjectLog