/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <oox/helper/binaryoutputstream.hxx>

#include <com/sun/star/io/XOutputStream.hpp>
#include <com/sun/star/io/XSeekable.hpp>
#include <osl/diagnose.h>
#include <string.h>

namespace oox {

using namespace ::com::sun::star::io;
using namespace ::com::sun::star::uno;

namespace {

const sal_Int32 OUTPUTSTREAM_BUFFERSIZE     = 0x8000;

} // namespace

BinaryXOutputStream::BinaryXOutputStream( const Reference< XOutputStream >& rxOutStrm, bool bAutoClose ) :
    BinaryStreamBase( Reference< XSeekable >( rxOutStrm, UNO_QUERY ).is() ),
    BinaryXSeekableStream( Reference< XSeekable >( rxOutStrm, UNO_QUERY ) ),
    maBuffer( OUTPUTSTREAM_BUFFERSIZE ),
    mxOutStrm( rxOutStrm ),
    mbAutoClose( bAutoClose && rxOutStrm.is() )
{
    mbEof = !mxOutStrm.is();
}

BinaryXOutputStream::~BinaryXOutputStream()
{
    close();
}

void BinaryXOutputStream::close()
{
    OSL_ENSURE( !mbAutoClose || mxOutStrm.is(), "BinaryXOutputStream::close - invalid call" );
    if( mxOutStrm.is() ) try
    {
        mxOutStrm->flush();
        if ( mbAutoClose )
            mxOutStrm->closeOutput();
    }
    catch( Exception& )
    {
        OSL_FAIL( "BinaryXOutputStream::close - closing output stream failed" );
    }
    mxOutStrm.clear();
    mbAutoClose = false;
    BinaryXSeekableStream::close();
}

void BinaryXOutputStream::writeData( const StreamDataSequence& rData, size_t /*nAtomSize*/ )
{
    if( mxOutStrm.is() ) try
    {
        mxOutStrm->writeBytes( rData );
    }
    catch( Exception& )
    {
        OSL_FAIL( "BinaryXOutputStream::writeData - stream read error" );
    }
}

void BinaryXOutputStream::writeMemory( const void* pMem, sal_Int32 nBytes, size_t nAtomSize )
{
    if( !(mxOutStrm.is() && (nBytes > 0)) )
        return;

    sal_Int32 nBufferSize = getLimitedValue< sal_Int32, sal_Int32 >( nBytes, 0, (OUTPUTSTREAM_BUFFERSIZE / nAtomSize) * nAtomSize );
    const sal_uInt8* pnMem = static_cast< const sal_uInt8* >( pMem );
    while( nBytes > 0 )
    {
        sal_Int32 nWriteSize = getLimitedValue< sal_Int32, sal_Int32 >( nBytes, 0, nBufferSize );
        maBuffer.realloc( nWriteSize );
        memcpy( maBuffer.getArray(), pnMem, static_cast< size_t >( nWriteSize ) );
        writeData( maBuffer, nAtomSize );
        pnMem += nWriteSize;
        nBytes -= nWriteSize;
    }
}

void
BinaryOutputStream::writeCharArrayUC( const OUString& rString, rtl_TextEncoding eTextEnc )
{
    OString sBuf( OUStringToOString( rString, eTextEnc ) );
    sBuf = sBuf.replace( '\0', '?' );
    writeMemory( static_cast< const void* >( sBuf.getStr() ), sBuf.getLength() );
}

void
BinaryOutputStream::writeUnicodeArray( const OUString& rString )
{
    OUString sBuf = rString.replace( '\0', '?' );
#ifdef OSL_BIGENDIAN
    // need a non-const buffer for swapping byte order
    sal_Unicode notConst[sBuf.getLength()];
    memcpy( notConst, sBuf.getStr(), sizeof(sal_Unicode)*sBuf.getLength() );
    writeArray( notConst, sBuf.getLength() );
#else
    writeArray( sBuf.getStr(), sBuf.getLength() );
#endif
}

void BinaryOutputStream::writeCompressedUnicodeArray( const OUString& rString, bool bCompressed )
{
    if ( bCompressed )
         // ISO-8859-1 maps all byte values 0xHH to the same Unicode code point U+00HH
        writeCharArrayUC( rString, RTL_TEXTENCODING_ISO_8859_1 );
    else
        writeUnicodeArray( rString );
}

SequenceOutputStream::SequenceOutputStream( StreamDataSequence & rData ) :
    BinaryStreamBase( true ),
    mpData( &rData ),
    mnPos( 0 )
{
}

void SequenceOutputStream::writeData( const StreamDataSequence& rData, size_t nAtomSize )
{
    if( mpData && rData.hasElements() )
        writeMemory( rData.getConstArray(), rData.getLength(), nAtomSize );
}

void SequenceOutputStream::writeMemory( const void* pMem, sal_Int32 nBytes, size_t /*nAtomSize*/ )
{
    if( mpData && (nBytes > 0) )
    {
        if( mpData->getLength() - mnPos < nBytes )
            mpData->realloc( mnPos + nBytes );
        memcpy( mpData->getArray() + mnPos, pMem, static_cast< size_t >( nBytes ) );
        mnPos += nBytes;
    }
}

sal_Int64 SequenceOutputStream::size() const
{
    return mpData ? mpData->getLength() : -1;
}

sal_Int64 SequenceOutputStream::tell() const
{
    return mpData ? mnPos : -1;
}

void SequenceOutputStream::seek( sal_Int64 nPos )
{
    if( mpData )
    {
        mnPos = getLimitedValue< sal_Int32, sal_Int64 >( nPos, 0, mpData->getLength() );
        mbEof = mnPos != nPos;
    }
}

void SequenceOutputStream::close()
{
    mpData = nullptr;
    mbEof = true;
}


} // namespace oox

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */