/**
 * Copyright (c) André Bargull
 * Alle Rechte vorbehalten / All Rights Reserved.  Use is subject to license terms.
 *
 * <https://github.com/anba/es6draft>
 */
package com.github.anba.es6draft.regexp;

/**
 * UTF-32 encoding with modified case-folding
 */
final class UTF32Encoding extends UEncoding {
    public static final UTF32Encoding INSTANCE = new UTF32Encoding();

    protected UTF32Encoding() {
        super("UTF-32", 4, 4);
    }

    @Override
    public byte[] toBytes(CharSequence cs) {
        return toBytes(cs.toString());
    }

    @Override
    public byte[] toBytes(String s) {
        byte[] bytes = new byte[Character.codePointCount(s, 0, s.length()) * 4 + 4]; // null-terminated c-string
        for (int cp, i = 0, j = 0; i < s.length(); i += Character.charCount(cp)) {
            cp = s.codePointAt(i);
            bytes[j++] = (byte) (cp >>> 24);
            bytes[j++] = (byte) (cp >>> 16);
            bytes[j++] = (byte) (cp >>> 8);
            bytes[j++] = (byte) (cp >>> 0);
        }
        return bytes;
    }

    @Override
    public int strLength(CharSequence cs, int start, int count) {
        int index = start;
        for (int j = 0; j < count; j += 4) {
            index += Character.charCount(Character.codePointAt(cs, index));
        }
        return index - start;
    }

    @Override
    public int length(CharSequence cs) {
        return Character.codePointCount(cs, 0, cs.length()) * 4;
    }

    @Override
    public int length(CharSequence cs, int start, int end) {
        assert 0 <= start && start <= end && end <= cs.length();
        return Character.codePointCount(cs, start, end) * 4;
    }

    @Override
    public int length(CharSequence cs, int byteIndex) {
        return 4;
    }

    @Override
    public int length(byte c) {
        return 4;
    }

    @Override
    public int length(byte[] bytes, int p, int end) {
        return p + 3 < end ? 4 : missing(p + 2 < end ? 1 : p + 1 < end ? 2 : 3);
    }

    @Override
    public int mbcToCode(byte[] bytes, int p, int end) {
        return ((bytes[p + 0] & 0xff) << 24) | ((bytes[p + 1] & 0xff) << 16) | ((bytes[p + 2] & 0xff) << 8)
                | (bytes[p + 3] & 0xff);
    }

    @Override
    public int codeToMbcLength(int code) {
        return 4;
    }

    @Override
    public int codeToMbc(int code, byte[] bytes, int p) {
        bytes[p + 0] = (byte) (code >>> 24);
        bytes[p + 1] = (byte) (code >>> 16);
        bytes[p + 2] = (byte) (code >>> 8);
        bytes[p + 3] = (byte) (code >>> 0);
        return 4;
    }

    @Override
    public int leftAdjustCharHead(byte[] bytes, int p, int s, int end) {
        return s > p ? s - ((s - p) & 3) : s;
    }

    @Override
    public int strLength(byte[] bytes, int p, int end) {
        return p <= end ? (end - p) >>> 2 : 0;
    }

    @Override
    public int strCodeAt(byte[] bytes, int p, int end, int index) {
        int q = p + (index << 2);
        return q + 3 < end ? mbcToCode(bytes, q, end) : -1;
    }
}
