// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.codec.codecs;

import com.hypixel.hytale.codec.schema.config.StringSchema;
import com.hypixel.hytale.codec.schema.config.Schema;
import com.hypixel.hytale.codec.schema.SchemaContext;
import java.util.Base64;
import java.io.IOException;
import com.hypixel.hytale.codec.util.RawJsonReader;
import org.bson.BsonBinary;
import com.hypixel.hytale.codec.exception.CodecException;
import org.bson.BsonBinarySubType;
import com.hypixel.hytale.codec.ExtraInfo;
import javax.annotation.Nonnull;
import org.bson.BsonValue;
import java.util.UUID;
import com.hypixel.hytale.codec.Codec;

public class UUIDBinaryCodec implements Codec<UUID>
{
    @Nonnull
    @Override
    public UUID decode(@Nonnull final BsonValue bsonValue, final ExtraInfo extraInfo) {
        final BsonBinary bsonBinary = bsonValue.asBinary();
        final byte subType = bsonBinary.getType();
        if (subType != BsonBinarySubType.UUID_STANDARD.getValue()) {
            throw new CodecException("Unexpected BsonBinarySubType");
        }
        final byte[] bytes = bsonBinary.getData();
        return uuidFromBytes(bytes);
    }
    
    @Nonnull
    @Override
    public BsonValue encode(@Nonnull final UUID uuid, final ExtraInfo extraInfo) {
        final byte[] binaryData = new byte[16];
        writeLongToArrayBigEndian(binaryData, 0, uuid.getMostSignificantBits());
        writeLongToArrayBigEndian(binaryData, 8, uuid.getLeastSignificantBits());
        return new BsonBinary(BsonBinarySubType.UUID_STANDARD, binaryData);
    }
    
    @Nonnull
    @Override
    public UUID decodeJson(@Nonnull final RawJsonReader reader, final ExtraInfo extraInfo) throws IOException {
        if (reader.peekFor('\"')) {
            return uuidFromHex(reader.readString());
        }
        reader.expect('{');
        reader.consumeWhiteSpace();
        UUID uuid = null;
        while (true) {
            final String key = reader.readString();
            reader.consumeWhiteSpace();
            reader.expect(':');
            reader.consumeWhiteSpace();
            final String s = key;
            switch (s) {
                case "$binary": {
                    uuid = uuidFromHex(reader.readString());
                    break;
                }
                case "$type": {
                    reader.expect('\"');
                    reader.expect('0');
                    reader.expect('4');
                    reader.expect('\"');
                    break;
                }
                default: {
                    throw new IOException("Unknown field '" + key + "' when decoding UUID!");
                }
            }
            reader.consumeWhiteSpace();
            if (reader.tryConsumeOrExpect('}', ',')) {
                if (uuid == null) {
                    throw new IOException("Expected to find '$binary' field when decoding UUID!");
                }
                return uuid;
            }
            else {
                reader.consumeWhiteSpace();
            }
        }
    }
    
    public static void writeLongToArrayBigEndian(@Nonnull final byte[] bytes, final int offset, final long x) {
        bytes[offset + 7] = (byte)(0xFFL & x);
        bytes[offset + 6] = (byte)(0xFFL & x >> 8);
        bytes[offset + 5] = (byte)(0xFFL & x >> 16);
        bytes[offset + 4] = (byte)(0xFFL & x >> 24);
        bytes[offset + 3] = (byte)(0xFFL & x >> 32);
        bytes[offset + 2] = (byte)(0xFFL & x >> 40);
        bytes[offset + 1] = (byte)(0xFFL & x >> 48);
        bytes[offset] = (byte)(0xFFL & x >> 56);
    }
    
    public static long readLongFromArrayBigEndian(@Nonnull final byte[] bytes, final int offset) {
        long x = 0L;
        x |= (0xFFL & (long)bytes[offset + 7]);
        x |= (0xFFL & (long)bytes[offset + 6]) << 8;
        x |= (0xFFL & (long)bytes[offset + 5]) << 16;
        x |= (0xFFL & (long)bytes[offset + 4]) << 24;
        x |= (0xFFL & (long)bytes[offset + 3]) << 32;
        x |= (0xFFL & (long)bytes[offset + 2]) << 40;
        x |= (0xFFL & (long)bytes[offset + 1]) << 48;
        x |= (0xFFL & (long)bytes[offset]) << 56;
        return x;
    }
    
    @Nonnull
    public static UUID uuidFromBytes(@Nonnull final byte[] bytes) {
        if (bytes.length != 16) {
            throw new CodecException(String.format("Expected length to be 16, not %d.", bytes.length));
        }
        return new UUID(readLongFromArrayBigEndian(bytes, 0), readLongFromArrayBigEndian(bytes, 8));
    }
    
    @Nonnull
    public static UUID uuidFromHex(final String src) {
        return uuidFromBytes(Base64.getDecoder().decode(src));
    }
    
    @Nonnull
    @Override
    public Schema toSchema(@Nonnull final SchemaContext context) {
        final StringSchema hexUUID = new StringSchema();
        hexUUID.setMinLength(24);
        hexUUID.setMaxLength(24);
        hexUUID.setPattern(Codec.BASE64_PATTERN);
        hexUUID.setTitle("UUID Binary");
        return hexUUID;
    }
}
