/* This file is part of the wvWare 2 project
   Copyright (C) 2001-2003 Werner Trobin <trobin@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

#include "olestream.h"
#include <stdio.h> // FILE,...

#include <gsf/gsf-input.h>
#include <gsf/gsf-output.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

using namespace wvWare;

OLEStream::OLEStream( OLEStorage* storage ) : m_storage( storage )
{
}

OLEStream::~OLEStream()
{
    if ( m_storage )
        m_storage->streamDestroyed( this );
}

void OLEStream::push()
{
    m_positions.push( tell() );
}

bool OLEStream::pop()
{
    if ( m_positions.empty() )
        return false;
    seek( m_positions.top(), G_SEEK_SET );
    m_positions.pop();
    return true;
}


OLEStreamReader::OLEStreamReader( GsfInput* stream, OLEStorage* storage ) :
    OLEStream( storage ), m_stream( stream )
{
}

OLEStreamReader::~OLEStreamReader()
{
    if ( m_stream )
        g_object_unref( G_OBJECT( m_stream ) );
}

bool OLEStreamReader::isValid() const
{
    return m_stream;
}

bool OLEStreamReader::seek( int offset, GSeekType whence )
{
    return gsf_input_seek( m_stream, offset, whence ) == 0;
}

int OLEStreamReader::tell() const
{
#ifdef WV2_CHECKING
    if ( !m_stream )
        return -1;
#endif
    return gsf_input_tell( m_stream );
}

size_t OLEStreamReader::size() const
{
#ifdef WV2_CHECKING
    if ( !m_stream )
        return 0;
#endif
    return gsf_input_size( m_stream );
}

U8 OLEStreamReader::readU8()
{
#ifdef WV2_CHECKING
    if ( !m_stream )
        return 0;
#endif

    U8 ret;
    gsf_input_read( m_stream, sizeof( ret ), static_cast<guint8*>( &ret ) );
    return ret;
}

S8 OLEStreamReader::readS8()
{
    return static_cast<S8>( readU8() );
}

U16 OLEStreamReader::readU16()
{
#ifdef WV2_CHECKING
    if ( !m_stream )
        return 0;
#endif

#if defined(WORDS_BIGENDIAN)
    // Please take care when "optimizing" that and read
    // http://gcc.gnu.org/ml/gcc-bugs/2000-12/msg00429.html
    // and http://www.eskimo.com/~scs/C-faq/q3.8.html
    U16 tmp1 = readU8();
    U16 tmp2 = readU8();
    return ( tmp2 << 8 ) | tmp1;
#else
    U16 ret;
    gsf_input_read( m_stream, sizeof( ret ), reinterpret_cast<guint8*>( &ret ) );
    return ret;
#endif
}

S16 OLEStreamReader::readS16()
{
    return static_cast<S16>( readU16() );
}

U32 OLEStreamReader::readU32()
{
#ifdef WV2_CHECKING
    if ( !m_stream )
        return 0;
#endif

#if defined(WORDS_BIGENDIAN)
    // Please take care when "optimizing" that and read
    // http://gcc.gnu.org/ml/gcc-bugs/2000-12/msg00429.html
    // and http://www.eskimo.com/~scs/C-faq/q3.8.html
    U32 tmp1 = readU16();
    U32 tmp2 = readU16();
    return ( tmp2 << 16 ) | tmp1;
#else
    U32 ret;
    gsf_input_read( m_stream, sizeof( ret ), reinterpret_cast<guint8*>( &ret ) );
    return ret;
#endif
}

S32 OLEStreamReader::readS32()
{
    return static_cast<S32>( readU32() );
}

bool OLEStreamReader::read( U8 *buffer, size_t length )
{
#ifdef WV2_CHECKING
    if ( !m_stream )
        return false;
#endif
    return gsf_input_read( m_stream, length, buffer ) != 0;
}

void OLEStreamReader::dumpStream( const std::string& fileName )
{
    push();
    seek( 0, G_SEEK_SET );

    FILE* myFile = fopen( fileName.c_str(), "w" );
    if ( !myFile ) {
        pop();
        return;
    }

    const size_t buflen = 1024;
    char buffer[ buflen ];
    size_t remaining = size();
    size_t length;

    while ( remaining ) {
        length = remaining > buflen ? buflen : remaining;
        remaining -= length;
        if ( gsf_input_read( m_stream, length, reinterpret_cast<guint8*>( buffer ) ) )
            fwrite( buffer, 1, length, myFile );
    }

    fclose( myFile );
    pop();
}


OLEStreamWriter::OLEStreamWriter( GsfOutput* stream, OLEStorage* storage ) :
    OLEStream( storage ), m_stream( stream )
{
}

OLEStreamWriter::~OLEStreamWriter()
{
    if ( m_stream ) {
        gsf_output_close( m_stream );
        g_object_unref( G_OBJECT( m_stream ) );
    }
}

bool OLEStreamWriter::isValid() const
{
    return m_stream;
}

bool OLEStreamWriter::seek( int offset, GSeekType whence )
{
    return gsf_output_seek( m_stream, offset, whence ) == 0;
}

int OLEStreamWriter::tell() const
{
#ifdef WV2_CHECKING
    if ( !m_stream )
        return -1;
#endif
    return gsf_output_tell(  m_stream );
}

size_t OLEStreamWriter::size() const
{
#ifdef WV2_CHECKING
    if ( !m_stream )
        return 0;
#endif
    return gsf_output_size( m_stream );
}

void OLEStreamWriter::write( U8 data )
{
#ifdef WV2_CHECKING
    if ( !m_stream )
        return;
#endif
    gsf_output_write( m_stream, sizeof( data ), &data );
}

void OLEStreamWriter::write( S8 data )
{
    write( static_cast<U8>( data ) );
}

void OLEStreamWriter::write( U16 data )
{
#ifdef WV2_CHECKING
    if ( !m_stream )
        return;
#endif
    U16 copy = toLittleEndian( data );
    gsf_output_write( m_stream, sizeof( copy ), reinterpret_cast<guint8 *>( &copy ) );
}

void OLEStreamWriter::write( S16 data )
{
    write( static_cast<U16>( data ) );
}

void OLEStreamWriter::write( U32 data )
{
#ifdef WV2_CHECKING
    if ( !m_stream )
        return;
#endif
    U32 copy = toLittleEndian( data );
    gsf_output_write( m_stream, sizeof( copy ), reinterpret_cast<guint8 *>( &copy ) );
}

void OLEStreamWriter::write( S32 data )
{
    write( static_cast<U32>( data ) );
}

void OLEStreamWriter::write( U8* data, size_t length )
{
#ifdef WV2_CHECKING
    if ( !m_stream )
        return;
#endif
    gsf_output_write( m_stream, length, data );
}
