//----------------------------BEGIN LICENSE----------------------------
/* 
 * Willow : the Open Source WorkFlow Project
 * Distributable under GNU LGPL license by gun.org
 *
 * Copyright (C) 2004-2010 huihoo.org 
 * Copyright (C) 2004-2010  ZosaTapo <dertyang@hotmail.com> 
 * 
 * ====================================================================
 * Project Homepage : http://www.huihoo.org/willow
 * Source Forge     : http://sourceforge.net/projects/huihoo
 * Mailing list     : willow@lists.sourceforge.net 
 */
//----------------------------END  LICENSE----------------------------

package org.huihoo.workflow.util;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

import org.huihoo.commons.util.NumberFormat;
import org.huihoo.workflow.WorkflowSerializable;

/**
 * A <code>UID</code> represents an identifier that is unique over time
 * with respect to the host it is generated on, or one of 2<sup>16</sup>
 * "well-known" identifiers.
 *
 * <p>The {@link #UID()} constructor can be used to generate an
 * identifier that is unique over time with respect to the host it is
 * generated on.  The {@link #UID(short)} constructor can be used to
 * create one of 2<sup>16</sup> well-known identifiers.
 *
 * <p>A <code>UID</code> instance contains three primitive values:
 * <ul>
 * <li><code>unique</code>, an <code>int</code> that uniquely identifies
 * the VM that this <code>UID</code> was generated in, with respect to its
 * host and at the time represented by the <code>time</code> value (an
 * example implementation of the <code>unique</code> value would be a
 * process identifier),
 *  or zero for a well-known <code>UID</code>
 * <li><code>time</code>, a <code>long</code> equal to a time (as returned
 * by {@link System#currentTimeMillis()}) at which the VM that this
 * <code>UID</code> was generated in was alive,
 * or zero for a well-known <code>UID</code>
 * <li><code>count</code>, a <code>short</code> to distinguish
 * <code>UID</code>s generated in the same VM with the same
 * <code>time</code> value
 * </ul>
 *
 * <p>An independently generated <code>UID</code> instance is unique
 * over time with respect to the host it is generated on as long as
 * the host requires more than one millisecond to reboot and its system
 * clock is never set backward.  A globally unique identifier can be
 * constructed by pairing a <code>UID</code> instance with a unique host
 * identifier, such as an IP address.
 *
 */
public final class UUID implements WorkflowSerializable
{

	private static long ONE_SECOND = 1000; // in milliseconds
	private static final int hostUnique = getHostUniqueNum();

	private static final Object lock = new Object();
	private static long lastTime = System.currentTimeMillis();
	private static short lastCount = Short.MIN_VALUE;

	/**
	 * number that uniquely identifies the VM that this <code>UID</code>
	 * was generated in with respect to its host and at the given time
	 * @serial
	 */
	private final int unique;

	/**
	 * a time (as returned by {@link System#currentTimeMillis()}) at which
	 * the VM that this <code>UID</code> was generated in was alive
	 * @serial
	 */
	private final long time;

	/**
	 * 16-bit number to distinguish <code>UID</code> instances created
	 * in the same VM with the same time value
	 * @serial
	 */
	private final short count;

	/**
	 * In the absence of an actual pid, just do something somewhat
	 * random.
	 */
	private static int getHostUniqueNum()
	{
		return (new Object()).hashCode();
	}

	/**
	 * Generates a <code>UID</code> that is unique over time with
	 * respect to the host that it was generated on.
	 */
	public UUID()
	{
		unique = hostUnique;

		synchronized (lock)
		{
			if (lastCount == Short.MAX_VALUE)
			{
				boolean done = false;
				while (!done)
				{
					long now = System.currentTimeMillis();
					if (now < lastTime + ONE_SECOND)
					{
						// pause for a second to wait for time to change
						try
						{
							Thread.currentThread().sleep(ONE_SECOND);
						}
						catch (java.lang.InterruptedException e)
						{
						} // ignore exception
						continue;
					}
					else
					{
						lastTime = now;
						lastCount = Short.MIN_VALUE;
						done = true;
					}
				}
			}
			time = lastTime;
			count = lastCount++;
		}
	}

	/**
	 * Creates a "well-known" <code>UID</code>.
	 *
	 * There are 2<sup>16</sup> possible such well-known ids.
	 *
	 * <p>A <code>UID</code> created via this constructor will not
	 * clash with any <code>UID</code>s generated via the no-arg
	 * constructor.
	 *
	 * @param	num number for well-known <code>UID</code>
	 */
	public UUID(short num)
	{
		unique = 0;
		time = 0;
		count = num;
	}

	/**
	 * Constructs a <code>UID</code> given data read from a stream.
	 */
	private UUID(int unique, long time, short count)
	{
		this.unique = unique;
		this.time = time;
		this.count = count;
	}

	/**
	 * Returns the hash code value for this <code>UID</code>.
	 *
	 * @return	the hash code value for this <code>UID</code>
	 */
	public int hashCode()
	{
		return (int) time + (int) count;
	}

	/**
	 * Compares the specified object with this <code>UID</code> for
	 * equality.
	 *
	 * This method returns <code>true</code> if and only if the
	 * specified object is a <code>UID</code> instance with the same
	 * <code>unique</code>, <code>time</code>, and <code>count</code>
	 * values as this one.
	 *
	 * @param	obj the object to compare this <code>UID</code> to
	 *
	 * @return	<code>true</code> if the given object is equivalent to
	 * this one, and <code>false</code> otherwise
	 */
	public boolean equals(Object obj)
	{
		if ((obj != null) && (obj instanceof UUID))
		{
			UUID uid = (UUID) obj;
			return (unique == uid.unique && count == uid.count && time == uid.time);
		}
		else
		{
			return false;
		}
	}

	/**
	 * Returns a string representation of this <code>UID</code>.
	 *
	 * @return	a string representation of this <code>UID</code>
	 */
	public String toString()
	{
		return toString(":");
	}


	/**
	 * Returns a string representation of this <code>UID</code>.
	 *
	 * @return	a string representation of this <code>UID</code>
	 */
	public String toString(String separator)
	{
		return NumberFormat.toString(unique,4) + separator + NumberFormat.toString(time,4) + separator+ NumberFormat.toString(count,4);
	}
	
	/**
	 * Marshals a binary representation of this <code>UID</code> to
	 * a <code>DataOutput</code> instance.
	 *
	 * <p>Specifically, this method first invokes the given stream's
	 * {@link DataOutput#writeInt(int)} method with this <code>UID</code>'s
	 * <code>unique</code> value, then it invokes the stream's
	 * {@link DataOutput#writeLong(long)} method with this <code>UID</code>'s
	 * <code>time</code> value, and then it invokes the stream's
	 * {@link DataOutput#writeShort(int)} method with this <code>UID</code>'s
	 * <code>count</code> value.
	 *
	 * @param	out the <code>DataOutput</code> instance to write
	 * this <code>UID</code> to
	 *
	 * @throws	IOException if an I/O error occurs while performing
	 * this operation
	 */
	public void write(DataOutput out) throws IOException
	{
		out.writeInt(unique);
		out.writeLong(time);
		out.writeShort(count);
	}

	/**
	 * Constructs and returns a new <code>UID</code> instance by
	 * unmarshalling a binary representation from an
	 * <code>DataInput</code> instance.
	 *
	 * <p>Specifically, this method first invokes the given stream's
	 * {@link DataInput#readInt()} method to read a <code>unique</code> value,
	 * then it invoke's the stream's
	 * {@link DataInput#readLong()} method to read a <code>time</code> value,
	 * then it invoke's the stream's
	 * {@link DataInput#readShort()} method to read a <code>count</code> value,
	 * and then it creates and returns a new <code>UID</code> instance
	 * that contains the <code>unique</code>, <code>time</code>, and
	 * <code>count</code> values that were read from the stream.
	 *
	 * @param	in the <code>DataInput</code> instance to read
	 * <code>UID</code> from
	 *
	 * @return	unmarshalled <code>UID</code> instance
	 *
	 * @throws	IOException if an I/O error occurs while performing
	 * this operation
	 */
	public static UUID read(DataInput in) throws IOException
	{
		int unique = in.readInt();
		long time = in.readLong();
		short count = in.readShort();
		return new UUID(unique, time, count);
	}
	
	public final static void main(String[] args)
	{
		System.err.println(new UUID());
	}
}
