#ifndef UTIL___LINE_READER__HPP
#define UTIL___LINE_READER__HPP

/*  $Id: line_reader.hpp 489925 2016-01-19 21:26:37Z elisovdn $
 * ===========================================================================
 *
 *                            PUBLIC DOMAIN NOTICE
 *               National Center for Biotechnology Information
 *
 *  This software/database is a "United States Government Work" under the
 *  terms of the United States Copyright Act.  It was written as part of
 *  the author's official duties as a United States Government employee and
 *  thus cannot be copyrighted.  This software/database is freely available
 *  to the public for use. The National Library of Medicine and the U.S.
 *  Government have not placed any restriction on its use or reproduction.
 *
 *  Although all reasonable efforts have been taken to ensure the accuracy
 *  and reliability of the software and data, the NLM and the U.S.
 *  Government do not and cannot warrant the performance or results that
 *  may be obtained by using this software or data. The NLM and the U.S.
 *  Government disclaim all warranties, express or implied, including
 *  warranties of performance, merchantability or fitness for any particular
 *  purpose.
 *
 *  Please cite the author in any work or product based on this material.
 *
 * ===========================================================================
 *
 * Author:  Aaron Ucko, Anatoliy Kuznetsov
 *
 */

/// @file line_reader.hpp
/// Lightweight interface for getting lines of data with minimal
/// memory copying.
///
/// Any implementation must always keep its current line in memory so
/// that callers may harvest data from it in place.

#include <corelib/ncbifile.hpp>

#include <memory>

/** @addtogroup Miscellaneous
 *
 * @{
 */


BEGIN_NCBI_SCOPE

/// Abstract base class for lightweight line-by-line reading.
class NCBI_XUTIL_EXPORT ILineReader : public CObject
{
public:
    /// Return a new ILineReader object corresponding to the given
    /// filename, taking "-" (but not "./-") to mean standard input.
    ///
    /// As always with ILineReader, an explicit call to operator++ or
    /// ReadLine() will be necessary to fetch the first line.
    static CRef<ILineReader> New(const string& filename);

    /// Return a new ILineReader object corresponding to the given
    /// input stream, optionally taking ownership thereof.
    ///
    /// As always with ILineReader, an explicit call to operator++ or
    /// ReadLine() will be necessary to fetch the first line.
    static CRef<ILineReader> New(CNcbiIstream& is,
                                 EOwnership ownership = eNoOwnership);

    /// Indicates (negatively) whether there is any more input.
    virtual bool AtEOF(void) const = 0;

    /// Returns the first character of the next string without consuming it.
    /// If the next string is empty, returns zero.
    ///  @note
    /// Before the first call of operator++() this function returns the
    /// first char of the first string.
    ///  @attention
    /// It is not guaranteed that any implementation of this function 
    /// will stop reading at EOF, so you should check for EOF yourself
    virtual char PeekChar(void) const = 0;

    /// Make a line available. MUST be called even for the first line;
    /// MAY trigger EOF conditions even when also retrieving data.
    /// When beyond the last string, becomes a no-op.
    virtual ILineReader& operator++(void) = 0;
    void ReadLine(void) { ++*this; }

    /// Unget current line, which must be valid.
    /// After calling this method:
    ///   1. AtEOF() returns false,
    ///   2. PeekChar() returns first char of the current line,
    ///   3. Calling operator*() or UngetLine() is illegal,
    ///   4. Calling operator++() will make the line current again
    virtual void UngetLine(void) = 0;

    ///  Return the current line, minus its terminator. Before the first run of
    ///  operator++() returns an empty CTempString.
    ///  At EOF returns an empty CTempString.
    /// @attention
    ///  Right after UngetLine() calling this
    ///  method is illegal. Call operator++() first
    virtual CTempString operator*(void) const = 0;
    CTempString GetCurrentLine(void) const { return **this; }

    /// Return the current (absolute) position.
    virtual CT_POS_TYPE GetPosition(void) const = 0;

    ///  Returns the current line number (counting from 1, not 0).
    /// @attention
    ///  Right after constructor or after UngetLine() calling this
    ///  method is illegal. Call operator++() first
    virtual unsigned int GetLineNumber(void) const = 0;
};


/// Simple implementation of ILineReader for i(o)streams.
class NCBI_XUTIL_EXPORT CStreamLineReader : public ILineReader
{
public:
    enum EEOLStyle {
        eEOL_unknown = 0, ///< to be detected
        eEOL_cr      = 1, ///< bare CR (classic Mac)
        eEOL_lf      = 2, ///< bare LF (Unix et al.)
        eEOL_crlf    = 3, ///< DOS/Windows
#ifdef NCBI_OS_UNIX
        eEOL_native  = eEOL_lf,
#elif defined(NCBI_OS_MSWIN)
        eEOL_native  = eEOL_crlf,
#else
        eEOL_native  = eEOL_unknown,
#endif
        eEOL_mixed   = 4 ///< contains both bare CRs and bare LFs
    };

    /// Open a line reader over a given stream, with the given
    /// EOL-style and ownership settings (if specified).
    ///
    /// As always with ILineReader, an explicit call to operator++ or
    /// ReadLine() will be necessary to fetch the first line.
    explicit CStreamLineReader(CNcbiIstream& is,
                               EEOLStyle eol_style = eEOL_unknown,
                               EOwnership ownership = eNoOwnership);

    /// Open a line reader over a given stream, with the given
    /// ownership setting.
    ///
    /// As always with ILineReader, an explicit call to operator++ or
    /// ReadLine() will be necessary to fetch the first line.
    CStreamLineReader(CNcbiIstream& is, EOwnership ownership);

    ~CStreamLineReader();

    bool               AtEOF(void) const;
    char               PeekChar(void) const;
    CStreamLineReader& operator++(void);
    void               UngetLine(void);
    CTempString        operator*(void) const;
    CT_POS_TYPE        GetPosition(void) const;
    unsigned int       GetLineNumber(void) const;

private:
    EEOLStyle x_AdvanceEOLUnknown(void);
    EEOLStyle x_AdvanceEOLSimple(char eol, char alt_eol);
    EEOLStyle x_AdvanceEOLCRLF(void);

    AutoPtr<CNcbiIstream> m_Stream;
    string                m_Line;
    unsigned int          m_LineNumber;
    SIZE_TYPE             m_LastReadSize;
    bool                  m_UngetLine;
    bool                  m_AutoEOL;
    EEOLStyle             m_EOLStyle;
};


/// Simple implementation of ILineReader for regions of memory
/// (such as memory-mapped files).
class NCBI_XUTIL_EXPORT CMemoryLineReader : public ILineReader
{
public:
    /// Open a line reader over the half-open memory range [start, end).
    ///
    /// As always with ILineReader, an explicit call to operator++ or
    /// ReadLine() will be necessary to fetch the first line.
    CMemoryLineReader(const char* start, const char* end)
        : m_Start(start), m_End(end), m_Pos(start), m_LineNumber(0) { }

    /// Open a line reader over the half-open memory range
    /// [start, start+length).
    ///
    /// As always with ILineReader, an explicit call to operator++ or
    /// ReadLine() will be necessary to fetch the first line.
    CMemoryLineReader(const char* start, SIZE_TYPE length)
        : m_Start(start), m_End(start + length), m_Pos(start), 
          m_LineNumber(0) { }

    /// Open a line reader over a given memory-mapped file, with the
    /// given ownership setting (if specified).
    ///
    /// As always with ILineReader, an explicit call to operator++ or
    /// ReadLine() will be necessary to fetch the first line.
    CMemoryLineReader(CMemoryFile* mem_file,
                      EOwnership ownership = eNoOwnership);

    bool               AtEOF(void) const;
    char               PeekChar(void) const;
    CMemoryLineReader& operator++(void);
    void               UngetLine(void);
    CTempString        operator*(void) const;
    CT_POS_TYPE        GetPosition(void) const;
    unsigned int       GetLineNumber(void) const;

private:
    const char*           m_Start;
    const char*           m_End;
    const char*           m_Pos;
    CTempString           m_Line;
    AutoPtr<CMemoryFile>  m_MemFile;
    unsigned int          m_LineNumber;
};

/// Implementation of ILineReader for IReader
///
class NCBI_XUTIL_EXPORT CBufferedLineReader : public ILineReader
{
public:
    /// read from the IReader
    ///
    /// As always with ILineReader, an explicit call to operator++ or
    /// ReadLine() will be necessary to fetch the first line.
    CBufferedLineReader(IReader* reader,
                        EOwnership ownership = eNoOwnership);

    /// read from the istream
    ///
    /// As always with ILineReader, an explicit call to operator++ or
    /// ReadLine() will be necessary to fetch the first line.
    CBufferedLineReader(CNcbiIstream& is,
                        EOwnership ownership = eNoOwnership);

    /// read from the file, "-" (but not "./-") means standard input
    ///
    /// As always with ILineReader, an explicit call to operator++ or
    /// ReadLine() will be necessary to fetch the first line.
    CBufferedLineReader(const string& filename);

    virtual ~CBufferedLineReader();

    bool                AtEOF(void) const;
    char                PeekChar(void) const;
    CBufferedLineReader& operator++(void);
    void                UngetLine(void);
    CTempString         operator*(void) const;
    CT_POS_TYPE         GetPosition(void) const;
    unsigned int        GetLineNumber(void) const;

private:
    CBufferedLineReader(const CBufferedLineReader&);
    CBufferedLineReader& operator=(const CBufferedLineReader&);
private:
    void x_LoadLong();
    bool x_ReadBuffer();
private:
    AutoPtr<IReader> m_Reader;
    bool          m_Eof;
    bool          m_UngetLine;
    SIZE_TYPE     m_LastReadSize;
    size_t        m_BufferSize;
    AutoArray<char> m_Buffer;
    const char*   m_Pos;
    const char*   m_End;
    CTempString   m_Line;
    string        m_String;
    CT_POS_TYPE   m_InputPos;
    unsigned int  m_LineNumber;
};



END_NCBI_SCOPE


/* @} */

#endif  /* UTIL___LINE_READER__HPP */
