/*
 * Copyright (C) 2005 Alfresco, Inc.
 *
 * Licensed under the Mozilla Public License version 1.1 
 * with a permitted attribution clause. You may obtain a
 * copy of the License at
 *
 *   http://www.alfresco.org/legal/license.txt
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific
 * language governing permissions and limitations under the
 * License.
 */
package org.alfresco.filesys.smb.dcerpc;

import org.alfresco.filesys.util.DataPacker;

/**
 * Universal Unique Identifier Class
 */
public class UUID
{

    // UUID constants

    public static final int UUID_LENGTH = 36;
    public static final int UUID_LENGTH_BINARY = 16;
    private static final String UUID_VALIDCHARS = "0123456789ABCDEFabcdef";

    // UUID string

    private String m_uuid;

    // Interface version

    private int m_ifVersion;

    // UUID bytes

    private byte[] m_uuidBytes;

    /**
     * Class constructor
     * 
     * @param id String
     */
    public UUID(String id)
    {
        if (validateUUID(id))
        {
            m_uuid = id;
            m_ifVersion = 1;
        }
    }

    /**
     * Class constructor
     * 
     * @param id String
     * @param ver int
     */
    public UUID(String id, int ver)
    {
        if (validateUUID(id))
        {
            m_uuid = id;
            m_ifVersion = ver;
        }
    }

    /**
     * Class constructor
     * 
     * @param buf byte[]
     * @param off int
     */
    public UUID(byte[] buf, int off)
    {

        // Copy the UUID bytes and generate the UUID string

        if ((off + UUID_LENGTH_BINARY) <= buf.length)
        {

            // Take a copy of the UUID bytes

            m_uuidBytes = new byte[UUID_LENGTH_BINARY];
            for (int i = 0; i < UUID_LENGTH_BINARY; i++)
                m_uuidBytes[i] = buf[off + i];

            // Generate the string version of the UUID

            m_uuid = generateUUIDString(m_uuidBytes);
        }
    }

    /**
     * Determine if the UUID is valid
     * 
     * @return boolean
     */
    public final boolean isValid()
    {
        return m_uuid != null ? true : false;
    }

    /**
     * Return the UUID string
     * 
     * @return String
     */
    public final String getUUID()
    {
        return m_uuid;
    }

    /**
     * Return the interface version
     * 
     * @return int
     */
    public final int getVersion()
    {
        return m_ifVersion;
    }

    /**
     * Set the interface version
     * 
     * @param ver int
     */
    public final void setVersion(int ver)
    {
        m_ifVersion = ver;
    }

    /**
     * Return the UUID as a byte array
     * 
     * @return byte[]
     */
    public final byte[] getBytes()
    {

        // Check if the byte array has been created

        if (m_uuidBytes == null)
        {

            // Allocate the byte array

            m_uuidBytes = new byte[UUID_LENGTH_BINARY];

            try
            {

                // Convert the first integer and pack into the buffer

                String val = m_uuid.substring(0, 8);
                long lval = Long.parseLong(val, 16);
                DataPacker.putIntelInt((int) (lval & 0xFFFFFFFF), m_uuidBytes, 0);

                // Convert the second word and pack into the buffer

                val = m_uuid.substring(9, 13);
                int ival = Integer.parseInt(val, 16);
                DataPacker.putIntelShort(ival, m_uuidBytes, 4);

                // Convert the third word and pack into the buffer

                val = m_uuid.substring(14, 18);
                ival = Integer.parseInt(val, 16);
                DataPacker.putIntelShort(ival, m_uuidBytes, 6);

                // Convert the fourth word and pack into the buffer

                val = m_uuid.substring(19, 23);
                ival = Integer.parseInt(val, 16);
                DataPacker.putShort((short) (ival & 0xFFFF), m_uuidBytes, 8);

                // Convert the final block of hex pairs to bytes

                int strPos = 24;
                int bytPos = 10;

                for (int i = 0; i < 6; i++)
                {
                    val = m_uuid.substring(strPos, strPos + 2);
                    m_uuidBytes[bytPos++] = (byte) (Short.parseShort(val, 16) & 0xFF);
                    strPos += 2;
                }
            }
            catch (NumberFormatException ex)
            {
                m_uuidBytes = null;
            }
        }

        // Return the UUID bytes

        return m_uuidBytes;
    }

    /**
     * Validate a UUID string
     * 
     * @param idStr String
     * @reutrn boolean
     */
    public static final boolean validateUUID(String idStr)
    {

        // Check if the UUID string is the correct length

        if (idStr == null || idStr.length() != UUID_LENGTH)
            return false;

        // Check for seperators

        if (idStr.charAt(8) != '-' || idStr.charAt(13) != '-' || idStr.charAt(18) != '-' || idStr.charAt(23) != '-')
            return false;

        // Check for hex digits

        int i = 0;
        for (i = 0; i < 8; i++)
            if (UUID_VALIDCHARS.indexOf(idStr.charAt(i)) == -1)
                return false;
        for (i = 9; i < 13; i++)
            if (UUID_VALIDCHARS.indexOf(idStr.charAt(i)) == -1)
                return false;
        for (i = 14; i < 18; i++)
            if (UUID_VALIDCHARS.indexOf(idStr.charAt(i)) == -1)
                return false;
        for (i = 19; i < 23; i++)
            if (UUID_VALIDCHARS.indexOf(idStr.charAt(i)) == -1)
                return false;
        for (i = 24; i < 36; i++)
            if (UUID_VALIDCHARS.indexOf(idStr.charAt(i)) == -1)
                return false;

        // Valid UUID string

        return true;
    }

    /**
     * Generate a UUID string from the binary representation
     * 
     * @param buf byte[]
     * @return String
     */
    public static final String generateUUIDString(byte[] buf)
    {

        // Build up the UUID string

        StringBuffer str = new StringBuffer(UUID_LENGTH);

        // Convert the first longword

        int ival = DataPacker.getIntelInt(buf, 0);
        str.append(Integer.toHexString(ival));
        while (str.length() != 8)
            str.insert(0, ' ');
        str.append("-");

        // Convert the second word

        ival = DataPacker.getIntelShort(buf, 4) & 0xFFFF;
        str.append(Integer.toHexString(ival));
        while (str.length() != 13)
            str.insert(9, '0');
        str.append("-");

        // Convert the third word

        ival = DataPacker.getIntelShort(buf, 6) & 0xFFFF;
        str.append(Integer.toHexString(ival));
        while (str.length() != 18)
            str.insert(14, '0');
        str.append("-");

        // Convert the remaining bytes

        for (int i = 8; i < UUID_LENGTH_BINARY; i++)
        {

            // Get the current byte value and add to the string

            ival = (int) (buf[i] & 0xFF);
            if (ival < 16)
                str.append('0');
            str.append(Integer.toHexString(ival));

            // Add the final seperator

            if (i == 9)
                str.append("-");
        }

        // Return the UUID string

        return str.toString();
    }

    /**
     * Compare a UUID with the current UUID
     * 
     * @param id UUID
     * @return boolean
     */
    public final boolean compareTo(UUID id)
    {

        // Compare the UUID versions

        if (getVersion() != id.getVersion())
            return false;

        // Compare the UUID bytes

        byte[] thisBytes = getBytes();
        byte[] idBytes = id.getBytes();

        for (int i = 0; i < UUID_LENGTH_BINARY; i++)
            if (thisBytes[i] != idBytes[i])
                return false;
        return true;
    }

    /**
     * Write the binary UUID to the specified buffer, and optionally the UUID version
     * 
     * @param buf byte[]
     * @param off int
     * @param writeVer boolean
     * @return int
     */
    public final int storeUUID(byte[] buf, int off, boolean writeVer)
    {

        // Get the UUID bytes

        int pos = off;
        byte[] uuidByts = getBytes();
        if (uuidByts == null)
            return pos;

        // Write the binary UUID to the buffer

        for (int i = 0; i < UUID_LENGTH_BINARY; i++)
            buf[pos + i] = uuidByts[i];
        pos += UUID_LENGTH_BINARY;

        // Check if version should be written to the buffer

        if (writeVer)
        {
            DataPacker.putIntelInt(getVersion(), buf, pos);
            pos += 4;
        }

        // Return the new buffer position

        return pos;
    }

    /**
     * Return the UUID as a string
     * 
     * @return String
     */
    public String toString()
    {
        StringBuffer str = new StringBuffer();

        str.append("[");
        str.append(m_uuid);
        str.append(":");
        str.append(m_ifVersion);
        str.append("]");

        return str.toString();
    }

    /***********************************************************************************************
     * Test Code
     * 
     * @param args String[]
     */
    /**
     * public final static void main(String[] args) { System.out.println("UUID Test");
     * System.out.println("---------"); String[] uuids = { "12345678-1234-abcd-ef00-01234567cffb",
     * "8a885d04-1ceb-11c9-9fe8-08002b104860", "338cd001-2244-31f1-aaaa-900038001003",
     * "367abb81-9844-35f1-ad32-98f038001003", "4b324fc8-1670-01d3-1278-5a47bf6ee188",
     * "6bffd098-a112-3610-9833-46c3f87e345a", "12345678-1234-abcd-ef00-0123456789ac",
     * "12345778-1234-abcd-ef00-0123456789ab", "1ff70682-0a51-30e8-076d-740be8cee98b" }; // Validate
     * and convert the UUIDs for ( int i = 0; i < uuids.length; i++) { UUID u = new UUID(uuids[i]);
     * if ( u.isValid()) { System.out.println("" + (i+1) + ": " + u.toString()); byte[] bytes =
     * u.getBytes(); HexDump.Dump(bytes,bytes.length, 0); System.out.println("Convert to string: " +
     * generateUUIDString(bytes)); } else System.out.println("Invalid UUID: " + uuids[i]); } }
     */
}