/*
 * The Broad Institute
 * SOFTWARE COPYRIGHT NOTICE AGREEMENT
 * This is copyright (2007-2008) by the Broad Institute/Massachusetts Institute
 * of Technology.  It is licensed to You under the Gnu Public License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *    http://www.opensource.org/licenses/gpl-2.0.php
 *
 * This software is supplied without any warranty or guaranteed support
 * whatsoever. Neither the Broad Institute nor MIT can be responsible for its
 * use, misuse, or functionality.
 */
package org.broad.igv.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;

/**
 *
 * @author jrobinso
 */
public class AsciiLineReader {

    InputStream is;
    byte[] buffer;
    int nextChar;
    int nChars;
    char[] lineBuffer;

    public AsciiLineReader(InputStream is) {
        this(is, 512000);
    }

    public AsciiLineReader(InputStream is, int bufferSize) {
        this.is = is;
        buffer = new byte[bufferSize];
        nextChar = nChars = 0;

        // Allocate this only once, even though it is essentially a local variable of
        // readLine.  This makes a huge difference in performance
        lineBuffer = new char[10000];
    }

    public String readLine() throws IOException {
        return readLine(false);
    }

    /**
     * Read a line of text.  A line is considered to be terminated by any one
     * of a line feed ('\n'), a carriage return ('\r'), or a carriage return
     * followed immediately by a linefeed.
     *
     * @param      includeTerminators  If true, the line-termination characters
     *             are included in the returned string. 
     *
     * @return     A String containing the contents of the line or null if the 
     *             end of the stream has been reached
     */
    public String readLine(boolean includeTerminators) throws IOException {
        int linePosition = 0;

        while (true)
        {
            if (nChars == -1)
            {
                return null;
            }

            // Refill buffer if neccessary
            if (nextChar == nChars)
            {
                fill();
                if (nextChar == nChars || nChars == -1)
                {
                    // eof reached.  Return the last line, or null if this is a new line
                    if (linePosition > 0)
                    {
                        return new String(lineBuffer, 0, linePosition);
                    } else
                    {
                        return null;
                    }
                }
            }


            char c = (char) (buffer[nextChar++] & 0xFF);
            if (c == '\n' || c == '\r')
            {

                if (includeTerminators)
                {
                    lineBuffer[linePosition++] = c;
                    if (c == '\r' && peek() == '\n')
                    {
                        lineBuffer[linePosition++] = c;
                        nextChar++; // <= to account for the '\n' we just ate
                    }
                } else
                {
                    if (c == '\r' && peek() == '\n')
                    {
                        nextChar++; // <= skip the trailing \n in case of \r\n termination
                    }

                }
                return new String(lineBuffer, 0, linePosition);
            } else
            {
                // Expand line buffer size if neccessary.  Reservce at least 2 characters
                // for potential line-terminators in return string

                if (linePosition > (lineBuffer.length - 3))
                {
                    char[] temp = new char[lineBuffer.length + 1000];
                    System.arraycopy(lineBuffer, 0, temp, 0, lineBuffer.length);
                    lineBuffer = temp;
                }

                lineBuffer[linePosition++] = c;
            }
        }
    }

    /**
     * Peek ahead one character, filling from the underlying stream if neccessary.
     * 
     * @return
     * @throws java.io.IOException
     */
    private char peek() throws IOException {
        // Refill buffer if neccessary
        if (nextChar == nChars)
        {
            fill();
            if (nextChar == nChars)
            {
                // eof reached.  
                return 0;
            }
        }
        return (char) buffer[nextChar];

    }

    private void fill() throws IOException {
        nChars = is.read(buffer);
        nextChar = 0;
    }

    public void close() throws IOException {
        is.close();
    }

    public static void main(String[] args) throws Exception {
        File testFile = new File("/Users/jrobinso/IGV/TestData/NCLE_RNA_12345_RMA.gct");
        AsciiLineReader reader = new AsciiLineReader(new FileInputStream(testFile));
        BufferedReader reader2 = new BufferedReader(new FileReader(testFile));
        long t0, lineCount, dt;
        double rate;

        t0 = System.currentTimeMillis();
        lineCount = 0;
        while (reader.readLine() != null)
        {
            lineCount++;
        }
        dt = System.currentTimeMillis() - t0;
        rate = ((double) lineCount) / dt;

        System.out.println("AS: read rate" + rate  + "  DT = " + dt);
        reader.close();

        t0 = System.currentTimeMillis();
        lineCount = 0;
        while (reader2.readLine() != null)
        {
            lineCount++;
        }
        dt = System.currentTimeMillis() - t0;
        rate = ((double) lineCount) / dt;
        System.out.println("BR: read rate" + rate  + "  DT = " + dt);
        reader2.close();

    }
}

