// Author:  Adam Sawicki, www.asawicki.info, adam__REMOVE__@asawicki.info
// Version: 1.1
// License: Public Domain

// Newer version of this code is available on:
// https://github.com/sawickiap/MISC/tree/master/PrintStream

#include "PrintStream.hpp"
#include <Windows.h> // For OutputDebugString
#include <cstdarg>
#include <cassert>
#include <cstdint>

////////////////////////////////////////////////////////////////////////////////
// CPrintStream

void CPrintStream::printf( const char* format, ... )
{
    va_list argList;
    va_start( argList, format );
    this->vprintf( format, argList );
    va_end( argList );
}

////////////////////////////////////////////////////////////////////////////////
// CConsolePrintStream

void CConsolePrintStream::vprintf( const char* format, va_list argList )
{
    ::vprintf( format, argList );
}

////////////////////////////////////////////////////////////////////////////////
// CConsolePrintStream

CFilePrintStream::CFilePrintStream() :
    m_File( nullptr )
{
}

CFilePrintStream::~CFilePrintStream()
{
    Close();
}

void CFilePrintStream::Open( const char* filePath, const char* mode )
{
    Close();

    bool ok = fopen_s( &m_File, filePath, mode ) == 0;
    if( !ok )
    {
        m_File = nullptr;
        // Handle error somehow.
        assert(0);
    }
}

void CFilePrintStream::Close()
{
    if( m_File )
    {
        fclose( m_File );
        m_File = nullptr;
    }
}

void CFilePrintStream::vprintf( const char* format, va_list argList )
{
    assert( Opened() );

    ::vfprintf( m_File, format, argList );
}

////////////////////////////////////////////////////////////////////////////////
// CMemoryPrintStream

void CMemoryPrintStream::GetAsString( std::string& out )
{
    out.assign( m_BufPtr->begin(), m_BufPtr->end() );
}

void CMemoryPrintStream::vprintf( const char* format, va_list argList )
{
    uint32_t dstLen = (uint32_t)_vscprintf( format, argList );
    if( dstLen )
    {
        size_t oldBufLen = m_BufPtr->size();
        m_BufPtr->resize( oldBufLen + dstLen + 1 );

        vsprintf_s( &(*m_BufPtr)[oldBufLen], dstLen + 1, format, argList );

        m_BufPtr->resize( oldBufLen + dstLen );
    }
}

////////////////////////////////////////////////////////////////////////////////
// CBufferingPrintStream

void CBufferingPrintStream::vprintf( const char* format, va_list argList )
{
    uint32_t dstLen = (uint32_t)_vscprintf( format, argList );
    if( dstLen )
    {
        m_Buf.resize( dstLen + 1 );
        char* bufPtr = &m_Buf[0];
        vsprintf_s( bufPtr, dstLen + 1, format, argList );
        PrintBuffer( bufPtr, dstLen );
    }
}

////////////////////////////////////////////////////////////////////////////////
// CDebugPrintStream

void CDebugPrintStream::PrintBuffer( const char* str, size_t strLen )
{
    OutputDebugStringA( str );
}
