/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2007-2015 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 org.broad.igv.track;

import htsjdk.tribble.Tribble;
import htsjdk.tribble.index.Index;
import htsjdk.tribble.index.IndexFactory;
import htsjdk.tribble.util.ParsingUtils;
import org.apache.log4j.Logger;
import org.broad.igv.Globals;
import org.broad.igv.feature.LocusScore;
import org.broad.igv.feature.Mutation;
import org.broad.igv.feature.Range;
import org.broad.igv.feature.genome.Genome;
import org.broad.igv.feature.tribble.MUTCodec;
import org.broad.igv.feature.tribble.TribbleIndexNotFoundException;
import org.broad.igv.util.ResourceLocator;
import htsjdk.tribble.Feature;

import java.io.IOException;
import java.util.*;

/**
 * @author jrobinso
 *         Date: 4/9/13
 *         Time: 8:45 AM
 */
public class MutationFeatureSource implements FeatureSource<Mutation> {

    private static Logger log = Logger.getLogger(MutationFeatureSource.class);

    String sample;
    MutationDataManager dataManager;


    public MutationFeatureSource(String sample, MutationDataManager dataManager) {
        this.sample = sample;
        this.dataManager = dataManager;
    }

    @Override
    public Iterator<Mutation> getFeatures(String chr, int start, int end) throws IOException {
        return dataManager.getFeatures(sample, chr, start, end);
    }

    @Override
    public List<LocusScore> getCoverageScores(String chr, int start, int end, int zoom) {
        return null;  //Not supported for mutation tracks
    }

    @Override
    public int getFeatureWindowSize() {
        return -1;  // Load all features for a given chromosome
    }

    @Override
    public void setFeatureWindowSize(int size) {
        // Ignored
    }


    static public class MutationDataManager {

        Range currentRange;
        Map<String, List<Mutation>> featureMap = Collections.synchronizedMap(new HashMap());
        TribbleFeatureSource tribbleFeatureSource;
        ResourceLocator locator;

        public MutationDataManager(ResourceLocator locator, Genome genome) throws IOException, TribbleIndexNotFoundException {
            this.tribbleFeatureSource = TribbleFeatureSource.getFeatureSource(locator, genome);
            this.locator = locator;
        }

        public boolean isIndexed() {
            return this.tribbleFeatureSource.isIndexed();
        }

        synchronized Iterator<Mutation> getFeatures(String trackKey, String chr, int start, int end) throws IOException {
            if (currentRange == null || !currentRange.contains(chr, start, end)) {
                Iterator<Feature> features = tribbleFeatureSource.getFeatures(chr, start, end);

                while (features.hasNext()) {
                    Mutation feat = (Mutation) features.next();
                    String thisKey = feat.getSampleId();
                    List<Mutation> keyFeatures = featureMap.get(thisKey);
                    if (keyFeatures == null) {
                        keyFeatures = new ArrayList<Mutation>();
                        featureMap.put(thisKey, keyFeatures);
                    }
                    keyFeatures.add(feat);
                    currentRange = new Range(chr, start, end);
                }

            }
            List<Mutation> featureList = featureMap.get(trackKey);
            return featureList == null ? Collections.EMPTY_LIST.iterator() : featureList.iterator();

        }


        public String[] getSamples() {

            if (this.tribbleFeatureSource.isIndexed()) {
                // Load the index for meta information.  Its already loaded, but not public in the htsjdk class.
                Index idx = loadIndex(this.locator.getPath());
                if (idx != null) {
                    Map<String, String> map = idx.getProperties();
                    if (map != null && map.containsKey("samples")) {
                        return Globals.commaPattern.split(map.get("samples"));
                    }
                }
            }

            // Try to fetch features from codec.  This is to support a deprecated option to
            // specify sample names in the .mut or .maf file header.

            MUTCodec codec = new MUTCodec(locator.getPath(), null);
            return codec.getSamples();

        }

        private Index loadIndex(String path)  {
            try {
                String indexFile = Tribble.indexFile(path);
                Index index = null;
                if (ParsingUtils.resourceExists(indexFile)) {
                    index = IndexFactory.loadIndex(indexFile);
                } else {
                    // See if the index itself is gzipped
                    indexFile = ParsingUtils.appendToPath(indexFile, ".gz");
                    if (ParsingUtils.resourceExists(indexFile)) {
                        index = IndexFactory.loadIndex(indexFile);
                    }
                }
                return index;
            } catch (IOException e) {
                log.error("Error loading index file", e);
                return null;
            }
        }
    }
}
