/**
 * Copyright (c) 2014 Etherios, a Division of Digi International, Inc.
 * All rights not expressly granted are reserved.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Etherios 11001 Bren Road East, Minnetonka, MN 55343
 * =======================================================================
 */
package com.etherios.connector.datapoints;

import com.etherios.connector.core.DataPoint;
import com.etherios.connector.core.DataStream;
import com.etherios.connector.emulator.CloudConnector;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

/**
 * Class to manage the Data Points upload.
 *
 * <p>It uploads the amount of free and used memory in the JVM every
 * {@value #REFRESH_TIMEOUT} milliseconds.</p>
 */
public class MemoryMonitor {

	/* Constants */

	// Data stream names.
	private static final String FREE_MEMORY_DATA_STREAM = "freeMemory";
	private static final String USED_MEMORY_DATA_STREAM = "usedMemory";

	// Timeout to refresh the memory values in milliseconds.
	private static final int REFRESH_TIMEOUT = 30 * 1000; // 30 seconds

	/* Variables */

	// Cloud Connector.
	private CloudConnector connector;

	// Timer to monitor the memory usage.
	private Timer memoryMonitorTimer;

	// Data streams.
	private DataStream freeMemoryDataStream;
	private DataStream usedMemoryDataStream;

	// Data points Vector.
	private List<DataPoint> dataPoints;

	/**
	 * Class constructor. Instances a new <code>MemoryMonitor</code> 
	 * object to manage the Data Points upload.
	 * 
	 * @param connector Cloud Connector instance.
	 */
	public MemoryMonitor(CloudConnector connector) {
		this.connector = connector;
		dataPoints = new ArrayList<>();

		// Create the free memory data stream and set the units.
		freeMemoryDataStream = new DataStream(FREE_MEMORY_DATA_STREAM);
		freeMemoryDataStream.setUnits("KB");

		// Create the used memory data stream and set the units.
		usedMemoryDataStream = new DataStream(USED_MEMORY_DATA_STREAM);
		usedMemoryDataStream.setUnits("KB");
	}

	/**
	 * Starts the memory monitoring.
	 *
	 * Schedules the timer task that periodically reads the free and used memory
	 * value and sends it to Device Cloud.
	 */
	public synchronized void start() {
		// Schedule the task to read the memory every REFRESH_TIMEOUT ms.
		if (memoryMonitorTimer != null) {
			memoryMonitorTimer.cancel();
			memoryMonitorTimer.purge();
		}
		memoryMonitorTimer = new Timer();
		memoryMonitorTimer.schedule(new MemoryMonitorTask(), 0, REFRESH_TIMEOUT);
	}

	/**
	 * Stops the memory monitoring.
	 *
	 * Cancels the periodic task to read the memory values.
	 */
	public synchronized void stop() {
		if (memoryMonitorTimer != null) {
			memoryMonitorTimer.cancel();
			memoryMonitorTimer.purge();
		}
		memoryMonitorTimer = null;
	}

	/**
	 * Memory monitor task to be performed every {@value #REFRESH_TIMEOUT} ms.
	 *
	 * The task will read the free and used memory values and will send them to
	 * Device Cloud using the Cloud Connector multiple data streams upload.
	 */
	private class MemoryMonitorTask extends TimerTask {

		@Override
		public void run() {
			// Get the JVM free memory in KB.
			long freeMemory = Runtime.getRuntime().freeMemory() / 1024;
			// Get the JVM used memory in KB.
			long usedMemory = (Runtime.getRuntime().totalMemory() / 1024) - freeMemory;

			// The user could manage these values if desired.

			// If the connector is connected, create and send the data points.
			if (connector.isConnected()) {
				// Create the free memory data point.
				DataPoint freeMemoryDataPoint = new DataPoint(freeMemory, freeMemoryDataStream);
				freeMemoryDataPoint.setDescription("Current JVM free memory");
				// Add the data point to the vector.
				dataPoints.add(freeMemoryDataPoint);

				// Create the used memory data point.
				DataPoint usedMemoryDataPoint = new DataPoint(usedMemory, usedMemoryDataStream);
				usedMemoryDataPoint.setDescription("Current JVM used memory");
				// Add the data point to the vector.
				dataPoints.add(usedMemoryDataPoint);

				// Upload both data points.
				connector.sendDataPoint(dataPoints);
				
				// Clear the list.
				dataPoints.clear();
			}
		}
	}
}
