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

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;
import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

import com.etherios.connector.IPlatformCustomization;
import com.etherios.connector.core.facilities.rci.IRCIExtension;
import com.etherios.connector.core.facilities.rci.descriptor.DescriptorHandler;
import com.etherios.connector.core.facilities.rci.descriptor.QueryDescriptor;
import com.etherios.connector.core.log.Logger;
import com.etherios.connector.core.utils.BooleanUtils;
import com.etherios.connector.preferences.PreferencesManager;

/**
 * RCI manager to handle remote configuration requests.
 * 
 * <p>The RCI manager loads the base platform descriptor file 
 * {@value #DESCRIPTOR_FILE} embedded in the jar file.</p>
 * 
 * <p>This class allows the extension of the RCI base descriptor by the 
 * applications that implements the interface {@link IGeneralRCIExtension}. 
 * This implementation must be provided to the 
 * {@link com.etherios.connector.emulator.CloudConnector} constructor.</p>
 * 
 * <p>This manager will call the different methods of the 
 * {@code IGeneralRCIExtension} when getting or setting an RCI value if the 
 * parameter {@value #PARAM_EXTEND_RCI} is set to {@code True} in the 
 * {@code connector_config.txt} file.</p>
 * 
 * <p>The location of the custom descriptor in the application is determined by 
 * the settings {@value #PARAM_DESCRIPTOR_TYPE} and 
 * {@value #PARAM_DESCRIPTOR_LOC}:</p>
 * 
 * <ul>
 * <li>{@value #DESCRIPTOR_TYPE_EMBEDDED} Cloud Connector will look for the 
 * descriptor file, defined in {@value #PARAM_DESCRIPTOR_TYPE}, inside the 
 * jar.</li>
 * <li>{@value #DESCRIPTOR_TYPE_EXTERNAL} Cloud Connector will look for the 
 * file defined {@value #PARAM_DESCRIPTOR_TYPE}in the file system of the device.</li>
 * </ul>
 * 
 * @see IGeneralRCIExtension
 * @see IRCIExtension
 */
public class GeneralRCIManager implements IRCIExtension {

	// Constants
	// RCI extended options
	/**
	 * 'Extend RCI' setting name: {@value}.
	 * <p>This setting allows the custom extension of the base descriptor.</p>
	 * @see #PARAM_DESCRIPTOR_TYPE
	 * @see #PARAM_DESCRIPTOR_LOC
	 * @see #getParameterValue(java.lang.String)
	 */
	public final static String PARAM_EXTEND_RCI = "extend_rci";
	/**
	 * 'Descriptor type' setting name: {@value}.
	 * <p>This setting specifies the type of the custom descriptor file:
	 * {@link #DESCRIPTOR_TYPE_EMBEDDED} ({@value #DESCRIPTOR_TYPE_EMBEDDED})
	 * or {@link #DESCRIPTOR_TYPE_EXTERNAL} ({@value #DESCRIPTOR_TYPE_EXTERNAL}).</p>
	 * @see #DESCRIPTOR_TYPE_EMBEDDED
	 * @see #DESCRIPTOR_TYPE_EXTERNAL
	 * @see #PARAM_EXTEND_RCI
	 * @see #PARAM_DESCRIPTOR_LOC
	 * @see #getParameterValue(java.lang.String)
	 */
	public final static String PARAM_DESCRIPTOR_TYPE = "descriptor_type";
	/**
	 * 'Descriptor location' setting name: {@value}.
	 * <p>This setting specifies the location of the custom descriptor file.</p>
	 * @see #PARAM_EXTEND_RCI
	 * @see #PARAM_DESCRIPTOR_TYPE
	 * @see #getParameterValue(java.lang.String)
	 */
	public final static String PARAM_DESCRIPTOR_LOC = "descriptor_loc";
	
	/**
	 * Embedded RCI descriptor: {@value}.
	 * @see #PARAM_DESCRIPTOR_TYPE
	 * @see #DESCRIPTOR_TYPE_EXTERNAL
	 */
	public final static String DESCRIPTOR_TYPE_EMBEDDED = "embedded";
	/**
	 * External RCI descriptor: {@value}.
	 * @see #PARAM_DESCRIPTOR_TYPE
	 * @see #DESCRIPTOR_TYPE_EMBEDDED
	 */
	public final static String DESCRIPTOR_TYPE_EXTERNAL = "external";
	
	private final static String DESCRIPTOR_FILE = "/__emulator-basic-descriptor-140320__.xml";
	private final static String FILE_PREFIX = "file://";
	
	// Descriptor names
	//private final static String DESCRIPTOR_EMULATOR_CELLULAR = "__emulator_cellular__";
	//private final static String DESCRIPTOR_EMULATOR_BATTERY = "__emulator_battery__";
	
	// Variables
	private IPlatformCustomization platformCustomization;
	private IGeneralRCIExtension generalRCIExtension;
	
	/**
	 * Class constructor. Instances a new General RCI Manager object.
	 * 
	 * @param platformCustomization Platform customization.
	 */
	public GeneralRCIManager(IPlatformCustomization platformCustomization) {
		this(platformCustomization, null);
	}
	
	/**
	 * Class constructor. Instances a new General RCI Manager object with the 
	 * given General RCI extension.
	 * 
	 * @param platformCustomization Platform customization.
	 * @param generalRCIExtension RCI extension used to extend RCI functionality.
	 */
	public GeneralRCIManager(IPlatformCustomization platformCustomization, IGeneralRCIExtension generalRCIExtension) {
		this.platformCustomization = platformCustomization;
		this.generalRCIExtension = generalRCIExtension;
	}
	
	@Override
	public InputStream getDescriptorFile() {
		// Check if custom descriptor is enabled.
		String extendRCI = getParameterValue(PARAM_EXTEND_RCI);
		if (extendRCI != null && BooleanUtils.parseBoolean(extendRCI) && generalRCIExtension != null) {
			// Read custom descriptor type and location.
			String customDescriptorType = getParameterValue(PARAM_DESCRIPTOR_TYPE);
			String customDescriptorLoc = getParameterValue(PARAM_DESCRIPTOR_LOC);
			if (customDescriptorLoc == null || customDescriptorLoc.length() == 0)
				return Class.class.getClass().getResourceAsStream(DESCRIPTOR_FILE);
			InputStream customDescriptorStream = null;
			// Check the custom descriptor type to get the data stream.
			if (customDescriptorType.trim().equalsIgnoreCase(DESCRIPTOR_TYPE_EMBEDDED))
				customDescriptorStream = Class.class.getClass().getResourceAsStream(customDescriptorLoc);
			else if (customDescriptorType.trim().equalsIgnoreCase(DESCRIPTOR_TYPE_EXTERNAL)) {
				FileConnection externalDescriptorFile;
				try {
					externalDescriptorFile = (FileConnection)Connector.open(FILE_PREFIX + customDescriptorLoc);
					if (externalDescriptorFile.exists())
						customDescriptorStream = externalDescriptorFile.openInputStream();
				} catch (IOException e) {
					Logger.logException(platformCustomization.getConnectorLogger(), e);
					return Class.class.getClass().getResourceAsStream(DESCRIPTOR_FILE);
				}
			}
			// Now read general descriptor and custom descriptor and combine them.
			if (customDescriptorStream != null) {
				try {
					QueryDescriptor descriptor = DescriptorHandler.parseDescriptorFile(Class.class.getClass().getResourceAsStream(DESCRIPTOR_FILE));
					QueryDescriptor customDescriptor = DescriptorHandler.parseDescriptorFile(customDescriptorStream);
					if (customDescriptor != null)
						descriptor.extendDescriptor(customDescriptor);
					// Return a data stream of the combined descriptor.
					return new ByteArrayInputStream(descriptor.toString().getBytes());
				} catch (ParserConfigurationException e) {
					Logger.logException(platformCustomization.getConnectorLogger(), e);
					return Class.class.getClass().getResourceAsStream(DESCRIPTOR_FILE);
				} catch (SAXException e) {
					Logger.logException(platformCustomization.getConnectorLogger(), e);
					return Class.class.getClass().getResourceAsStream(DESCRIPTOR_FILE);
				} catch (IOException e) {
					Logger.logException(platformCustomization.getConnectorLogger(), e);
					return Class.class.getClass().getResourceAsStream(DESCRIPTOR_FILE);
				} finally {
					try {
						customDescriptorStream.close();
					} catch (IOException e) {}
				}
			}
		}
		return Class.class.getClass().getResourceAsStream(DESCRIPTOR_FILE);
	}

	@Override
	public String getSettingValue(String group, String setting, int index, String name) {
		String settingValue = null;
		/*if (group.equalsIgnoreCase(DESCRIPTOR_EMULATOR_CELLULAR)
				|| group.equalsIgnoreCase(DESCRIPTOR_EMULATOR_BATTERY))
			settingValue = EmulatorSettings.getSettingValue(setting);
		// If null, try to get it from the extended descriptor*/
		if (settingValue == null && generalRCIExtension != null)
			settingValue = generalRCIExtension.getSettingValue(group, setting, index, name);
		return settingValue;
	}

	@Override
	public String setSettingValue(String group, String setting, String value, int index, String name) {
		String response = null;
		if (generalRCIExtension != null)
			response = generalRCIExtension.setSettingValue(group, setting, value, index, name);
		return response;
	}

	@Override
	public String handleZigbeeRequest(String data) {
		String response = null;
		if (generalRCIExtension != null)
			response = generalRCIExtension.handleZigbeeRequest(data);
		return response;
	}

	/**
	 * Retrieves the value of the given parameter name.
	 * 
	 * @param parameter the parameter name to get its value.
	 * @return a string representing the value of the given parameter name.
	 */
	public String getParameterValue(String parameter) {
		return ((PreferencesManager)platformCustomization.getConnectorPreferences()).getParameterValue(parameter);
	}
}
