package com.yihu.hos.common;

import java.io.Serializable;
import java.security.SecureRandom;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author HZY
 * @vsrsion 1.0
 * Created at 2016/8/8.
 */
public final class ObjectVersion implements Comparable<ObjectVersion>, Serializable {
    private static final long serialVersionUID = 789056789654456072L;
    private static final int BYTE_ARRAY_LENGTH = 6;
    private static final AtomicInteger NEXT_COUNTER = new AtomicInteger((new SecureRandom()).nextInt());
    private static final char[] HEX_CHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    private final int timestamp;
    private final short counter;

    public static boolean isValid(String hexString) {
        if(hexString == null) {
            throw new IllegalArgumentException();
        } else {
            int len = hexString.length();
            if(len != 12) {
                return false;
            } else {
                for(int i = 0; i < len; ++i) {
                    char c = hexString.charAt(i);
                    if((c < 48 || c > 57) && (c < 97 || c > 102) && (c < 65 || c > 70)) {
                        return false;
                    }
                }

                return true;
            }
        }
    }

    public static int getCurrentCounter() {
        return NEXT_COUNTER.get();
    }

    public ObjectVersion() {
        this(new Date());
    }

    public ObjectVersion(Date date) {
        this(dateToTimestampSeconds(date), (short)NEXT_COUNTER.getAndIncrement());
    }

    public ObjectVersion(Date date, short counter) {
        this(dateToTimestampSeconds(date), counter);
    }

    public ObjectVersion(int timestamp, short counter) {
        this.timestamp = timestamp;
        this.counter = counter;
    }

    public ObjectVersion(String hexString) {
        this(parseHexString(hexString));
    }

    public ObjectVersion(byte[] bytes) {
        if(bytes == null) {
            throw new IllegalArgumentException();
        } else if(bytes.length != 6) {
            throw new IllegalArgumentException("need 6 bytes");
        } else {
            this.timestamp = makeInt(bytes[0], bytes[1], bytes[2], bytes[3]);
            this.counter = (short)makeInt((byte)0, (byte)0, bytes[4], bytes[5]);
        }
    }

    public byte[] toByteArray() {
        byte[] bytes = new byte[]{int3(this.timestamp), int2(this.timestamp), int1(this.timestamp), int0(this.timestamp), int1(this.counter), int0(this.counter)};
        return bytes;
    }

    public Date getDate() {
        return new Date((long)this.timestamp * 1000L);
    }

    public int getTimestamp() {
        return this.timestamp;
    }

    public short getCounter() {
        return this.counter;
    }

    public String toHexString() {
        char[] chars = new char[12];
        int i = 0;
        byte[] var3 = this.toByteArray();
        int var4 = var3.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            byte b = var3[var5];
            chars[i++] = HEX_CHARS[b >> 4 & 15];
            chars[i++] = HEX_CHARS[b & 15];
        }

        return new String(chars);
    }

    public boolean equals(Object o) {
        if(this == o) {
            return true;
        } else if(o != null && this.getClass() == o.getClass()) {
            ObjectVersion objectId = (ObjectVersion)o;
            return this.counter != objectId.counter?false:this.timestamp == objectId.timestamp;
        } else {
            return false;
        }
    }

    public int hashCode() {
        int result = this.timestamp;
        result = 31 * result + this.counter;
        return result;
    }

    public int compareTo(ObjectVersion other) {
        if(other == null) {
            throw new NullPointerException();
        } else {
            byte[] byteArray = this.toByteArray();
            byte[] otherByteArray = other.toByteArray();

            for(int i = 0; i < 6; ++i) {
                if(byteArray[i] != otherByteArray[i]) {
                    return (byteArray[i] & 255) < (otherByteArray[i] & 255)?-1:1;
                }
            }

            return 0;
        }
    }

    public String toString() {
        return this.toHexString();
    }

    private static byte[] parseHexString(String s) {
        if(!isValid(s)) {
            throw new IllegalArgumentException("invalid hexadecimal representation of an ObjectId: [" + s + "]");
        } else {
            byte[] b = new byte[6];

            for(int i = 0; i < b.length; ++i) {
                b[i] = (byte)Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16);
            }

            return b;
        }
    }

    private static int dateToTimestampSeconds(Date time) {
        return (int)(time.getTime() / 1000L);
    }

    private static int makeInt(byte b3, byte b2, byte b1, byte b0) {
        return b3 << 24 | (b2 & 255) << 16 | (b1 & 255) << 8 | b0 & 255;
    }

    private static byte int3(int x) {
        return (byte)(x >> 24);
    }

    private static byte int2(int x) {
        return (byte)(x >> 16);
    }

    private static byte int1(int x) {
        return (byte)(x >> 8);
    }

    private static byte int0(int x) {
        return (byte)x;
    }
}