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

import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;

import javax.microedition.io.AccessPoint;
import javax.microedition.io.Connector;
import javax.microedition.io.ConnectionOption;
import javax.microedition.io.SocketConnection;

import com.etherios.connector.core.network.ISocket;

/**
 * Class which handles the Cloud Connector socketCon.
 */

public class ConnectorSocket implements ISocket {

	// Variables
	private String host;

	private int port;

	private SocketConnection socket;

	/**
	 * Class constructor. Instances a new object of type ConnectorSocket
	 * with the given parameters.
	 * 
	 * @param host Socket destination host.
	 * @param port Socket destination port.
	 */
	public ConnectorSocket(String host, int port) {
		this.host = host;
		this.port = port;
	}

	@Override
	public void close() throws Exception {
		if (socket != null) {
			socket.close();
			socket = null;
		}
	}

	@Override
	public InputStream getInputStream() throws Exception {
		socket = openSocketConnection();
		if (socket == null)
			return null;
		return socket.openInputStream();
	}

	@Override
	public OutputStream getOutputStream() throws Exception {
		socket = openSocketConnection();
		if (socket == null)
			return null;
		return socket.openOutputStream();
	}

	@Override
	public boolean isClosed() {
		return socket == null;
	}

	/**
	 * Returns the socketCon connection.
	 * 
	 * @return the socketCon connection.
	 */
	public SocketConnection getSocketConnection() {
		return socket;
	}

	/**
	 * Returns an open socketCon connection to host:port.
	 * <p>First it tries with the default access point, if it is not connected, it 
	 * will look for a connected one in the following order:</p>
	 * 
	 * <p>WIRED, WIFI, 3GPP, CDMA, DIALUP, LOOPBACK</p>
	 * 
	 * @return an open socketCon connection or <code>null</code> if there is not
	 *         any access point connected.
	 * @throws IOException if any error occur while opening the socket connection.
	 */
	private SocketConnection openSocketConnection() throws IOException {
		if (socket != null)
			return socket;
		// Try first with the default access point
		SocketConnection socketCon = (SocketConnection)Connector.open("socket://" + host + ":" + port);
		if (socketCon != null && socketCon.getAccessPoint() != null
				&& socketCon.getAccessPoint().isConnected())
			return socketCon;
		// Look for a connected access point using the following priorities:
		// WIRED, WIFI, 3GPP, CDMA, DIALUP, LOOPBACK
		String[] typesPriority = new String[]{"WIRED", "WIFI", "3GPP", 
												"CDMA", "DIALUP", "LOOPBACK"};
		AccessPoint ap = null;
		for (int i = 0; i < typesPriority.length; i++) {
			ap = getActiveAccessPoint(typesPriority[i]);
			if (ap != null) 
				break;
		}
		if (ap == null)
			return null;
		ConnectionOption apOption = new ConnectionOption("AccessPoint", ap);
		socketCon = (SocketConnection)Connector.open("socket://" + host + ":" + port, apOption);
		return socketCon;
	}

	/**
	 * Get the active access point of the given network interface type.
	 * The active access point is the first found access point connected for the
	 * given network interface type.
	 * 
	 * @param type the network interface type 
	 *             (WIRED, WIFI, 3GPP, CDMA, DIALUP, LOOPBACK)
	 * @return the first access point connected found for the given network 
	 *         interface type, <code>null</code> if none.
	 */
	private AccessPoint getActiveAccessPoint(String type) {
		AccessPoint[] caps = AccessPoint.getAccessPoints(true);
		for (int i = 0; i < caps.length; i++) {
			if (caps[i].getType().equals(type))
				return caps[i];
		}
		return null;
	}
}
