package org.stagemonitor.os;

import org.hyperic.sigar.FileSystem;
import org.hyperic.sigar.NetRoute;
import org.hyperic.sigar.Sigar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.stagemonitor.core.CorePlugin;
import org.stagemonitor.core.MeasurementSession;
import org.stagemonitor.core.Stagemonitor;
import org.stagemonitor.core.StagemonitorPlugin;
import org.stagemonitor.core.elasticsearch.ElasticsearchClient;
import org.stagemonitor.core.grafana.GrafanaClient;
import org.stagemonitor.os.metrics.AbstractSigarMetricSet;
import org.stagemonitor.os.metrics.CpuMetricSet;
import org.stagemonitor.os.metrics.EmptySigarMetricSet;
import org.stagemonitor.os.metrics.FileSystemMetricSet;
import org.stagemonitor.os.metrics.MemoryMetricSet;
import org.stagemonitor.os.metrics.NetworkMetricSet;
import org.stagemonitor.os.metrics.SwapMetricSet;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class OsPlugin extends StagemonitorPlugin  {

	private final static Logger logger = LoggerFactory.getLogger(OsPlugin.class);

	private Sigar sigar;

	@Override
	public void initializePlugin(StagemonitorPlugin.InitArguments initArguments) throws Exception {
		final CorePlugin corePlugin = initArguments.getPlugin(CorePlugin.class);

		ElasticsearchClient elasticsearchClient = corePlugin.getElasticsearchClient();
		if (corePlugin.isReportToGraphite()) {
			elasticsearchClient.sendGrafana1DashboardAsync("grafana/Grafana1GraphiteCPU.json");
			elasticsearchClient.sendGrafana1DashboardAsync("grafana/Grafana1GraphiteFilesystem.json");
			elasticsearchClient.sendGrafana1DashboardAsync("grafana/Grafana1GraphiteMemory.json");
			elasticsearchClient.sendGrafana1DashboardAsync("grafana/Grafana1GraphiteNetwork.json");
			elasticsearchClient.sendGrafana1DashboardAsync("grafana/Grafana1GraphiteOSOverview.json");
		}
		if (corePlugin.isReportToElasticsearch()) {
			final GrafanaClient grafanaClient = corePlugin.getGrafanaClient();
			grafanaClient.sendGrafanaDashboardAsync("grafana/ElasticsearchHostDashboard.json");
			elasticsearchClient.sendClassPathRessourceBulkAsync("kibana/Host.bulk");
		}

		if (sigar == null) {
			if (!SigarNativeBindingLoader.loadNativeSigarBindings()) {
				// redeploys are a problem, because the native libs can only be loaded by one class loader
				// this would lead to a UnsatisfiedLinkError: Native Library sigar already loaded in another class loader
				throw new RuntimeException("The OsPlugin only works with one application per JVM " +
						"and does not work after a redeploy");
			}
			sigar = newSigar();
		}
		initArguments.getMetricRegistry().registerAll(init(new CpuMetricSet(sigar, sigar.getCpuInfoList()[0])));
		initArguments.getMetricRegistry().registerAll(init(new MemoryMetricSet(sigar)));
		initArguments.getMetricRegistry().registerAll(init(new SwapMetricSet(sigar)));

		Set<String> routedNetworkInterfaces = new HashSet<String>();
		for (NetRoute netRoute : sigar.getNetRouteList()) {
			routedNetworkInterfaces.add(netRoute.getIfname());
		}
		for (String ifname : routedNetworkInterfaces) {
			initArguments.getMetricRegistry().registerAll(init(new NetworkMetricSet(ifname, sigar)));
		}
		@SuppressWarnings("unchecked")
		final Set<Map.Entry<String, FileSystem>> entries = (Set<Map.Entry<String, FileSystem>>) sigar.getFileSystemMap().entrySet();
		for (Map.Entry<String, FileSystem> e : entries) {
			final FileSystem fs = e.getValue();
			if (fs.getType() == FileSystem.TYPE_LOCAL_DISK || fs.getType() == FileSystem.TYPE_NETWORK) {
				initArguments.getMetricRegistry().registerAll(init(new FileSystemMetricSet(e.getKey(), sigar)));
			}
		}
	}

	@Override
	public void onShutDown() {
		if (sigar != null) {
			sigar.close();
			sigar = null;
		}
	}

	private Sigar newSigar() throws Exception {
		try {
			final Sigar s = new Sigar();
			s.getCpuInfoList();
			return s;
		} catch (UnsatisfiedLinkError e) {
			throw new RuntimeException(e);
		}
	}

	/*
	 * initializing by calling getSnapshot helps to avoid a strange npe
	 */
	private AbstractSigarMetricSet<?> init(AbstractSigarMetricSet<?> metrics) {
		try {
			metrics.getSnapshot();
			return metrics;
		} catch (RuntimeException e) {
			logger.warn(e.getMessage() + ". (This exception is ignored)", e);
			return new EmptySigarMetricSet();
		}
	}

	public static void main(String[] args) throws InterruptedException {
		OsConfigurationSourceInitializer.addConfigurationSource(args);
		Stagemonitor.startMonitoring(getMeasurementSession());
		System.out.println("Interrupt (Ctrl + C) to exit");
		Thread.currentThread().join();
	}

	static MeasurementSession getMeasurementSession() {
		final CorePlugin corePlugin = Stagemonitor.getPlugin(CorePlugin.class);
		String applicationName = corePlugin.getApplicationName() != null ? corePlugin.getApplicationName() : "os";
		String instanceName = corePlugin.getInstanceName() != null ? corePlugin.getInstanceName() : "host";
		return new MeasurementSession(applicationName, corePlugin.getHostName(), instanceName);
	}

	@Override
	public void registerWidgetMetricTabPlugins(WidgetMetricTabPluginsRegistry widgetMetricTabPluginsRegistry) {
		widgetMetricTabPluginsRegistry.addWidgetMetricTabPlugin("/stagemonitor/static/tabs/metrics/os-metrics");
	}

	public Sigar getSigar() {
		return sigar;
	}
}
