/*
 * Copyright Debezium Authors.
 *
 * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
 */
package io.debezium.connector.cassandra.transforms.type.deserializer;

import java.nio.ByteBuffer;
import java.util.List;

import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.TupleType;
import org.apache.kafka.connect.data.SchemaBuilder;
import org.apache.kafka.connect.data.Struct;

import io.debezium.connector.cassandra.transforms.CassandraTypeDeserializer;

public class TupleTypeDeserializer extends TypeDeserializer {

    private static final String TUPLE_NAME_POSTFIX = "Tuple";
    private static final String FIELD_NAME_PREFIX = "field";

    @Override
    public Object deserialize(AbstractType<?> abstractType, ByteBuffer bb) {
        // the fun single case were we don't actually want to do the default deserialization!
        TupleType tupleType = (TupleType) abstractType;
        List<AbstractType<?>> innerTypes = tupleType.allTypes();
        ByteBuffer[] innerValueByteBuffers = tupleType.split(bb);

        Struct struct = new Struct(getSchemaBuilder(abstractType).build());

        for (int i = 0; i < innerTypes.size(); i++) {
            AbstractType<?> currentInnerType = innerTypes.get(i);
            String fieldName = createFieldNameForIndex(i);
            Object deserializedInnerObject = CassandraTypeDeserializer.deserialize(currentInnerType, innerValueByteBuffers[i]);
            struct.put(fieldName, deserializedInnerObject);
        }

        return struct;
    }

    @Override
    public SchemaBuilder getSchemaBuilder(AbstractType<?> abstractType) {
        TupleType tupleType = (TupleType) abstractType;
        List<AbstractType<?>> tupleInnerTypes = tupleType.allTypes();

        String recordName = createTupleName(tupleInnerTypes);

        SchemaBuilder schemaBuilder = SchemaBuilder.struct().name(recordName);

        for (int i = 0; i < tupleInnerTypes.size(); i++) {
            AbstractType<?> innerType = tupleInnerTypes.get(i);
            schemaBuilder.field(createFieldNameForIndex(i), CassandraTypeDeserializer.getSchemaBuilder(innerType).build());
        }

        return schemaBuilder;
    }

    private String createTupleName(List<AbstractType<?>> innerTypes) {
        StringBuilder tupleNameBuilder = new StringBuilder();
        for (AbstractType<?> innerType : innerTypes) {
            tupleNameBuilder.append(abstractTypeToNiceString(innerType));
        }
        return tupleNameBuilder.append(TUPLE_NAME_POSTFIX).toString();
    }

    private String createFieldNameForIndex(int i) {
        // begin indexing at 1
        return FIELD_NAME_PREFIX + (i + 1);
    }

    private String abstractTypeToNiceString(AbstractType<?> tupleInnerType) {
        // the full class name of the type. We want to pair it down to just the final type and remove the "Type".
        String typeName = tupleInnerType.getClass().getSimpleName();
        return typeName.substring(0, typeName.length() - 4);
    }
}