/*******************************************************************************
 * Copyright (c) 2014, 2018 Red Hat.
 * 
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Red Hat - Initial Contribution
 *******************************************************************************/
package org.eclipse.linuxtools.internal.vagrant.core;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.eclipse.core.runtime.Platform;
import org.eclipse.linuxtools.vagrant.core.IVagrantConnection;
import org.eclipse.linuxtools.vagrant.core.IVagrantVM;
import org.eclipse.linuxtools.vagrant.core.IVagrantVMListener;

public class VagrantVMRefreshManager implements IVagrantVMListener {

	private static VagrantVMRefreshManager instance;
	private final long DEFAULT_REFRESH_TIME = 15;

	private Map<IVagrantConnection, ContainerRefreshThread> refreshThreadMap;

	private VagrantVMRefreshManager() {
		refreshThreadMap = new HashMap<>();
	}

	public static VagrantVMRefreshManager getInstance() {
		if (instance == null)
			instance = new VagrantVMRefreshManager();
		return instance;
	}

	@Override
	public synchronized void listChanged(
			final IVagrantConnection connection,
			final List<IVagrantVM> dclist) {

		ContainerRefreshThread rt = refreshThreadMap.get(connection);
		if (rt == null) {
			long refreshRateInSeconds = Platform.getPreferencesService()
					.getLong("org.eclipse.linuxtools.vagrant.ui", //$NON-NLS-1$
							"containerRefreshTime", DEFAULT_REFRESH_TIME, null); //$NON-NLS-1$
			rt = new ContainerRefreshThread(connection,
					TimeUnit.SECONDS.toMillis(refreshRateInSeconds));
			rt.start();
			refreshThreadMap.put(connection, rt);
		}
	}

	/**
	 * Method to reset the refresh rate for updating container lists
	 * 
	 * @param seconds
	 *            - time to wait between refreshes
	 */
	public void setRefreshTime(long seconds) {
		if (seconds >= 5) {
			long refreshRate = TimeUnit.SECONDS.toMillis(seconds);
			for (ContainerRefreshThread t : refreshThreadMap.values()) {
				t.setSleepTime(refreshRate);
			}
		}
	}

	/**
	 * ContainerRefreshThread class is used to update the container lists for a
	 * particular connection as short as needed to keep the UI updated on a
	 * regular basis.
	 *
	 */
	private class ContainerRefreshThread extends Thread {

		private IVagrantConnection connection;
		private long sleepTime;
		private boolean kill;

		public ContainerRefreshThread(IVagrantConnection connection,
				long sleepTime) {
			this.connection = connection;
			this.sleepTime = sleepTime;
		}

		public IVagrantConnection getConnection() {
			return connection;
		}

		public synchronized long getSleepTime() {
			return sleepTime;
		}

		public synchronized void setSleepTime(long newTime) {
			sleepTime = newTime;
			this.interrupt();
		}

		public void kill() {
			kill = true;
			this.interrupt();
		}

		@Override
		public void run() {
			for (;;) {
				try {
					Thread.sleep(getSleepTime());
					// After sleep period, update the containers list, but make
					// sure the refreshManager isn't notified since that
					// is what triggered this to begin with.
					synchronized (instance) {
						connection.removeVMListener(instance);
						((VagrantConnection) connection).getVMs(true);
						connection.addVMListener(instance);
					}
				} catch (InterruptedException e) {
					if (kill)
						break;
					// otherwise..continue
				}
			}
		}
	}

	/***
	 * Method to kill all threads at shutdown.
	 */
	public void killAllThreads() {
		for (ContainerRefreshThread rt : refreshThreadMap.values()) {
			rt.kill();
			refreshThreadMap.remove(rt.getConnection());
		}
	}
}
