package zielu.gittoolbox.fetch;
import com.google.common.collect.Lists;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.util.messages.MessageBusConnection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import org.jetbrains.annotations.NotNull;
import zielu.gittoolbox.ConfigNotifier;
import zielu.gittoolbox.GitToolBoxApp;
import zielu.gittoolbox.GitToolBoxConfigForProject;
public class AutoFetch extends AbstractProjectComponent {
private static final int DEFAULT_DELAY_MINUTES = 1;
private final Logger LOG = Logger.getInstance(getClass());
private final AtomicLong myLastAutoFetch = new AtomicLong();
private final AtomicBoolean myActive = new AtomicBoolean();
private MessageBusConnection myConnection;
private ScheduledExecutorService myExecutor;
private final List<ScheduledFuture<?>> myScheduledTasks = new LinkedList<>();
private int currentInterval;
public AutoFetch(Project project) {
super(project);
}
public static AutoFetch getInstance(@NotNull Project project) {
return project.getComponent(AutoFetch.class);
}
@Override
public void initComponent() {
myConnection = myProject.getMessageBus().connect();
myConnection.subscribe(ConfigNotifier.CONFIG_TOPIC, new ConfigNotifier.Adapter() {
@Override
public void configChanged(Project project, GitToolBoxConfigForProject config) {
onConfigChange(config);
}
});
myConnection.subscribe(AutoFetchNotifier.TOPIC, this::onStateChanged);
}
public static AutoFetch create(Project project) {
return new AutoFetch(project);
}
private void init() {
GitToolBoxConfigForProject config = GitToolBoxConfigForProject.getInstance(project());
if (config.autoFetch) {
synchronized (this) {
currentInterval = config.autoFetchIntervalMinutes;
scheduleInitTask();
}
}
}
private void cancelCurrentTasks() {
synchronized (this) {
List<ScheduledFuture<?>> tasks = Lists.newArrayList(myScheduledTasks);
myScheduledTasks.clear();
tasks.forEach(t -> t.cancel(false));
}
}
private boolean cleanAndCheckTasks() {
synchronized (this) {
myScheduledTasks.removeIf(task -> task.isCancelled() || task.isDone());
return myScheduledTasks.isEmpty();
}
}
private void onConfigChange(GitToolBoxConfigForProject config) {
if (config.autoFetch) {
LOG.debug("Auto-fetch enabled");
synchronized (this) {
if (currentInterval != config.autoFetchIntervalMinutes) {
if (LOG.isDebugEnabled()) {
LOG.debug("Auto-fetch interval or state changed: enabled="
+ config.autoFetch + ", interval=" + config.autoFetchIntervalMinutes);
}
cancelCurrentTasks();
LOG.debug("Existing task cancelled on auto-fetch change");
if (currentInterval == 0) {
//enable
scheduleFastTask();
} else {
scheduleTask();
}
currentInterval = config.autoFetchIntervalMinutes;
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Auto-fetch interval and state did not change: enabled="
+ config.autoFetch + ", interval=" + config.autoFetchIntervalMinutes);
}
}
}
} else {
LOG.debug("Auto-fetch disabled");
synchronized (this) {
cancelCurrentTasks();
currentInterval = 0;
LOG.debug("Existing task cancelled on auto-fetch disable");
}
}
}
private void scheduleInitTask() {
scheduleFastTask(30);
}
private void scheduleFastTask() {
scheduleFastTask(60);
}
private void scheduleFastTask(int seconds) {
if (isActive()) {
synchronized (this) {
if (cleanAndCheckTasks()) {
LOG.debug("Scheduling fast auto-fetch in ", seconds, " seconds");
myScheduledTasks.add(myExecutor.schedule(AutoFetchTask.create(this), seconds, TimeUnit.SECONDS));
} else {
LOG.debug("Tasks already scheduled (in fast auto-fetch)");
}
}
}
}
private void onStateChanged(AutoFetchState state) {
if (state.canAutoFetch() && isAutoFetchEnabled()) {
int delayMinutes;
long lastAutoFetch = lastAutoFetch();
if (lastAutoFetch != 0) {
long nextAutoFetch = lastAutoFetch + TimeUnit.MINUTES.toMillis(getIntervalMinutes());
long difference = nextAutoFetch - System.currentTimeMillis();
if (difference > 0) {
delayMinutes = Math.max((int) TimeUnit.MILLISECONDS.toMinutes(difference), DEFAULT_DELAY_MINUTES);
} else {
delayMinutes = DEFAULT_DELAY_MINUTES;
}
} else {
delayMinutes = DEFAULT_DELAY_MINUTES;
}
scheduleTask(delayMinutes);
}
}
private int getIntervalMinutes() {
return GitToolBoxConfigForProject.getInstance(project()).autoFetchIntervalMinutes;
}
private void scheduleTask() {
scheduleTask(getIntervalMinutes());
}
private void scheduleTask(int delayMinutes) {
if (isActive()) {
synchronized (this) {
if (cleanAndCheckTasks()) {
LOG.debug("Scheduling regular auto-fetch in ", delayMinutes, " minutes");
myScheduledTasks.add(myExecutor.schedule(AutoFetchTask.create(this), delayMinutes, TimeUnit.MINUTES));
} else {
LOG.debug("Tasks already scheduled (in regular auto-fetch)");
}
}
}
}
void scheduleNextTask() {
synchronized (this) {
if (isAutoFetchEnabled()) {
scheduleTask();
}
}
}
private boolean isAutoFetchEnabled() {
return GitToolBoxConfigForProject.getInstance(project()).autoFetch;
}
public Project project() {
return myProject;
}
boolean isActive() {
return myActive.get();
}
public void updateLastAutoFetchDate() {
myLastAutoFetch.set(System.currentTimeMillis());
}
public long lastAutoFetch() {
return myLastAutoFetch.get();
}
@Override
public void projectOpened() {
if (myActive.compareAndSet(false, true)) {
myExecutor = GitToolBoxApp.getInstance().autoFetchExecutor();
init();
}
}
@Override
public void projectClosed() {
if (myActive.compareAndSet(true, false)) {
cancelCurrentTasks();
}
}
@Override
public void disposeComponent() {
if (myConnection != null) {
myConnection.disconnect();
myConnection = null;
}
}
}