// Auto-generated by BoltFFI. Do not edit.
package {{ package_name }};

{{ self::record_import_block(record) }}{% if record.uses_native_record_syntax() -%}
{{ self::javadoc_block(record.doc, "") }}public record {{ record.class_name }}({% for field in record.fields %}{{ field.java_type }} {{ field.name }}{% if !loop.last %}, {% endif %}{% endfor %}) {
{%- if record.has_default_constructors() %}{% for ctor in record.default_constructors %}

    public {{ record.class_name }}({% for param in ctor.params %}{{ param.java_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %}) {
        this({% for argument in ctor.arguments %}{{ argument }}{% if !loop.last %}, {% endif %}{% endfor %});
    }
{% endfor %}{%- endif %}
{% else -%}
{{ self::javadoc_block(record.doc, "") }}public final class {{ record.class_name }}{% if record.is_error %} extends RuntimeException{% endif %} {
{%- for field in record.fields %}
    public final {{ field.java_type }} {{ field.name }};
{%- endfor %}

{{ self::javadoc_block(record.doc, "    ") }}    public {{ record.class_name }}({% for field in record.fields %}{{ field.java_type }} {{ field.name }}{% if !loop.last %}, {% endif %}{% endfor %}) {
{%- if record.is_error %}
{%- match record.message_field() %}
{%- when Some with (message_field) %}
        super({{ message_field.name }});
{%- when None %}
        super();
{%- endmatch %}
{%- endif %}
{%- for field in record.fields %}
        this.{{ field.name }} = {{ field.name }};
{%- endfor %}
    }

{%- if record.has_default_constructors() %}{% for ctor in record.default_constructors %}

    public {{ record.class_name }}({% for param in ctor.params %}{{ param.java_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %}) {
        this({% for argument in ctor.arguments %}{{ argument }}{% if !loop.last %}, {% endif %}{% endfor %});
    }
{% endfor %}{%- endif %}
{% if !record.fields.is_empty() %}{% for field in record.fields -%}

{{ self::javadoc_block(field.doc, "    ") }}    public {{ field.java_type }} {{ field.name }}() {
        return {{ field.name }};
    }{% if !loop.last %}

{% endif %}{%- endfor %}{%- endif %}
{% if record.is_error %}

    public {{ record.class_name }} getError() {
        return this;
    }
{% endif %}
{% endif -%}
{%- if !record.constructors.is_empty() %}{% for ctor in record.constructors -%}

{% include "render_java/value_type_constructor.txt" %}
{% endfor %}
{%- endif %}
{%- if !record.methods.is_empty() %}{% for method in record.methods -%}

{% include "render_java/value_type_method.txt" %}
{% endfor %}
{%- endif %}
    static {{ record.class_name }} decode(WireReader reader) {
        return new {{ record.class_name }}(
{%- for field in record.fields %}
            {{ field.wire_decode_expr }}{% if !loop.last %},{% endif %}
{%- endfor %}
        );
    }

    int wireEncodedSize() {
{%- if record.is_empty() %}
        return 0;
{%- else %}
        return {% for field in record.fields %}{{ field.wire_size_expr }}{% if !loop.last %} + {% endif %}{% endfor %};
{%- endif %}
    }

    void wireEncodeTo(WireWriter wire) {
{%- for field in record.fields %}
        {{ field.wire_encode_expr }};
{%- endfor %}
    }

    byte[] wireEncodeToBytes() {
        try (WireWriter wire = new WireWriter(wireEncodedSize())) {
            wireEncodeTo(wire);
            return wire.toByteArray();
        }
    }

{%- if let Some(layout) = record.blittable_layout %}

    static final int STRUCT_SIZE = {{ layout.struct_size }};
{%- for field in layout.fields %}
    private static final int OFFSET_{{ field.const_name }} = {{ field.offset }};
{%- endfor %}

    private static {{ record.class_name }} decodeBlittableFromBuffer(ByteBuffer buf, int base) {
        return new {{ record.class_name }}(
{%- for field in layout.fields %}
            {{ field.decode_expr }}{% if !loop.last %},{% endif %}
{%- endfor %}
        );
    }

    private static void writeBlittableToBuffer(ByteBuffer buf, int base, {{ record.class_name }} item) {
{%- for field in layout.fields %}
        {{ field.encode_expr }};
{%- endfor %}
    }

    static List<{{ record.class_name }}> decodeBlittableVec(WireReader reader) {
        int count = reader.readI32();
        if (count == 0) return Collections.emptyList();
        ByteBuffer buf = reader.readBlittableBuffer(count, STRUCT_SIZE);
        List<{{ record.class_name }}> result = new ArrayList<>(count);
        for (int index = 0; index < count; index++) {
            result.add(decodeBlittableFromBuffer(buf, index * STRUCT_SIZE));
        }
        return result;
    }

    static List<{{ record.class_name }}> decodeBlittableVecFromRawBuffer(byte[] raw) {
        if (raw == null || raw.length == 0) return Collections.emptyList();
        if (raw.length % STRUCT_SIZE != 0) {
            throw new RuntimeException("corrupt buffer: invalid blittable vec length for {{ record.class_name }}");
        }
        ByteBuffer buf = ByteBuffer.wrap(raw).order(ByteOrder.LITTLE_ENDIAN);
        int count = raw.length / STRUCT_SIZE;
        List<{{ record.class_name }}> result = new ArrayList<>(count);
        for (int index = 0; index < count; index++) {
            result.add(decodeBlittableFromBuffer(buf, index * STRUCT_SIZE));
        }
        return result;
    }

    static void encodeBlittableVec(WireWriter wire, List<{{ record.class_name }}> items) {
        wire.writeI32(items.size());
        int byteCount = items.size() * STRUCT_SIZE;
        wire.writeRawBytes(byteCount, (buf, baseOffset) -> {
            for (int index = 0; index < items.size(); index++) {
                int base = baseOffset + index * STRUCT_SIZE;
                writeBlittableToBuffer(buf, base, items.get(index));
            }
        });
    }

    ByteBuffer encodeBlittableInput() {
        ByteBuffer buf = ByteBuffer.allocateDirect(STRUCT_SIZE).order(ByteOrder.LITTLE_ENDIAN);
        writeBlittableToBuffer(buf, 0, this);
        return buf;
    }

    static ByteBuffer encodeBlittableVecInput(List<{{ record.class_name }}> items) {
        int byteCount = items.size() * STRUCT_SIZE;
        ByteBuffer buf = ByteBuffer.allocateDirect(byteCount).order(ByteOrder.LITTLE_ENDIAN);
        for (int index = 0; index < items.size(); index++) {
            int base = index * STRUCT_SIZE;
            writeBlittableToBuffer(buf, base, items.get(index));
        }
        return buf;
    }
{%- endif %}

{%- if !record.uses_native_record_syntax() %}

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        {{ record.class_name }} other = ({{ record.class_name }}) obj;
{%- if record.is_empty() %}
        return true;
{%- else %}
        return {% for field in record.fields %}{{ field.equals_expr }}{% if !loop.last %} && {% endif %}{% endfor %};
{%- endif %}
    }

    @Override
    public int hashCode() {
{%- if record.is_empty() %}
        return 1;
{%- else %}
        int result = 1;
{%- for field in record.fields %}
        result = 31 * result + {{ field.hash_expr }};
{%- endfor %}
        return result;
{%- endif %}
    }

    @Override
    public String toString() {
{%- if record.is_empty() %}
        return "{{ record.class_name }}{}";
{%- else %}
        return "{{ record.class_name }}{" +
{%- for field in record.fields %}
            "{% if loop.first %}{% else %}, {% endif %}{{ field.name }}=" + {{ field.name }}{% if !loop.last %} +{% else %} +{% endif %}
{%- endfor %}
            '}';
{%- endif %}
    }
{%- endif %}
}
