/*
 * Copyright (c) 2012 The Broad Institute
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package htsjdk.variant.bcf2;

import htsjdk.tribble.TribbleException;
import htsjdk.variant.variantcontext.Allele;
import htsjdk.variant.variantcontext.Genotype;
import htsjdk.variant.variantcontext.GenotypeBuilder;
import htsjdk.variant.variantcontext.LazyGenotypesContext;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Lazy version of genotypes decoder for BCF2 genotypes
 *
 * @author Mark DePristo
 * @since 5/12
 */
public class BCF2LazyGenotypesDecoder implements LazyGenotypesContext.LazyParser {
    // the essential information for us to use to decode the genotypes data
    // initialized when this lazy decoder is created, as we know all of this from the BCF2Codec
    // and its stored here again for code cleanliness
    private final BCF2Codec codec;
    private final List<Allele> siteAlleles;
    private final int nSamples;
    private final int nFields;
    private final GenotypeBuilder[] builders;

    BCF2LazyGenotypesDecoder(
            final BCF2Codec codec,
            final List<Allele> alleles,
            final int nSamples,
            final int nFields,
            final GenotypeBuilder[] builders) {
        this.codec = codec;
        this.siteAlleles = alleles;
        this.nSamples = nSamples;
        this.nFields = nFields;
        this.builders = builders;
    }

    @Override
    public LazyGenotypesContext.LazyData parse(final Object data) {
        try {

            // load our byte[] data into the decoder
            final BCF2Decoder decoder = new BCF2Decoder(((BCF2Codec.LazyData) data).bytes);

            for (int i = 0; i < nSamples; i++) builders[i].reset(true);

            for (int i = 0; i < nFields; i++) {
                // get the field name
                final int offset = (Integer) decoder.decodeTypedValue();
                final String field = codec.getDictionaryString(offset);

                // the type of each element
                final byte typeDescriptor = decoder.readTypeDescriptor();
                final int numElements = decoder.decodeNumberOfElements(typeDescriptor);
                final BCF2GenotypeFieldDecoders.Decoder fieldDecoder = codec.getGenotypeFieldDecoder(field);
                try {
                    fieldDecoder.decode(siteAlleles, field, decoder, typeDescriptor, numElements, builders);
                } catch (ClassCastException e) {
                    throw new TribbleException("BUG: expected encoding of field " + field
                            + " inconsistent with the value observed in the decoded value");
                }
            }

            final ArrayList<Genotype> genotypes = new ArrayList<Genotype>(nSamples);
            for (final GenotypeBuilder gb : builders) genotypes.add(gb.make());

            return new LazyGenotypesContext.LazyData(
                    genotypes,
                    codec.getHeader().getSampleNamesInOrder(),
                    codec.getHeader().getSampleNameToOffset());
        } catch (IOException e) {
            throw new TribbleException("Unexpected IOException parsing already read genotypes data block", e);
        }
    }
}
