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

import com.etherios.connector.core.log.ConnectorLogger;
import com.etherios.connector.core.log.Logger;
import com.etherios.connector.emulator.rci.IGeneralRCIExtension;
import java.io.IOException;
import java.util.Properties;
import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;

/**
 * Class which extends the RCI functionality by implementing the
 * {@link com.etherios.connector.emulator.rci.IGeneralRCIExtension} interface.
 *
 * <p>All the settings are read and written in the {@value #SETTINGS_FILE} file.</p>
 */
public class RCIExtension implements IGeneralRCIExtension {

	/* Constants */

	private static final String FILE_PREFIX = "file://";
	private static final String FS_ROOT = "/root1/";
	private static final String SETTINGS_FILE = "settings.txt";

	// Group names
	private static final String NETWORK_GROUP = "network";
	private static final String WIFI_GROUP = "wifi";
	private static final String MOBILE_GROUP = "mobile";
	private static final String MEMORY_INFO_GROUP = "memory_info";

	// Memory info settings group element names
	private static final String FREE_MEMORY_SETTING = "free_memory";
	private static final String MAX_MEMORY_SETTING = "max_memory";
	private static final String TOTAL_MEMORY_SETTING = "total_memory";

	/* Variables */

	private Properties properties;
	private FileConnection connection;

	/**
	 * Class constructor.
	 */
	public RCIExtension() {
	}
	
	/**
	 * Initializes the properties file and the connection with it.
	 */
	private void initializeProperties() {
		properties = new Properties();
		try {
			connection = (FileConnection) Connector.open(FILE_PREFIX + FS_ROOT + SETTINGS_FILE);
			properties.load(connection.openInputStream());
		} catch (IOException ex) {
			Logger.logException(ConnectorLogger.getInstance(), ex);
		}
	}

	@Override
	public String getSettingValue(String group, String setting, int index, String name) {
		/**
		 * Returns the value for the given setting within the given group.
		 * In this case, it returns the value stored in the settings file.
		 */
		System.out.println("<query_setting> request received for the setting '" + setting + "' of the group '" + group + "'...");
		
		// If the properties file is null, initialize it.
		if (properties == null)
			initializeProperties();
		
		switch (group) {
			case NETWORK_GROUP:
				return getNetworkSettingValue(setting);
			case WIFI_GROUP:
				return getWifiSettingValue(setting);
			case MOBILE_GROUP:
				return getMobileSettingValue(setting);
			case MEMORY_INFO_GROUP:
				return getMemoryInfoStateValue(setting);
			default:
				// Error: Group does not exist.
				System.out.println("Error: group '" + group + "' does not exist.");
				return null;
		}
	}

	@Override
	public String setSettingValue(String group, String setting, String value, int index, String name) {
		/**
		 * This method should be "configuring the interface".
		 * Sets the given value for the given setting within the given group.
		 * In this case, it stores the value in the settings file.
		 */
		System.out.println("<set_setting> request received for the setting '" + setting + "' of the group '" + group + "'...");
		
		// If the properties file is null, initialize it.
		if (properties == null)
			initializeProperties();
		
		switch (group) {
			case NETWORK_GROUP:
				return setNetworkSettingValue(setting, value);
			case WIFI_GROUP:
				return setWifiSettingValue(setting, value);
			case MOBILE_GROUP:
				return setMobileSettingValue(setting, value);
			default:
				// Error: Group does not exist.
				System.out.println("Error: group '" + group + "' does not exist.");
				return null;
		}
	}

	@Override
	public String handleZigbeeRequest(String data) {
		// Not handled.
		return null;
	}

	/**
	 * Retrieves the value of the given {@value #NETWORK_GROUP} setting.
	 *
	 * @param setting the setting to get the value.
	 * @return the value, {@code null} if error.
	 */
	private String getNetworkSettingValue(String setting) {
		return properties.getProperty(setting);
	}

	/**
	 * Sets the value of the given {@value #NETWORK_GROUP} setting.
	 *
	 * @param setting the setting to set the value.
	 * @param value the new value.
	 * @return Error string. Empty string means no error.
	 */
	private String setNetworkSettingValue(String setting, String value) {
		return setPropertyValue(setting, value);
	}

	/**
	 * Retrieves the value of the given {@value #WIFI_GROUP} setting.
	 *
	 * @param setting the setting to get the value.
	 * @return the value, {@code null} if error.
	 */
	private String getWifiSettingValue(String setting) {
		return properties.getProperty(setting);
	}

	/**
	 * Sets the value of the given {@value #WIFI_GROUP} setting.
	 *
	 * @param setting the setting to set the value.
	 * @param value the new value.
	 * @return Error string. Empty string means no error.
	 */
	private String setWifiSettingValue(String setting, String value) {
		return setPropertyValue(setting, value);
	}

	/**
	 * Retrieves the value of the given {@value #MOBILE_GROUP} setting.
	 *
	 * @param setting the setting to get the value.
	 * @return the value, {@code null} if error.
	 */
	private String getMobileSettingValue(String setting) {
		return properties.getProperty(setting);
	}

	/**
	 * Sets the value of the given {@value #MOBILE_GROUP} setting.
	 *
	 * @param setting the setting to set the value.
	 * @param value the new value.
	 * @return Error string. Empty string means no error.
	 */
	private String setMobileSettingValue(String setting, String value) {
		return setPropertyValue(setting, value);
	}

	/**
	 * Sets the value of the given setting.
	 *
	 * @param setting the setting to set the value.
	 * @param value the new value.
	 * @return Error string. Empty string means no error.
	 */
	private String setPropertyValue(String setting, String value) {
		String error = "";
		properties.setProperty(setting, value);
		
		try {
			// Re-create the settings file and open an Output stream.
			connection.delete();
			connection.create();
			properties.store(connection.openOutputStream(), "");
		} catch (IOException ex) {
			error = ex.getMessage();
			Logger.logException(ConnectorLogger.getInstance(), ex);
		}
		return error;
	}

	/**
	 * Retrieves the value of the given {@value #MEMORY_INFO_GROUP} setting.
	 *
	 * @param state the setting to get the value.
	 * @return the value, {@code null} if error.
	 */
	private String getMemoryInfoStateValue(String state) {
		switch (state) {
			case FREE_MEMORY_SETTING:
				return Runtime.getRuntime().freeMemory() + "";
			case MAX_MEMORY_SETTING:
				return Runtime.getRuntime().maxMemory() + "";
			case TOTAL_MEMORY_SETTING:
				return Runtime.getRuntime().totalMemory() + "";
			default:
				// Error: State field does not exist.
				System.out.println("Error: state field '" + state + "' does not exist.");
				return null;
		}
	}
}
