package zielu.gittoolbox.fetch;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task.Backgroundable;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import git4idea.GitUtil;
import git4idea.GitVcs;
import git4idea.repo.GitRepository;
import git4idea.repo.GitRepositoryManager;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.jetbrains.annotations.NotNull;
import zielu.gittoolbox.GitToolBoxConfigForProject;
import zielu.gittoolbox.GitToolBoxProject;
import zielu.gittoolbox.ResBundle;
import zielu.gittoolbox.compat.NotificationHandle;
import zielu.gittoolbox.compat.Notifier;
import zielu.gittoolbox.ui.util.AppUtil;

public class AutoFetchTask implements Runnable {
    private static final boolean showNotifications = false;

    private final Logger LOG = Logger.getInstance(getClass());
    private final AutoFetch myParent;
    private final Project myProject;

    private final AtomicReference<NotificationHandle> lastNotification = new AtomicReference<NotificationHandle>();

    private AutoFetchTask(AutoFetch parent) {
        myParent = parent;
        myProject = myParent.project();
    }

    public static AutoFetchTask create(AutoFetch parent) {
        return new AutoFetchTask(parent);
    }

    private void finishedNotification() {
        NotificationHandle toCancel = lastNotification.getAndSet(null);
        if (toCancel != null && myParent.isActive()) {
            toCancel.expire();
        }
        if (myParent.isActive()) {
            NotificationHandle notification = Notifier.getInstance(myProject).notifyLogOnly(
                ResBundle.getString("message.autoFetch"),
                ResBundle.getString("message.finished"));
            lastNotification.set(notification);
        }
    }

    private void finishedWithoutFetch() {
        NotificationHandle toCancel = lastNotification.getAndSet(null);
        if (toCancel != null && myParent.isActive()) {
            toCancel.expire();
        }
    }

    private boolean isSmartMode() {
        return !DumbService.isDumb(myProject);
    }

    private boolean isEnabled() {
        return myParent.isActive() && isSmartMode();
    }

    private List<GitRepository> reposForFetch() {
        GitRepositoryManager repositoryManager = GitUtil.getRepositoryManager(myProject);
        ImmutableList<GitRepository> allRepos = ImmutableList.copyOf(repositoryManager.getRepositories());
        AutoFetchStrategy strategy = GitToolBoxConfigForProject.getInstance(myProject).getAutoFetchStrategy();
        List<GitRepository> fetchable = strategy.fetchableRepositories(allRepos, myProject);
        List<GitRepository> toFetch = Lists.newArrayListWithCapacity(fetchable.size());
        for (GitRepository repository : fetchable) {
            if (repository.getRoot().exists() && !repository.isRebaseInProgress()) {
                toFetch.add(repository);
            }
        }
        return toFetch;
    }

    private void doFetch(List<GitRepository> repos, @NotNull ProgressIndicator indicator, @NotNull String title) {
        LOG.debug("Starting auto-fetch...");
        indicator.setText(title);
        indicator.setIndeterminate(true);
        indicator.startNonCancelableSection();
        if (isEnabled()) {
            Collection<GitRepository> fetched =
                GtFetcher.builder().fetchAll().build(myProject, indicator).fetchRoots(repos);
            indicator.finishNonCancelableSection();
            GitToolBoxProject.getInstance(myProject).perRepoStatusCache().refresh(fetched);
            LOG.debug("Finished auto-fetch");
            if (showNotifications) {
                finishedNotification();
            }
        } else {
            LOG.debug("Skipped auto-fetch");
        }
        myParent.updateLastAutoFetchDate();
    }


    @Override
    public void run() {
        final List<GitRepository> repos = reposForFetch();
        boolean shouldFetch = !repos.isEmpty();
        boolean enabled = isEnabled();
        if (shouldFetch && enabled) {
            AppUtil.invokeLaterIfNeeded(() -> GitVcs.runInBackground(new Backgroundable(Preconditions.checkNotNull(myProject),
                ResBundle.getString("message.autoFetching"), false) {
                @Override
                public void run(@NotNull ProgressIndicator indicator) {
                    doFetch(repos, indicator, getTitle());
                }
            }));
        } else {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Fetched skipped: shouldFetch=" + shouldFetch + ", enabled=" + enabled);
            }
            if (showNotifications) {
                AppUtil.invokeLaterIfNeeded(this::finishedWithoutFetch);
            }
        }
    }
}

@@ -590,24 +590,74 @@
 .util.List;%0A
+import java.util.concurrent.atomic.AtomicBoolean;%0A
 import java.
@@ -1249,16 +1249,89 @@
 Project;
+%0A    private final AtomicBoolean myFetchInProgress = new AtomicBoolean();
 %0A%0A    pr
@@ -3684,16 +3684,628 @@
         
+if (myFetchInProgress.compareAndSet(false, true)) %7B%0A                try %7B%0A                    doFetch(repos, indicator);%0A                %7D finally %7B%0A                    indicator.finishNonCancelableSection();%0A                    myFetchInProgress.compareAndSet(true, false);%0A                %7D%0A            %7D else %7B%0A                LOG.info(%22Auto-fetch already in progress%22);%0A            %7D%0A        %7D else %7B%0A            LOG.debug(%22Auto-fetch inactive%22);%0A        %7D%0A        myParent.updateLastAutoFetchDate();%0A    %7D%0A%0A    private void doFetch(List%3CGitRepository%3E repos, @NotNull ProgressIndicator indicator) %7B%0A        
 Collecti
@@ -4331,32 +4331,16 @@
 etched =
-%0A               
  GtFetch
@@ -4414,64 +4414,8 @@
 s);%0A
-            indicator.finishNonCancelableSection();%0A    
     
@@ -4498,36 +4498,32 @@
 tched);%0A        
-    
 LOG.debug(%22Finis
@@ -4536,28 +4536,24 @@
 to-fetch%22);%0A
-    
         if (
@@ -4585,20 +4585,16 @@
         
-    
 finished
@@ -4621,136 +4621,15 @@
     
+%7D%0A
     %7D
-%0A        %7D else %7B%0A            LOG.debug(%22Skipped auto-fetch%22);%0A        %7D%0A        myParent.updateLastAutoFetchDate();%0A    %7D%0A
 %0A%0A  

Allow only one auto-fetch (#33)