package com.millistream.www;
import java.util.ArrayList;

/**
 * This class represents the connection object between the libmdf client and server
 * 
 * @author Liang Qu
 * @version 0.1, 03/25/10
 */
public class Mdf {
	/**
	 * Handle to the C object, the opaque pointer to the C <code>mdf_s</code> on
	 * the C heap. It's exactly the same as <code>mdf_t</code>.
	 */
	private final long handle;

	/*
	 * Load library "libmdf-java.so", which contains the native implementation.
	 */
	static {
		if (System.getProperty("os.name").startsWith("Windows"))
			System.loadLibrary("libmdf-java");
		else
			System.loadLibrary("mdf-java");

		initialize();
	}

	/**
	 * Constructs a new <code>MessageHeader</code> object. The object is not thread-safe
	 * but different threads can access different objects simultaneously.
	 */
	public Mdf() {
		handle = 0;
		create ();
	}

	protected void finalize() throws Throwable {
		try {
			destroy();
		}

		finally {
			super.finalize();
		}
	};

	private native boolean create();
	private native void destroy();
	private static native void initialize();

	/**
	 * Returns the header of the current message in the stream. Also proceeds
	 * the internal position to the next message in the stream. Performance critical 
	 * and latency sensitive applications should use the
	 * <code>getMessagesAndFields()</code> method instead.
	 * 
	 * @return a new MessageHeader object representing the current message in
	 *         the stream or null if there is no more messages in the stream
	 * @see com.millistream.www.MessageHeader
	 */
	public native MessageHeader get_next_message();

	/**
	 * Returns the current field in the message and proceed the internal
	 * position to the next field in the message. Performance critical and
	 * latency sensitive applications should use the
	 * <code>getMessagesAndFields()</code> method instead.
	 * 
	 * @return a new MessageField object representing the current field in the
	 *         message, or null if there is no more fields in the message
	 * @see com.millistream.www.MessageField
	 */
	public native MessageField get_next_field();

	/**
	 * Calls <code>get_next_message()</code> + <code>get_next_field()</code>
	 * repeatedly and constructs an <code>ArrayList</code> in order to minimize
	 * the amount of Java to native calls for performance critical and latency
	 * sensitive client applications. When this method is used the <code>getFields()</code>
	 * method in the <code>MessageHeader</code> class can be called in order to get
	 * the <code>ArrayList</code> of the fields.
	 * 
	 * @return a new ArrayList object representing all messages received by the
	 *         call to <code>consume()</code> method.
	 * @see com.millistream.www.MessageHeader
	 */
	public native ArrayList<MessageHeader> getMessagesAndFields();

	/**
	 * Connects to a server specified in the servers string. The servers string
	 * can be a comma separated list of <code>"server:port"</code> where server
	 * can be a host name or a raw IP address (IPv6 must be enclosed in brackets
	 * "[address]", if the first server does not respond in time, the next will
	 * be tried and so on until there is no more servers in the list.
	 * 
	 * @param servers
	 *            a comma separated list in the form of "server:port"
	 * @return true if a connection with a server was possible and false if
	 *         connection failed with every server on the list
	 */
	public native boolean connect(String servers);

	/**
	 * Disconnects from the server. Safe to call even if the connection
	 * is already down.
	 */
	public native void disconnect();

	/**
	 * Consumes any bytes from the server (if any). If a complete transaction
	 * has been received, any defined consumeData callback function will be called.
	 * <code>timeout</code> is the time in seconds to wait for data from the
	 * server, if zero the function will return immediately if there is no data
	 * to consume.
	 * 
	 * @param timeout
	 *            time in seconds to wait for data from the server
	 * @return 1 if there are messages to decode and no callback function was
	 *         defined, 0 if the function timeouts and -1 if there was an error
	 * @see com.millistream.www.Callback
	 */
	public native int consume(int timeout);

	/**
	 * Send the message chain to the server.
	 * 
	 * @param message
	 *            the message to be sent to the server
	 * @return true if the message is successfully sent, false if there is an
	 *         error
	 * @see com.millistream.www.Message
	 */
	public native boolean send(Message message);

	/**
	 * Sets the Connection Timeout property,
	 * 
	 * @param value the number of seconds before determining that a connection attempt has timed out. Valid values are 1-60.
	 * @return true if property successfully set, false if cannot be set
	 */
	public native boolean setTimeout(int value);

	/**
	 * Gets the Connection Timeout property.
	 * 
	 * @return timeout in seconds
	 */
	public native int getTimeout();

	/**
	 * Sets the Heartbeat Interval property.
	 * 
	 * @param value the number of seconds the connection must be idle before the API sends a hearbeat request to the server. Valid values are 1-86400.
	 * @return true if property successfully set, false if cannot be set
	 */
	public native boolean setHeartbeatInterval(int value);

	/**
	 * Gets the Heartbeat Interval property.
	 * 
	 * @return the number of seconds the connection must be idle before the API sends a heartbeat request to the server.
	 */
	public native int getHeartbeatInterval();

	/**
	 * Sets the Max Missed Heartbeats property.
	 * 
	 * @param value how many outstanding heartbeat requests to allow before the connection is determined to be disconnected. Valid values are 1-100.
	 * @return true if property successfully set, false if cannot be set
	 */
	public native boolean setHeartbeatMaxMissed(int value);

	/**
	 * Gets Max Missed Heartbeats property.
	 * 
	 * @return how many outstanding heartbeat requests to allow before the connection is determined to be disconnected.
	 */
	public native int getHeartbeatMaxMissed();

	/**
	 * Disable the TCP Nagle algorithm.
	 * 
	 * @param value '1' will disable the Nagle algorithm, '0' will enable it (the default).
	 * @return true if property successfully set, false if cannot be set
	 */
	public native boolean setTCPNoDelay(int value);

	/**
	 * Gets the status of the TCP Nagle algorithm.
	 * 
	 * @return '1' if the TCP Nagle algorithm is disabled, '0' if it's enabled.
	 */
	public native int getTCPNoDelay();

	/**
	 * Disable the Client to Server encryption.
	 * 
	 * @param value '1' will disable client to server traffic encryption, '0' will enable it (the default). Once disabled it cannot be enabled again until the connection is closed.
	 * @return true if property successfully set, false if cannot be set
	 */
	public native boolean setNoEncryption(int value);

	/**
	 * Gets the status of the Client to Server encryption.
	 * 
	 * @return '1' if Client to Server encryption is disabled, '0' if it's enabled.
	 */
	public native int getNoEncryption();

	/**
	 * Gets the Time Difference property.
	 * 
	 * @return the time difference in number of seconds between the client and the server. The value should be added to the current time on the client in order to get the server time. Please note that the value can be negative if the client clock is ahead of the server clock.
	 */
	public native int getTimeDifference();

	/**
	 * Gets the Time DifferenceNS property.
	 * 
	 * @return the time difference in number of nano seconds between the client and the server. The value should be added to the current time on the client in order to get the server time. Please note that the value can be negative if the client clock is ahead of the server clock.
	 */
	public native long getTimeDifferenceNS();

	/**
	 * Sets the "Number of bytes received from the server" property.
	 * 
	 * @param value
	 *            number of bytes, can NOT be negative
	 * @return true if property successfully set, false if cannot be set
	 */
	public native boolean setReceivedBytes(long value);

	/**
	 * Gets the "Number of bytes received from the server" property.
	 * 
	 * @return the number of bytes received by the server
	 */
	public native long getReceivedBytes();

	/**
	 * Sets the "Number of bytes sent to the server" property
	 * 
	 * @param value
	 *            number of bytes, can NOT be negative
	 * @return true if property successfully set, false if cannot be set
	 */
	public native boolean setSentBytes(long value);

	/**
	 * Gets the "Number of bytes sent to the server" property
	 * 
	 * @return the number of bytes sent to the server
	 */
	public native long getSentBytes();

	/**
	 * Gets the internal libmdf error code
	 * 
	 * @return the internal libmdf error code (if any).
	 */
	public native int getError();

	/**
	 * Sets the IPv4 or IPv6 address to bind to before connecting
	 * 
	 * @param address
	 *            a numerical IPv4 or IPv6 address to bind to, can be null if the intention is to unset the bind address
	 * @return true if property successfully set, false if cannot be set
	 */
	public native boolean setBindAddress(String address);

	/**
	 * Gets the (by the user) set IPv4 or IPv6 address to bind to before connecting
	 * 
	 * @return the bind address set by the user.
	 */
	public native String getBindAddress();

	/**
	 * Sets the encryption ciphers that the client will offer to the server upon connect
	 * 
	 * @param ciphers
	 *            a comma separated list of the encryption ciphers offered by the client to the server, can be null if the intention is to use the default list
	 * @return true if property successfully set, false if cannot be set
	 */
	public native String setCiphers(String ciphers);

	/**
	 * Returns the encryption ciphers that the client will offer to the server upon connect
	 * 
	 * @return a comma separated list of the encryption ciphers offered by the client to the server.
	 */
	public native String getCiphers();

	/**
	 * Returns the encryption cipher chosen by the server for the connection, only availably after <code>connect</code> returns
	 * 
	 * @return the encryption cipher used for the connection.
	 */
	public native String getCipher();

	/**
	 * Sets the message digests that the client will offer to the server upon connect
	 * 
	 * @param digests
	 *            a comma separated list of the message digest offered by the client to the server, can be null if the intention is to use the default list
	 * @return true if property successfully set, false if cannot be set
	 */
	public native String setDigests(String digests);

	/**
	 * Returns the message digests that the client will offer to the server upon connect
	 * 
	 * @return a comma separated list of the message digests offered by the client to the server.
	 */
	public native String getDigests();

	/**
	 * Returns the message digest chosen by the server for the connection, only availably after <code>connect</code> returns
	 * 
	 * @return the message digest used for the connection.
	 */
	public native String getDigest();

	/**
	 * Enables/Disables delay-mode. Must be set prior to connect for the setting to take effect.
	 * 
	 * @param value '1' will enable delay-mode, '0' will disable it (the default).
	 * @return true if property successfully set, false if cannot be set
	 */
	public native boolean setHandleDelay(int value);

	/**
	 * Returns if we have enabled delay-mode or not.
	 * 
	 * @return '1' if we have enabled delay-mode, '0' if it's disabled.
	 */
	public native int getHandleDelay();

	/**
	 * Returns the host name of the server we are connected to, only availably after <code>connect</code> returns
	 * 
	 * @return the connected server name.
	 */
	public native String getConnectedHostName();

	/**
	 * Returns the ip of the server we are connected to, only availably after <code>connect</code> returns
	 * 
	 * @return the connected server ip.
	 */
	public native String getConnectedHostIP();

	/**
	 * Registers a consumeData Callback
	 * 
	 * @param callback
	 *            an implementation of <code>Callback</code> interface, can be null if the intention is to unset the callback
	 * @return true if successfully set, false if cannot be set
	 */
	public native boolean setConsumeDataCallback(Callback callback);

	/**
	 * Registers a statusChange Callback
	 * 
	 * @param callback
	 *            an implementation of <code>Callback</code> interface, can be set to null if the intention is to unset the callback
	 * @return true if successfully set, false if cannot be set
	 */
	public native boolean setStatusChangeCallback(Callback callback);
}

