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

#include "MultiLogWriter.h"

#include "logger/AllLogWriter.h"
#include "logger/DirectoryEntry.h"
#include "logger/LinearApproxLogWriter.h"

/// Constructor
MultiLogWriter::MultiLogWriter( LogWriter& outputTo, const Str& name )
    : LogWriter(),
      outputTo_( outputTo ),
      varName_( name ),
      makeDirEntries_( true ),
      nameWritten_( false ),
      dataAccess_( NULL ),
      dataAccessHidden_( false ),
      currentLogWriter_( NULL )
{}

// Destructor
MultiLogWriter::~MultiLogWriter()
{
    if( NULL != dataAccess_ )
    {
        delete dataAccess_;
    }
    if( NULL != currentLogWriter_ )
    {
        delete currentLogWriter_;
    }
}

/// Write the log header, if any
unsigned int MultiLogWriter::writeHeader()
{
    unsigned int bytesSent( 0 );
    if( NULL != currentLogWriter_ )
    {
        bytesSent += currentLogWriter_->writeHeader();
    }
    wroteHeader_ = true;
    return bytesSent;
}

/// Write the given LogEntry - should lock the writeMutex_
unsigned int MultiLogWriter::write( const LogEntry* entry, const Unit* unit )
{
    unsigned int bytesSent( 0 );
    MutexLocker lock( writeMutex_ );
    if( NULL == currentLogWriter_ )
    {
        return bytesSent;
    }
    if( makeDirEntries_ && NULL != entry && entry->getType() == LogEntry::DATA_LOG_ENTRY )
    {
        DataEntry* dataEntry = const_cast<DataEntry*>( static_cast<const DataEntry*>( entry ) );
        if( ( NULL == dataAccess_ ) || dataAccessHidden_ )
        {
            unsigned short ownerCode( CodedStr::NO_CODE );
            size_t nameLen = varName_.find( "." );
            if( nameLen > 0 && nameLen < varName_.length() )
            {
                Str name( varName_, nameLen );
                CodedStr* codedName = Slate::FindCodedName( name );
                if( NULL != codedName )
                {
                    ownerCode = codedName->getCode();
                    if( NULL != codedName )
                    {
                        if( !nameWritten_ )
                        {
                            nameWritten_ = true;
                            DirectoryEntry* nameEntry = new DirectoryEntry( codedName );
                            bytesSent += outputTo_.write( nameEntry );
                            delete nameEntry;
                        }
                    }
                }
            }
            unsigned short uriCode = Slate::GetElementURICode( varName_.cStr() );
            ElementURI* element = Slate::GetElementURI( uriCode );
            DirectoryEntry* dirEntry = new DirectoryEntry( element );
            bytesSent += outputTo_.write( dirEntry );
            delete dirEntry;
            if( NULL == dataAccess_ )
            {
                bool noInterp( currentLogWriter_->getDecimationType() != DecimationLogWriter::LINEAR_APPROXIMATION );
                dataAccess_ = new DataAccess(
                    ownerCode,
                    uriCode,
                    dataEntry->getDataAccess()->getUniversalCode() == uriCode ? uriCode : ElementURI::NO_CODE,
                    dataEntry->getDataAccess()->getUnit()->getName(),
                    dataEntry->getDataAccess()->getBinaryType(),
                    dataEntry->getDataAccess()->getTypeSize(),
                    dataEntry->getDataAccess()->getAccessorType(),
                    dataEntry->getDataAccess()->getCode(),
                    noInterp,
                    dataEntry->getDataAccess()->getTimePrecision()
                );
            }
            dataAccessHidden_ = false;
            dirEntry = new DirectoryEntry( dataAccess_ );
            bytesSent += outputTo_.write( dirEntry );
            delete dirEntry;
        }
        if( NULL != dataAccess_ )
        {
            dataEntry->setDataAccess( dataAccess_ );
        }
    }
    bytesSent +=  currentLogWriter_->write( entry, unit );
    return bytesSent;
}

/// Write the log footer, if any
unsigned int MultiLogWriter::writeFooter()
{
    unsigned int bytesSent( 0 );
    if( NULL != currentLogWriter_ )
    {
        bytesSent += currentLogWriter_->writeFooter();
    }
    return bytesSent;
}

void MultiLogWriter::resetDirectoryEntries()
{
    nameWritten_ = false;
    dataAccessHidden_ = true;
}

void MultiLogWriter::setCurrent( DecimationLogWriter* newLogWriter )
{
    MutexLocker lock( writeMutex_ );
    if( NULL != currentLogWriter_ && newLogWriter != currentLogWriter_ )
    {
        delete currentLogWriter_;
    }
    currentLogWriter_ = newLogWriter;
}

unsigned int MultiLogWriter::forceWrite( DataEntry* dataEntry )
{
    unsigned int bytesSent( 0 );
    MutexLocker lock( writeMutex_ );
    if( ( NULL == dataAccess_ ) || dataAccessHidden_ )
    {
        dataAccess_ = dataEntry->getDataAccess();
        unsigned short ownerCode( dataAccess_->getOwnerCode() );
        CodedStr* codedName = Slate::GetName( ownerCode );
        if( NULL != codedName && !nameWritten_ )
        {
            nameWritten_ = true;
            DirectoryEntry* nameEntry = new DirectoryEntry( codedName );
            bytesSent += outputTo_.write( nameEntry );
            delete nameEntry;
        }
        unsigned short uriCode = dataAccess_->getElementCode();
        ElementURI* element = Slate::GetElementURI( uriCode );
        DirectoryEntry* dirEntry = new DirectoryEntry( element );
        bytesSent += outputTo_.write( dirEntry );
        delete dirEntry;
        dataAccessHidden_ = false;
        dirEntry = new DirectoryEntry( dataAccess_ );
        bytesSent += outputTo_.write( dirEntry );
        delete dirEntry;
    }
    bytesSent += outputTo_.write( dataEntry );
    return bytesSent;
}

unsigned int MultiLogWriter::writeSyslog( const SyslogEntry* syslogEntry )
{
    unsigned int bytesSent( 0 );
    MutexLocker lock( writeMutex_ );
    if( !nameWritten_ )
    {
        const CodedStr& codedName = syslogEntry->getLoggerFacility();
        DirectoryEntry* nameEntry = new DirectoryEntry( &codedName );
        bytesSent += outputTo_.write( nameEntry );
        delete nameEntry;
        nameWritten_ = true;
    }
    bytesSent += outputTo_.write( syslogEntry );
    return bytesSent;
}

unsigned int MultiLogWriter::flush()
{
    unsigned int bytesSent( 0 );
    MutexLocker lock( writeMutex_ );
    if( NULL != currentLogWriter_ && currentLogWriter_->isActive() )
    {
        bytesSent += currentLogWriter_->flush();
    }
    return bytesSent;
}

