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

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

#include "FileLogWriter.h"

#include "data/Slate.h"
#include "io/FileEncoder.h"
#include "units/Units.h"


/** Constructor with filename & specified flushFreq
 *
 * \param filename Name of  file to write to
 * \param logWriter logWriter that provides input for file logging
 * \param append if true, append to file, otherwise overwrite.
 * \param fileEncoder optional encoder to run whenever the file closes
 */
FileLogWriter::FileLogWriter( const Str &filename, LogWriter* logWriter, bool append, FileEncoder* fileEncoder )
    : LogWriter(),
      logWriter_( logWriter ),
      entriesWritten_( 0 ),
      bytesInFile_( 0 ),
      initFilename_( Str::EMPTY_STR != filename ),
      flushFreq_( FLUSH_ONCE ),
      writtenInCycle_( false ),
      fileEncoder_( fileEncoder )
{
    logWriter_->addParent();
    logWriter_->setOutStream( fileStream_ );
    if( initFilename_ )
    {
        open( filename.cStr(), append );
    }
}

/** Constructor with FILE
 *
 * \param file FILE to write to
 * \param logWriter logWriter that provides input for file logging
 */
FileLogWriter::FileLogWriter( FILE* file, LogWriter* logWriter )
    : LogWriter(),
      logWriter_( logWriter ),
      fileStream_( file ),
      entriesWritten_( 0 ),
      bytesInFile_( 0 ),
      initFilename_( false ),
      flushFreq_( FLUSH_ONCE ),
      writtenInCycle_( false ),
      fileEncoder_( NULL )
{
    logWriter_->addParent();
    logWriter_->setOutStream( fileStream_ );
    logWriter_->writeHeader();
}

/// Destructor.
///
/// Closes  file if open
FileLogWriter::~FileLogWriter()
{
    close();
    if( NULL != logWriter_ )
    {
        logWriter_->removeParent();
        if( logWriter_->getParentCount() <= 0 )
        {
            delete logWriter_;
        }
    }
    if( fileEncoder_ != NULL )
    {
        delete fileEncoder_;
    }
}

/// Reset cached input settings
void FileLogWriter::resetInputs()
{
    if( NULL != logWriter_ )
    {
        logWriter_->resetInputs();
    }
}

/// Open the given  file.
///
/// \param filename Name of system log.
/// \param append If true, open the file in append mode (otherwise overwrite)
bool FileLogWriter::open( const char *filename, const bool append )
{
    if( isWritable() )
    {
        close();
    }

    setFilename( filename );

    entriesWritten_ = 0;

    // Initialize bytesInFile
    struct stat fileStat;
    if( !append )
    {
        remove( filename );
    }
    if( !append || stat( filename, &fileStat ) != 0 )
    {
        bytesInFile_ = 0;
    }
    else
    {
        bytesInFile_ = ( unsigned int )fileStat.st_size;
    }

    fileStream_.open( filename, append );

    if( bytesInFile_ == 0 )
    {
        logWriter_->writeHeader();
    }

    wroteFooter_ = false;

    // insert error checking here
    return true;
}

/// Close the file stream.
void FileLogWriter::close( void )
{
    if( isWritable() )
    {
        logWriter_->writeFooter();
        fileStream_.close();
        bytesInFile_ = 0;
    }

    if( initFilename_ && NULL != fileEncoder_ )
    {
        if( !fileEncoder_->encode( filename_.cStr() ) )
        {
            printf( "Error encoding %s\n", filename_.cStr() );
        }
    }

    if( !isWritable() )
    {
        setFilename( Str::EMPTY_STR );
    }
}

/// Write
unsigned int FileLogWriter::write( const LogEntry *entry, const Unit* unit )
{
    unsigned int bytesWritten = 0;

    MutexLocker lock( writeMutex_ );

    if( isWritable() )
    {
        bytesWritten = logWriter_->write( entry, unit );
        if( bytesWritten > 0 )
        {
            writtenInCycle_ = true;
            if( flushFreq_ == FLUSH_PER_WRITE )
            {
                fileStream_.flush();
            }
        }
        entriesWritten_++;
        bytesInFile_ += bytesWritten;
    }

    /// Logic for rotating files goes here...
    return bytesWritten;
}

/// If flushFreq_ is FLUSH_PER_CYCLE, this causes an output flush
void FileLogWriter::endCycle()
{
    if( writtenInCycle_ && flushFreq_ == FLUSH_PER_CYCLE )
    {
        fileStream_.flush();
        writtenInCycle_ = false;
    }
}

/*void FileLogWriter::config( Logger& logger )
{
    int flushFreqInt = FLUSH_ONCE;
    if( Str::EMPTY_STR != filename_ )
    {
        size_t lastSlash = filename_.findLastOf( '/' );
        lastSlash = lastSlash == Str::NO_POS ? 0 : lastSlash + 1;
        Str name( filename_, lastSlash );
        name.replaceChar( '.', '_' );
        Slate::ReadOnce( "Config/logger", "flush_freq_" + name, Units::ENUM, flushFreqInt, logger );
    }
    flushFreq_ = ( FlushFreq )flushFreqInt;
}*/

void FileLogWriter::resetFilename( const Str& filename )
{
    close();
    open( filename.cStr(), false );
}
