/**
 * 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.binarydatapoints;

import com.etherios.connector.core.BinaryDataPoint;
import com.etherios.connector.core.DataStream;
import com.etherios.connector.core.log.Logger;
import com.etherios.connector.emulator.CloudConnector;
import java.util.Arrays;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

/**
 * Class to manage the Binary Data Points upload.
 * 
 * <p>Simulates the monitoring of a patient, sending his vital signs encrypted 
 * as binary data points to Device Cloud every {@value #REFRESH_TIMEOUT} ms.</p>
 */
public class PatientMonitor {
	
	/* Constants */
	
	// Timeout to send the sample in milliseconds.
	private static final int REFRESH_TIMEOUT = 30 * 1000; // 30 seconds
	
	// Data stream name.
	private static final String DATASTREAM_NAME = "patient";
	
	// Encryption key.
	private static final String MY_SECRET_KEY = "thisIsASecretKey";
	
	
	/* Variables */
	
	private CloudConnector connector;
	
	private Timer monitorTimer;
	
	private DataStream dataStream;
	
	private boolean encrypt = true;
	
	/**
	 * Class constructor. Instances a new <code>PatientManager</code> 
	 * to manage the Binary Data Points upload.
	 *
	 * @param connector The Cloud Connector object.
	 */
	public PatientMonitor(CloudConnector connector) {
		this.connector = connector;
		dataStream = new DataStream(DATASTREAM_NAME);
	}
	
	/**
	 * Starts the monitoring.
	 *
	 * <p>Schedules the timer task that periodically reads the vital signs of 
	 * the patient and sends them to Device Cloud.</p>
	 * 
	 * @param encrypt Determines if the medical data will be encrypted or not.
	 */
	public void start(boolean encrypt) {
		this.encrypt = encrypt;
		
		if (monitorTimer != null) {
			monitorTimer.cancel();
			monitorTimer.purge();
		}
		monitorTimer = new Timer();
		monitorTimer.schedule(new MonitorTask(), 10, REFRESH_TIMEOUT);
	}
	
	/**
	 * Stops the monitoring.
	 *
	 * <p>Cancels the periodic task to read the vital signs of the patient.</p>
	 */
	public void stop() {
		if (monitorTimer != null) {
			monitorTimer.cancel();
			monitorTimer.purge();
		}
		monitorTimer = null;
	}

	/**
	 * Encrypts the given data using the AES algorithm.
	 * 
	 * @param input Data to encrypt.
	 * @param key Key to encrypt the data.
	 * @return Data encrypted.
	 */
	private byte[] encrypt(byte[] input, byte[] key) {
		try {
			Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
			SecretKeySpec secretKey = new SecretKeySpec(key, 0, key.length, "AES");
			cipher.init(Cipher.ENCRYPT_MODE, secretKey);
			byte[] enc = new byte[input.length * 2];
			cipher.doFinal(input, 0, input.length, enc, 0);
			int i = enc.length;
			while (i > 0 && enc[i - 1] == 0) i--;
			return Arrays.copyOf(enc, i);
		} catch (Exception ex) {
			Logger.logException(connector.getLogger(), ex);
		}
		return new byte[0];
	}
	
	/**
	 * Simulates and generates the vital signs of the patient.
	 * 
	 * @return Vital signs of the patient.
	 */
	private String generateConstants() {
		Random r = new Random(System.currentTimeMillis());
		
		StringBuilder sb = new StringBuilder("patient_id=af3424pm").append("\n");
		sb.append("temperature=").append(37 + ((double) Math.round(r.nextDouble() * 10) / 10)).append("\n");
		sb.append("pulse_rate=").append(60 + r.nextInt(50)).append("\n");
		sb.append("s_blood_pressure=").append(90 + r.nextInt(20)).append("\n");
		sb.append("d_blood_pressure=").append(55 + r.nextInt(20)).append("\n");
		sb.append("respiratory_rate=").append(10 + r.nextInt(8));
		return sb.toString();
	}
	
	/**
	 * Monitor task to be performed every {@value #REFRESH_TIMEOUT} ms.
	 *
	 * <p>The task will read the vital signs of the patient and will send them to
	 * Device Cloud using binary data points.</p>
	 */
	private class MonitorTask extends TimerTask {

		@Override
		public void run() {
			if (connector.isConnected()) {
				byte[] data;
				if (encrypt)
					data = encrypt(generateConstants().getBytes(), MY_SECRET_KEY.getBytes());
				else
					data = generateConstants().getBytes();
				
				// Create and send the binary data point.
				BinaryDataPoint bdp = new BinaryDataPoint(data, dataStream);
				connector.sendBinaryDataPoint(bdp);
			}
		}
		
	}
}
