/*
 * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
 * All Rights Reserved.
 *
 * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
 * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
 *
 * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
 * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
 * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
 * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
 * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
 * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
 * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
 * FOREGOING.
 */

package org.broad.tribble.index.interval;

import org.apache.log4j.Logger;
import org.broad.tribble.Feature;
import org.broad.tribble.FeatureCodec;
import org.broad.tribble.exception.UnsortedFileException;
import org.broad.tribble.index.Block;
import org.broad.tribble.index.Index;
import org.broad.tribble.index.IndexCreator;
import org.broad.tribble.util.AsciiLineReader;
import org.broad.tribble.util.LittleEndianOutputStream;

import java.io.*;

/**
 * Created by IntelliJ IDEA.
 * User: jrobinso
 * Date: Jul 12, 2010
 * Time: 12:59:47 PM
 * To change this template use File | Settings | File Templates.
 */
public class IntervalIndexCreator extends IndexCreator {

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

    static int DEFAULT_FEATURE_COUNT = 1000;

    private int featuresPerInterval;

    private boolean verbose = false;

    /**
     * Constructor.
     *
     * @param featureFile
     * @param codec
     */
    public IntervalIndexCreator(File featureFile, FeatureCodec codec) {
        super(featureFile, codec);
        this.featuresPerInterval = DEFAULT_FEATURE_COUNT;

    }

    public void setFeaturesPerInterval(int featuresPerInterval) {
        this.featuresPerInterval = featuresPerInterval;
    }

    public Index createIndex() throws IOException {

        //int nHeaderLines = getHeaderLineCount();
        IntervalTreeIndex featureIndex = new IntervalTreeIndex(featureFile.getAbsolutePath());

        FileInputStream is = new FileInputStream(featureFile);
        AsciiLineReader reader = new AsciiLineReader(is);

        // make sure to read the header off first
        codec.readHeader(reader);

        long fileLength = featureFile.length();
        long progressIncrement = fileLength / 100;

        long filePosition;
        long currentFilePosition = 0;
        String lastChr = null;
        int lastStart = 0;
        int intervalStart = 0;
        int intervalEnd = 0;
        int progressCounter = 1; // progress in %
        int lineNumber = 0;
        String nextLine = "";
        int featureCount = 0;

        // Skip to first feature
        Feature f = null;
        do {
            lineNumber++;
            filePosition = reader.getPosition();
            nextLine = reader.readLine();
        } while (nextLine != null && (f = codec.decodeLoc(nextLine)) == null);

        // "f" should never be null here.
        if (f != null) {
            lastChr = f.getChr();
            intervalStart = f.getStart();
            intervalEnd = f.getEnd();
            featureCount++;
        }

        while ((nextLine = reader.readLine()) != null) {
            lineNumber++;

            f = codec.decodeLoc(nextLine);
            if (f == null) {
                continue;
            }

            String chr = f.getChr();
            featureCount++;

            if (!chr.equals(lastChr) || featureCount >= featuresPerInterval) {
                // Record interval
                int bytesCount = (int) (currentFilePosition - filePosition);
                Block block = new Block(filePosition, bytesCount);
                Interval interval = new Interval(intervalStart, intervalEnd, block);
                featureIndex.insert(lastChr, interval);

                // Start new interval
                featureCount = 1;
                intervalStart = f.getStart();
                intervalEnd = f.getEnd();
                filePosition = currentFilePosition;
                lastStart = f.getStart();
                lastChr = chr;

            } else {
                // Get alignment start and verify file is sorted.
                int start = f.getStart();

                if (start < lastStart) {
                    throw new UnsortedFileException(" File must be sorted by start position. " +
                            "Sort test failed at: " + nextLine);
                }
                lastStart = start;
                intervalEnd = Math.max(f.getEnd(),intervalEnd);
            }


            currentFilePosition = reader.getPosition();

            if (currentFilePosition > (progressCounter * progressIncrement)) {
                updateProgress(progressCounter);
                progressCounter++;

            }
        }

        // Record remainder bin
        int bytesCount = (int) (currentFilePosition - filePosition);
        Block block = new Block(filePosition, bytesCount);
        Interval interval = new Interval(intervalStart, intervalEnd, block);
        featureIndex.insert(lastChr, interval);


        is.close();

        if (idxFile != null) {
            LittleEndianOutputStream stream = null;
            try {
                stream = new LittleEndianOutputStream(new BufferedOutputStream(new FileOutputStream(idxFile)));
                featureIndex.write(stream);
            }
            finally {
                if (stream != null) {
                    stream.close();
                }
            }
        }

        return featureIndex;
    }


    private void updateProgress(int progressCounter) {
        if(verbose) System.out.println("Progress: " + progressCounter + "%");
    }

    public void setVerbose(boolean verbose) {
        this.verbose = verbose;
    }
}