package com.milaboratory.core.sequence;

import com.milaboratory.core.sequence.quality.FunctionWithIndex;
import com.milaboratory.primitivio.PrimitivI;
import com.milaboratory.primitivio.PrimitivO;
import com.milaboratory.primitivio.annotations.Serializable;

import java.util.Arrays;
import java.util.Objects;
import java.util.function.Function;

/**
 * Represents an ordered list of nucleotide sequences with quality having an id.
 * Like sequencing read but without description.
 */
@Serializable(by = NSQTuple.Serializer.class)
public final class NSQTuple {
    private final long id;
    private final NSequenceWithQuality[] elements;

    private NSQTuple(long id, NSequenceWithQuality[] elements, boolean unsafe) {
        this.id = id;
        this.elements = elements;
        for (NSequenceWithQuality element : elements) Objects.requireNonNull(element);
    }

    public NSQTuple(long id, NSequenceWithQuality... elements) {
        this(id, Objects.requireNonNull(elements).clone(), true);
    }

    public long getId() {
        return id;
    }

    public NSQTuple withId(long newId) {
        return new NSQTuple(newId, elements);
    }

    public int size() {
        return elements.length;
    }

    public NSequenceWithQuality get(int i) {
        return elements[i];
    }

    public NSQTuple map(Function<NSequenceWithQuality, NSequenceWithQuality> transformation) {
        NSequenceWithQuality[] newElements = elements.clone();
        for (int i = 0; i < newElements.length; i++)
            newElements[i] = transformation.apply(newElements[i]);
        return new NSQTuple(id, newElements, true);
    }

    public NSQTuple mapWithIndex(FunctionWithIndex<NSequenceWithQuality, NSequenceWithQuality> transformation) {
        NSequenceWithQuality[] newElements = elements.clone();
        for (int i = 0; i < newElements.length; i++)
            newElements[i] = transformation.apply(i, newElements[i]);
        return new NSQTuple(id, newElements, true);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        NSQTuple nsqTuple = (NSQTuple) o;
        return id == nsqTuple.id && Arrays.equals(elements, nsqTuple.elements);
    }

    @Override
    public int hashCode() {
        int result = Objects.hash(id);
        result = 31 * result + Arrays.hashCode(elements);
        return result;
    }

    public static class Serializer implements com.milaboratory.primitivio.Serializer<NSQTuple> {
        @Override
        public void write(PrimitivO output, NSQTuple obj) {
            output.writeLong(obj.getId());
            output.writeObject(obj.elements);
        }

        @Override
        public NSQTuple read(PrimitivI input) {
            long id = input.readLong();
            NSequenceWithQuality[] elements = Objects.requireNonNull(input.readObject(NSequenceWithQuality[].class));
            return new NSQTuple(id, elements, true);
        }

        @Override
        public boolean isReference() {
            return true;
        }

        @Override
        public boolean handlesReference() {
            return false;
        }
    }

}
