/** \file
 *
 *  Contains the FileLogReader class implementation.
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 *
 */

#include <cerrno>
#include <sys/types.h>
#include <unistd.h>

#include "FileLogReader.h"

FlexArray<const char*>* FileLogReader::PriorityList_( NULL );

/** Constructor
 *
 * \param filebase Base of name of file to read to
 * \param logReader logReader that provides input for file logging
 */
FileLogReader::FileLogReader( const Str& filebase, LogReader* logReader )
    : LogReader(),
      filebase_( filebase ),
      file_( NULL ),
      fileStream_( ),
      entriesRead_( 0 ),
      logReader_( logReader ),
      nextFilenum_( 0 )
{

    file_ = fopen( filebase_.cStr(), "rb" );
    Str filename;
    if( NULL == file_ )
    {
        filename = matchAndOpen( nextFilename() );
    }
    else
    {
        filename = filebase_;
    }
    if( NULL != file_ )
    {
        fileStream_.setFile( file_, filename.cStr() );
    }
    logReader_->setInStream( fileStream_ );
}

/// Destructor.
///
/// Closes  file if open
FileLogReader::~FileLogReader()
{
    delete logReader_;
    if( NULL != file_ )
    {
        fclose( file_ );
        file_ = NULL;
    }
}

/// Read
unsigned int FileLogReader::read()
{
    unsigned int entriesRead = 0;
    while( entriesRead == 0 && NULL != file_ && fileStream_.eof() != true )
    {
        entriesRead = readEntries();
    }

    if( entriesRead == 0 )
    {
        if( NULL != file_ )
        {
            fclose( file_ );
            file_ = NULL;
        }

        if( nextFilenum_ > 0 )
        {
            Str filename = matchAndOpen( nextFilename() );
            if( NULL != file_ )
            {
                fileStream_.setFile( file_, filename.cStr() );
                while( entriesRead == 0 && fileStream_.eof() != true )
                {
                    entriesRead = readEntries();
                }
            }
        }
    }

    entriesRead_ += entriesRead;
    return entriesRead;
}

Str FileLogReader::matchAndOpen( const Str& filename )
{
    Str retFilename = filename;
    file_ = fopen( filename.cStr(), "rb" );
    long int size = 0;
    // skip over non-existent or empty files
    if( NULL != file_ )
    {
        fseek( file_, 0, SEEK_END ); // seek to end of file
        size = ftell( file_ ); // get current file pointer
        fseek( file_, 0, SEEK_SET );
    }
    if( size == 0 )
    {
        size_t lastSlash = filename.findLastOf( '/' );
        if( lastSlash == Str::NO_POS )
        {
            lastSlash = filename.findLastOf( '\\' );
        }
        file_ = fopen( ( filename + ".bak" ).cStr(), "rb" );
        if( NULL == file_ && filename.find( ".dir" ) == Str::NO_POS
                && lastSlash != Str::NO_POS && NULL != PriorityList_ )
        {
            for( size_t i = 0; i < PriorityList_->size(); ++i )
            {
                Str altFilename = filename.substr( 0, lastSlash + 1 )
                                  + PriorityList_->get( i ) + filename.substr( filename.length() - 4, 4 );
                file_ = fopen( altFilename.cStr(), "rb" );
                if( NULL == file_ )
                {
                    file_ = fopen( ( altFilename += ".bak" ).cStr(), "rb" );
                }
                if( NULL != file_ )
                {
                    retFilename = altFilename;
                    break;
                }
            }
        }
    }
    return retFilename;
}

/// Uses nextFilenum_ to determine the next filename
Str FileLogReader::nextFilename()
{
    char buf[256];
    unsigned int bufsize;
    bufsize = snprintf( buf, 255, "%s%04d", filebase_.cStr(), nextFilenum_ );
    ++nextFilenum_;
    return Str( buf, bufsize );
}

/// Reads log entries from file
unsigned int FileLogReader::readEntries()
{
    long filePos( 0 );
    unsigned int entriesRead( 0 );
    filePos = ftell( file_ );
    entriesRead = logReader_->read();

    if( logReader_->hasError() && entriesRead == 0 && fileStream_.eof() != true )
    {
        // Step forward byte-by-byte until we find valid data
        ++filePos;
        fseek( file_, filePos, SEEK_SET );
    }

    return entriesRead;
}
