/* JFox, the OpenSource J2EE Application Server
 *
 * Distributable under GNU LGPL license by gun.org
 * more details please visit http://www.huihoo.org/jfox
 */

package org.jfox.framework.util;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.net.InetAddress;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

/**
 * @author <a href="mailto:young_yy@hotmail.com">Young Yang</a>
 */

public class VMID implements Serializable {

    // unique vm uuid
    private UUID vmUUID = UUID.randomUUID();

    private byte[] address = getLocalAddress();

    private static MessageDigest md;

    static {
        try {
            md = MessageDigest.getInstance("SHA");
        }
        catch(NoSuchAlgorithmException e) {
            try {
                md = MessageDigest.getInstance("MD5");
            }
            catch(NoSuchAlgorithmException ex) {
                throw new RuntimeException(ex);
            }
        }
    }

    private static VMID instance = new VMID();

    private VMID() {
    }

    public static VMID getInstance() {
        return instance;
    }

    public byte[] getAddress() {
        return address;
    }

    private static byte[] getLocalAddress() {
        try {
            return InetAddress.getLocalHost().getAddress();
        }
        catch(Exception e) {
            return new byte[]{127, 0, 0, 1};
        }
    }

    public String toString() {
        StringBuffer buff = new StringBuffer();
        byte[] addr = computeAddressHash(address);
        for(int i = 0; i < addr.length; i++) {
            int n = (int) (addr[i] & 0xFF);
            buff.append(Integer.toString(n, Character.MAX_RADIX));
        }
        buff.append("-").append(vmUUID.toString());

        return buff.toString();
    }

    public boolean equals(Object o) {
        if(this == o) return true;
        if(!(o instanceof VMID)) return false;

        final VMID vmid = (VMID) o;

        if(!Arrays.equals(address, vmid.address)) return false;
        if(vmUUID != null ? !vmUUID.equals(vmid.vmUUID) : vmid.vmUUID != null) return false;

        return true;
    }

    public int hashCode() {
        return (vmUUID != null ? vmUUID.hashCode() : 0);
    }


    /**
     * Compute the hash an IP address.  The hash is the first 8 bytes
     * of the SHA digest of the IP address.
     */
    private static byte[] computeAddressHash(byte[] addr) {

        /*
        * Get the local host's IP address.
        */
//    byte[] addr = address;

        byte[] addrHash;
        final int ADDR_HASH_LENGTH = 8;

        try {
            /*
            * Calculate message digest of IP address using SHA.
            */

            ByteArrayOutputStream sink = new ByteArrayOutputStream(64);
            DataOutputStream out = new DataOutputStream(new DigestOutputStream(sink, md));
            out.write(addr, 0, addr.length);
            out.flush();

            byte digest[] = md.digest();
            int hashlength = Math.min(ADDR_HASH_LENGTH, digest.length);
            addrHash = new byte[hashlength];
            System.arraycopy(digest, 0, addrHash, 0, hashlength);

        }
        catch(IOException ignore) {
            /* can't happen, but be deterministic anyway. */
            addrHash = new byte[0];
        }
        return addrHash;
    }

    /**
     * is vmid is stay in current VM
     *
     * @return
     */
    public boolean isCurrentVM() {
        return this.equals(VMID.getInstance());
    }

    public boolean isSameVM(VMID vmid) {
        return this.equals(vmid);
    }


    public static void main(String[] args) {
        System.out.println(new VMID().toString());
        System.out.println(new VMID().toString());
        System.out.println(new VMID().toString());
        System.out.println(VMID.getInstance().isCurrentVM());
    }
}
