/*
 * MtxIOStream.cpp
 *
 *  Created on: Feb 15, 2012
 *      Author: godin
 */

#include "MtxIOStream.h"

MtxIOStream::MtxIOStream( const char* filename, bool clobber )
    : FileIOStream( filename, clobber )
{}

MtxIOStream::~MtxIOStream() {}

Mtx MtxIOStream::read( Logger& logger, int minN, int maxN,
                       Syslog::Severity severity )
{
    bool ok( true );
    int m( 0 ), n( 0 );
    Mtx newData( logger, m, n, nanf( "" ), severity );
    seek( 0 );
    ok &= readInt( m ).bytesRead() == 4;
    if( ok ) ok &= readInt( n ).bytesRead() == 4;
    if( n <= 0 )
    {
        struct stat stat = getStat();
        off_t size = stat.st_size;
        if( size > 8 )
        {
            n = ( size - 8 ) / ( 4 *  m );
        }
        else
        {
            n = 0;
        }
    }
    if( ok )
    {
        if( minN > 0 && minN < n )
        {
            n -= minN;
            maxN -= minN;
            seek( ( minN * 4 * m ) + 8 );
        }
        if( maxN > 0 && maxN < n )
        {
            n = maxN;
        }
        newData.setSize( m, n );
        for( int j = 0; ok && j < n; ++j )
        {
            for( int i = 0; ok && i < m; ++i )
            {
                ok &= readFloat( newData[i][j] ).bytesRead() == 4;
            }
        }
        if( ok )
        {
            //printf( "Read %dx%d matrix at %s\n", m, n, getName() );
            return newData;
        }
    }
    logger.syslog( "Failed to read data from ", getName(), severity );
    return newData;
}

bool MtxIOStream::write( const Mtx& mtx, Logger& logger,
                         int minM, int maxM, int minN, int maxN,
                         Syslog::Severity severity )
{
    bool ok( true );
    minM = minM >= 0 ? minM : mtx.getM() + minM;
    maxM = maxM >= 0 ? maxM : mtx.getM() + maxM;
    minN = minN >= 0 ? minN : mtx.getN() + minN;
    maxN = maxN >= 0 ? maxN : mtx.getN() + maxN;
    int m = maxM - minM + 1;
    int n = maxN - minN + 1;
    ok = m >= 0 && n >= 0;
    seek( 0 );
    ok &= writeInt( m ).bytesWritten() == 4;
    if( ok )
    {
        if( mtx.isUnlimitedN() )
        {
            ok &= writeInt( -1 ).bytesWritten() == 4;
        }
        else
        {
            ok &= writeInt( n ).bytesWritten() == 4;
        }
    }
    if( ok )
    {
        for( int j = 0; ok && j < n; ++j )
        {
            for( int i = 0; ok && i < m; ++i )
            {
                ok &= writeFloat( mtx( i + minM, j + minN ) ).bytesWritten() == 4;
            }
        }
        if( ok )
        {
            //printf( "Write %dx%d matrix at %s\n", m, n, getName() );
            return ok;
        }
    }
    logger.syslog( "Failed to write data from ", getName(), severity );
    return ok;
}

bool MtxIOStream::append( const Mtx& mtx, Logger& logger,
                          Syslog::Severity severity )
{
    bool ok( true );

    struct stat stat = getStat();
    off_t size = stat.st_size;

    if( 0 == size )
    {
        return write( mtx, logger, 0, -1, 0, -1, severity );
    }

    int m = mtx.getM();
    int n = mtx.getN();
    ok = m >= 0 && n >= 0;

    int fileM( 0 ), fileN( 0 );
    seek( 0 );
    ok &= readInt( fileM ).bytesRead() == 4;
    if( ok ) ok &= readInt( fileN ).bytesRead() == 4;

    if( m != fileM )
    {
        logger.syslog( Str( "Can not append Mtx with m=" ) + m
                       + " to file with m=" + fileM, severity );
        return false;
    }
    if( fileN > 0 )
    {
        logger.syslog( "Can not append Mtx to file without unlimited n", severity );
        return false;
    }

    fileN = ( size - 8 ) / ( fileM * 4 );
    seek( fileN * fileM * 4 + 8 );

    if( ok )
    {
        for( int j = 0; ok && j < n; ++j )
        {
            for( int i = 0; ok && i < m; ++i )
            {
                ok &= writeFloat( mtx( i, j ) ).bytesWritten() == 4;
            }
        }
        if( ok )
        {
            //printf( "Write %dx%d matrix at %s\n", m, n, getName() );
            return ok;
        }
    }
    logger.syslog( "Failed to write data from ", getName(), severity );
    return ok;
}
