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

#include "SplitFileLogWriter.h"

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

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

/** Constructor for manual split
 *
 * \param filebase Basename of file to write to
 * \param logWriter logWriter that provides input for file logging
 * \param fileEncoder In non-NULL, object that encodes the file after saving
 */
SplitFileLogWriter::SplitFileLogWriter( const Str &filebase, LogWriter* logWriter, FileEncoder* fileEncoder, int nextFilenum )
    : FileLogWriter( Str::EMPTY_STR, logWriter, false, fileEncoder ),
      filebase_( filebase ),
      capacity_( 0 ),
      nextFilenum_( nextFilenum ),
      span_( Timespan::INVALID_TIMESPAN ),
      splitTime_( Timestamp::NOT_SET_TIME )
{
}

/** Constructor for capacity split
 *
 * \param filebase Basename of file to write to
 * \param capacity maximum size, in bytes, of each split file.
 * \param logWriter logWriter that provides input for file logging
 * \param fileEncoder In non-NULL, object that encodes the file after saving
 */
SplitFileLogWriter::SplitFileLogWriter( const Str &filebase, unsigned int capacity, LogWriter* logWriter, FileEncoder* fileEncoder, int nextFilenum )
    : FileLogWriter( Str::EMPTY_STR, logWriter, false, fileEncoder ),
      filebase_( filebase ),
      capacity_( capacity ),
      nextFilenum_( nextFilenum ),
      span_( Timespan::INVALID_TIMESPAN ),
      splitTime_( Timestamp::NOT_SET_TIME )
{
}

/** Constructor for Timespan split
 *
 * \param filebase Basename of file to write to
 * \param span timespan to contain within each file split
 * \param logWriter logWriter that provides input for file logging
 * \param fileEncoder In non-NULL, object that encodes the file after saving
 */
SplitFileLogWriter::SplitFileLogWriter( const Str &filebase, const Timespan& span, LogWriter* logWriter, FileEncoder* fileEncoder, int nextFilenum )
    : FileLogWriter( Str::EMPTY_STR, logWriter, false, fileEncoder ),
      filebase_( filebase ),
      capacity_( 0 ),
      nextFilenum_( nextFilenum ),
      span_( span ),
      splitTime_( Timestamp::NOT_SET_TIME )
{
}

/// Destructor.
///
/// Closes  file if open
SplitFileLogWriter::~SplitFileLogWriter()
{
    split( false );
}

/// Write
unsigned int SplitFileLogWriter::write( const LogEntry *entry, const Unit* unit )
{
    // No mutex lock because super is called below...
    // MutexLocker lock( writeMutex_ );

    if( capacity_ > 0 )
    {
        /// Close the file if needed
        if( getBytesInFile() > capacity_ )
        {
            split();
        }
    }
    else if( span_ != Timespan::INVALID_TIMESPAN && entry != NULL )
    {
        /// Close the file if needed
        if( splitTime_ != Timestamp::NOT_SET_TIME && entry->getTimestamp() > splitTime_ )
        {
            split();
        }
        /// Open a new one if needed
        if( getBytesInFile() == 0 )
        {
            splitTime_ = entry->getTimestamp() + span_;
        }
    }
    /// Open a new one if needed
    if( getBytesInFile() == 0 && !isWritable() )
    {
        Str next( nextFilename() );
        open( next.cStr(), false );
    }
    unsigned int bytesSent = NULL == entry ? 0 : FileLogWriter::write( entry, unit );
    return bytesSent;
}

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

/// Do the split.
void SplitFileLogWriter::split( bool andReopen, int nextFilenum )
{
    bool wasOpen( isWritable() );
    Str lastFilename( wasOpen ? getFilename() : Str::EMPTY_STR );
    close();
    if( wasOpen && getFileEncoder() != NULL )
    {
        getFileEncoder()->encode( lastFilename.cStr() );
    }
    if( andReopen )
    {
        Str next( nextFilename( nextFilenum ) );
        open( next.cStr(), false );
    }
}

/// Change file destination.
void SplitFileLogWriter::resetFileBase( const Str& filebase, int nextFilenum )
{
    lockWrites();
    logWriter_->writeFooter();
    filebase_ = filebase;
    nextFilenum_ = nextFilenum;
    split();
    unlockWrites();
}

/// Uses nextFilenum_ to determine the next filename
Str SplitFileLogWriter::nextFilename( int nextFilenum )
{
    char buf[256];
    unsigned int bufsize;
    int result;
    if( nextFilenum > 0 )
    {
        nextFilenum_ = nextFilenum;
    }
    do
    {
        bufsize = snprintf( buf, 255, "%s%04d", filebase_.cStr(), nextFilenum_ );
        result = access( buf, F_OK );
        ++nextFilenum_;
    }
    while( result != 0 && errno != ENOENT );
    return Str( buf, bufsize );
}

