package com.millistream.www;

/**
 * This class represents the message chain used for constructing the messages sent to the server.
 * 
 * @author Liang Qu
 * @version 0.1, 03/25/10
 */
public class Message {
	/**
	 * Handle to the C object, the opaque pointer to the C
	 * <code>mdf_message_s</code> on the C heap. It's exactly the same as
	 * <code>mdf_message_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>Message</code> object.
	 * 
	 */
	public Message() {
		handle = 0;
		create();
	}

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

		finally {
			super.finalize();
		}
	};

	/**
	 * Serialize the message chain and return it as a base64 encoded string
	 * @return base64 encoded serialized representation of the message chain
	 *
	*/
	public native String toString();

	/**
	 * Creates a Message from the string returned from the <code>toString()</code> method.
	 * @param string a string returned from the <code>toString()</code> method of a <code>Message</code> class
	 * @return a Message class populated with the message chain from the base64 encoded input string
	 *
	*/
	public static Message fromString(String string) {
		Message m = new Message();

		if (!m.deserialize (string))
			return null;

		return m;
	}

	private native boolean create();
	private native boolean destroy();
	private native boolean deserialize(String str);
	private static native void initialize();

	/**
	 * Sets the compression level as used by <code>add_string</code> when
	 * compressing the strings with zlib.
	 * 
	 * @param level
	 *            compression level between 0-9 where 0 is Z_NO_COMPRESSION and
	 *            9 is Z_BEST_COMPRESSION. Default value is 1 (Z_BEST_SPEED).
	 * @return true if the compression level was successfully changed or false
	 *         if there was an error
	 */
	public native boolean setCompressionLevel(int level);

	/**
	 * Enables (1) or Disables (0) UTF-8 validation of the string given to <code>add_string</code>, the
	 * default is Disabled (0).
	 * 
	 * @param value
	 *            1 to Enable UTF-8 validation or 0 to Disable it.
	 * @return true if the setting was successfully changed or false
	 *         if there was an error
	 */
	public native boolean setUTF8Validation(int value);

	/**
	 * Sets the intended delay for the current and/or future messages. The 
	 * setting will remain until changed (a call to <code>reset</code> will not
	 * reset the setting back to the default). The default value is DLY.BEST.
	 * 
	 * @param value
	 *            The intended delay
	 * @return true if the setting was successfully changed or false
	 *         if there was an error
	 */
	public native boolean setDelay(int value);

	/**
	 * Adds a new message to the message chain. If there is an empty message in
	 * the chain, that message will be reused and no memory will be allocated.
	 * Use <code>message_reference</code> to define which message to send.
	 * 
	 * @param insref
	 *            instrument reference
	 * @param mref
	 *            message reference
	 * @return true if a new message was added (or an old was reused) or false
	 *         if there was an error
	 */
	public native boolean add(long insref, int mref);

	/**
	 * Adds a new message to the message chain. If there is an empty message in
	 * the chain, that message will be reused and no memory will be allocated.
	 * Use <code>message_reference</code> to define which message to send.
	 * 
	 * @param insref
	 *            instrument reference
	 * @param mref
	 *            message reference
	 * @param delay
	 *            message delay
	 * @return true if a new message was added (or an old was reused) or false
	 *         if there was an error
	 */
	public native boolean addWithDelayInfo(long insref, int mref, int delay);

	/**
	 * Removes the current message from the message chain, marking it to be
	 * reused. The current message pointer will change to the previous message
	 * in the chain if there is any, so repeated calls will reset the whole
	 * chain.
	 * 
	 * @return true if there are more messages in the chain or false if the
	 *         message chain is empty
	 */
	public native boolean del();

	/**
	 * Resets the message chain so it can be reused. The same as calling
	 * <code>del()</code> until it returns false.
	 */
	public native void reset();

	/**
	 * Adds a space separated list of unsigned integers to the current message.
	 * @param tag field tag to add
	 * @param value a list of unsigned integers separated by space
	 * @return true if add was successfull, false if not
	 */
	public native boolean add_list(int tag, String value);

	/**
	 * Adds a numeric field to the current message.
	 * @param tag field tag to add
	 * @param value the numeric value to add as a string
	 * @return true if add was successfull, false if not
	 */
	public native boolean add_numeric(int tag, String value);

	/**
	 * Adds a scaled integer field to the current message.
	 * @param tag field tag to add
	 * @param value the scaled integer value
	 * @param decimals the number of fixed decimals in the scaled integer
	 * @return true if add was successfull, false if not
	 */
	public native boolean add_long(int tag, long value, int decimals);

	/**
	 * Adds a string field to the current message.
	 * @param tag field tag to add
	 * @param value the string to add
	 * @return true if add was successfull, false if not
	 */
	public native boolean add_string(int tag, String value);

	/**
	 * Adds a date field to the current message.
	 * @param tag field tag to add
	 * @param value the date as a string in "YYYY-MM-DD", "YYYY-MM", "YYYY-Hx", "YYYY-Tx", "YYYY-Qx" or "YYYY-Wxx" format
	 * @return true if add was successfull, false if not
	 */
	public native boolean add_date(int tag, String value);

	/**
	 * Adds a date field to the current message without having to construct it as a string before.
	 * @param tag field tag to add
	 * @param year the Year part of the date to add
	 * @param month the Month part of the date to add
	 * @param day the Day part of the date to add
	 * @return true if add was successfull, false if not
	 */
	public native boolean add_date2(int tag, int year, int month, int day);

	/**
	 * Adds a time field to the current message.
	 * @param tag field tag to add
	 * @param value the time as a string in "HH:MM:SS" or "HH:MM:SS.mmm" format
	 * @return true if add was successfull, false if not
	 */
	public native boolean add_time(int tag, String value);

	/**
	 * Adds a time field to the current message without having to construct it as a string before.
	 * @param tag field tag to add
	 * @param hour the Hour part of the timestamp
	 * @param min the Minutes part of the timestamp
	 * @param sec the Seconds part of the timestamp
	 * @param msec the Milliseconds part of the timestamp
	 * @return true if add was successfull, false if not
	 */
	public native boolean add_time2(int tag, int hour, int min, int sec, int msec);

	/**
	 * Adds a time field to the current message without having to construct it as a string before, with nanosecond support.
	 * @param tag field tag to add
	 * @param hour the Hour part of the timestamp
	 * @param min the Minutes part of the timestamp
	 * @param sec the Seconds part of the timestamp
	 * @param nsec the Nanoseconds part of the timestamp
	 * @return true if add was successfull, false if not
	 */
	public native boolean add_time3(int tag, int hour, int min, int sec, int nsec);

	/**
	 * Return the total number of messages in the chain. The difference between this and the number returned by the {@link #get_num_active() get_num_active} method
	 * is that the message chain only grows as you add new messages to it and it never shrinks even after a call to {@link #reset() reset} or {@link #del() del}.
	 * So <code>get_num()</code> returns the current absolute size of the message chain, i.e the number of messages it can contain before it needs to 
	 * (automatically) be resized.
	 *
	 * @return the total number of messages in the chain
	 */
	public native int get_num();

	/**
	 * Return the number of active messages in the chain.
	 *
	 * @return the number of active messages in the chain
	 */
	public native int get_num_active();


	/**
	 * Moves all messages with an insref matching <code>insrefFrom</code> to the Message at <code>dst</code>
	 * and change it to <code>insrefTo</code>. If <code>dst</code> is set to the source object or
	 * <code>null</code> then the change from <code>insrefFrom</code> to <code>insrefTo</code> will be done in-place.
	 * @param dst the <code>Message</code> to move to or <code>null</code> if in-place renaming of insrefs are to be done.
	 * @param insrefFrom the insref to change from
	 * @param insrefTo the insref to change to
	 * @return true if add was successfull, false if not
	 */
	public native boolean move(Message dst, long insrefFrom, long insrefTo);
}
