/*
* 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);
        }
    }
}
