/*
 * Copyright 2009 - 2010 Kevin Ackley (kackley@gwi.net)
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include <cmath>

#include "ImageFileImpl.h"
#include "SourceDestBufferImpl.h"

using namespace e57;
using namespace std;


SourceDestBufferImpl::SourceDestBufferImpl( ImageFileImplWeakPtr destImageFile, const ustring &pathName, const size_t capacity, bool doConversion, bool doScaling )
   : destImageFile_( destImageFile ),
     pathName_( pathName ),
     capacity_( capacity ),
     doConversion_( doConversion ),
     doScaling_( doScaling )
{
}

template<typename T>
void SourceDestBufferImpl::setTypeInfo( T *base, size_t stride )
{
   static_assert( std::is_integral<T>::value || std::is_floating_point<T>::value, "Integral or floating point required." );

   base_ = reinterpret_cast<char*>( base );
   stride_ = stride;

   // this is a little ugly, but it saves us having to pass around the memory representation
   if ( is_same<T, int8_t>::value ) { memoryRepresentation_ = E57_INT8; }
   else if ( is_same<T, uint8_t>::value ) { memoryRepresentation_ = E57_UINT8; }
   else if ( is_same<T, int16_t>::value ) { memoryRepresentation_ = E57_INT16; }
   else if ( is_same<T, uint16_t>::value ) { memoryRepresentation_ = E57_UINT16; }
   else if ( is_same<T, int32_t>::value ) { memoryRepresentation_ = E57_INT32; }
   else if ( is_same<T, uint32_t>::value ) { memoryRepresentation_ = E57_UINT32; }
   else if ( is_same<T, int64_t>::value ) { memoryRepresentation_ = E57_INT64; }
   else if ( is_same<T, bool>::value ) { memoryRepresentation_ = E57_BOOL; }
   else if ( is_same<T, float>::value ) { memoryRepresentation_ = E57_REAL32; }
   else if ( is_same<T, double>::value ) { memoryRepresentation_ = E57_REAL64; }

   checkState_();
}

template void SourceDestBufferImpl::setTypeInfo<int8_t>( int8_t *base, size_t stride );
template void SourceDestBufferImpl::setTypeInfo<uint8_t>( uint8_t *base, size_t stride );
template void SourceDestBufferImpl::setTypeInfo<int16_t>( int16_t *base, size_t stride );
template void SourceDestBufferImpl::setTypeInfo<uint16_t>( uint16_t *base, size_t stride );
template void SourceDestBufferImpl::setTypeInfo<int32_t>( int32_t *base, size_t stride );
template void SourceDestBufferImpl::setTypeInfo<uint32_t>( uint32_t *base, size_t stride );
template void SourceDestBufferImpl::setTypeInfo<int64_t>( int64_t *base, size_t stride );
template void SourceDestBufferImpl::setTypeInfo<bool>( bool *base, size_t stride );
template void SourceDestBufferImpl::setTypeInfo<float>( float *base, size_t stride );
template void SourceDestBufferImpl::setTypeInfo<double>( double *base, size_t stride );


SourceDestBufferImpl::SourceDestBufferImpl(ImageFileImplWeakPtr destImageFile, const ustring &pathName, vector<ustring>* b)
   : destImageFile_(destImageFile),
     pathName_(pathName),
     memoryRepresentation_(E57_USTRING),
     ustrings_(b)
{
   /// don't checkImageFileOpen, checkState_ will do it

   /// Set capacity_ after testing that b is OK
   if (b == nullptr)
   {
      throw E57_EXCEPTION2(E57_ERROR_BAD_BUFFER, "sdbuf.pathName=" + pathName);
   }

   capacity_ = b->size();

   checkState_();

   /// Note that capacity_ is set to the size() of the vector<>, not its capacity().
   /// The size() of *ustrings_ will not be changed as strings are stored in it.
}

template<typename T>
void SourceDestBufferImpl::_setNextReal( T inValue )
{
   static_assert( std::is_same<T, double>::value || std::is_same<T, float>::value,
                  "_setNextReal() requires float or double type" );

   /// don't checkImageFileOpen

   /// Verify have room
   if (nextIndex_ >= capacity_)
      throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "pathName=" + pathName_);

   /// Calc start of memory location, index into buffer using stride_ (the distance between elements).
   char* p = &base_[nextIndex_*stride_];

   switch (memoryRepresentation_) {
      case E57_INT8:
         if (!doConversion_)
            throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
         //??? fault if get special value: NaN, NegInf...  (all other ints below too)
         if (inValue < E57_INT8_MIN || E57_INT8_MAX < inValue)
            throw E57_EXCEPTION2(E57_ERROR_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " value=" + toString(inValue));
         *reinterpret_cast<int8_t*>(p) = static_cast<int8_t>(inValue);
         break;
      case E57_UINT8:
         if (!doConversion_)
            throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
         if (inValue < E57_UINT8_MIN || E57_UINT8_MAX < inValue)
            throw E57_EXCEPTION2(E57_ERROR_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " value=" + toString(inValue));
         *reinterpret_cast<uint8_t*>(p) = static_cast<uint8_t>(inValue);
         break;
      case E57_INT16:
         if (!doConversion_)
            throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
         if (inValue < E57_INT16_MIN || E57_INT16_MAX < inValue)
            throw E57_EXCEPTION2(E57_ERROR_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " value=" + toString(inValue));
         *reinterpret_cast<int16_t*>(p) = static_cast<int16_t>(inValue);
         break;
      case E57_UINT16:
         if (!doConversion_)
            throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
         if (inValue < E57_UINT16_MIN || E57_UINT16_MAX < inValue)
            throw E57_EXCEPTION2(E57_ERROR_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " value=" + toString(inValue));
         *reinterpret_cast<uint16_t*>(p) = static_cast<uint16_t>(inValue);
         break;
      case E57_INT32:
         if (!doConversion_)
            throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
         if (inValue < T(E57_INT32_MIN) || T(E57_INT32_MAX) < inValue)
            throw E57_EXCEPTION2(E57_ERROR_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " value=" + toString(inValue));
         *reinterpret_cast<int32_t*>(p) = static_cast<int32_t>(inValue);
         break;
      case E57_UINT32:
         if (!doConversion_)
            throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
         if (inValue < T(E57_UINT32_MIN) || T(E57_UINT32_MAX) < inValue)
            throw E57_EXCEPTION2(E57_ERROR_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " value=" + toString(inValue));
         *reinterpret_cast<uint32_t*>(p) = static_cast<uint32_t>(inValue);
         break;
      case E57_INT64:
         if (!doConversion_)
            throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
         if (inValue < T(E57_INT64_MIN) || T(E57_INT64_MAX) < inValue)
            throw E57_EXCEPTION2(E57_ERROR_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " value=" + toString(inValue));
         *reinterpret_cast<int64_t*>(p) = static_cast<int64_t>(inValue);
         break;
      case E57_BOOL:
         if (!doConversion_)
            throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
         *reinterpret_cast<bool*>(p) = (inValue ? false : true);
         break;
      case E57_REAL32:
         if ( std::is_same<T, double>::value )
         {
            /// Does this count as conversion?  It loses information.
            /// Check for really large exponents that can't fit in a single precision
            if (inValue < E57_DOUBLE_MIN || E57_DOUBLE_MAX < inValue)
               throw E57_EXCEPTION2(E57_ERROR_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " value=" + toString(inValue));
            *reinterpret_cast<float*>(p) = static_cast<float>(inValue);
         }
         else
         {
            *reinterpret_cast<float*>(p) = inValue;
         }
         break;
      case E57_REAL64:
         //??? does this count as a conversion?
         *reinterpret_cast<double*>(p) = static_cast<double>(inValue);
         break;
      case E57_USTRING:
         throw E57_EXCEPTION2(E57_ERROR_EXPECTING_NUMERIC, "pathName=" + pathName_);
   }

   nextIndex_++;
}

void SourceDestBufferImpl::checkState_() const
{
    /// Implement checkImageFileOpen functionality for SourceDestBufferImpl ctors
    /// Throw an exception if destImageFile (destImageFile_) isn't open
    ImageFileImplSharedPtr destImageFile(destImageFile_);
    if (!destImageFile->isOpen())
        throw E57_EXCEPTION2(E57_ERROR_IMAGEFILE_NOT_OPEN, "fileName=" + destImageFile->fileName());

    /// Check pathName is well formed (can't verify path is defined until associate sdbuffer with CompressedVector later)
    ImageFileImplSharedPtr imf(destImageFile_);
    imf->pathNameCheckWellFormed(pathName_);

    if (memoryRepresentation_ != E57_USTRING) {
        if (base_ == nullptr)
            throw E57_EXCEPTION2(E57_ERROR_BAD_BUFFER, "pathName=" + pathName_);
        if (stride_ == 0)
            throw E57_EXCEPTION2(E57_ERROR_BAD_BUFFER, "pathName=" + pathName_);
        //??? check base alignment, depending on CPU type
        //??? check if stride too small, positive or negative
    } else {
        if (ustrings_ == nullptr)
            throw E57_EXCEPTION2(E57_ERROR_BAD_BUFFER, "pathName=" + pathName_);
    }
}

int64_t SourceDestBufferImpl::getNextInt64()
{
    /// don't checkImageFileOpen

    /// Verify index is within bounds
    if (nextIndex_ >= capacity_)
        throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "pathName=" + pathName_);

    /// Fetch value from source buffer.
    /// Convert from non-integer formats if requested.
    char* p = &base_[nextIndex_*stride_];
    int64_t value;
    switch (memoryRepresentation_) {
        case E57_INT8:
            value = static_cast<int64_t>(*reinterpret_cast<int8_t*>(p));
            break;
        case E57_UINT8:
            value = static_cast<int64_t>(*reinterpret_cast<uint8_t*>(p));
            break;
        case E57_INT16:
            value = static_cast<int64_t>(*reinterpret_cast<int16_t*>(p));
            break;
        case E57_UINT16:
            value = static_cast<int64_t>(*reinterpret_cast<uint16_t*>(p));
            break;
        case E57_INT32:
            value = static_cast<int64_t>(*reinterpret_cast<int32_t*>(p));
            break;
        case E57_UINT32:
            value = static_cast<int64_t>(*reinterpret_cast<uint32_t*>(p));
            break;
        case E57_INT64:
            value = *reinterpret_cast<int64_t*>(p);
            break;
        case E57_BOOL:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            /// Convert bool to 0/1, all non-zero values map to 1.0
            value = (*reinterpret_cast<bool*>(p)) ? 1 : 0;
            break;
        case E57_REAL32:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            //??? fault if get special value: NaN, NegInf...
            value = static_cast<int64_t>(*reinterpret_cast<float*>(p));
            break;
        case E57_REAL64:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            //??? fault if get special value: NaN, NegInf...
            value = static_cast<int64_t>(*reinterpret_cast<double*>(p));
            break;
        case E57_USTRING:
            throw E57_EXCEPTION2(E57_ERROR_EXPECTING_NUMERIC, "pathName=" + pathName_);
        default:
            throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "pathName=" + pathName_);
    }
    nextIndex_++;
    return(value);
}

int64_t SourceDestBufferImpl::getNextInt64(double scale, double offset)
{
    /// don't checkImageFileOpen

    /// Reverse scale (undo scaling) of a user's number to get raw value to put in file.

    /// Encorporating the scale is optional (requested by user when constructing the sdbuf).
    /// If the user did not request scaling, then we get raw values from user's buffer.
    if (!doScaling_) {
        /// Just return raw value.
        return(getNextInt64());
    }

    /// Double check non-zero scale.  Going to divide by it below.
    if (scale == 0)
        throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "pathName=" + pathName_);

    /// Verify index is within bounds
    if (nextIndex_ >= capacity_)
        throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "pathName=" + pathName_);

    /// Fetch value from source buffer.
    /// Convert from non-integer formats if requested
    char* p = &base_[nextIndex_*stride_];
    double doubleRawValue;
    switch (memoryRepresentation_) {
        case E57_INT8:
            /// Calc (x-offset)/scale rounded to nearest integer, but keep in floating point until sure is in bounds
            doubleRawValue = floor((*reinterpret_cast<int8_t*>(p) - offset)/scale + 0.5);
            break;
        case E57_UINT8:
            /// Calc (x-offset)/scale rounded to nearest integer, but keep in floating point until sure is in bounds
            doubleRawValue = floor((*reinterpret_cast<uint8_t*>(p) - offset)/scale + 0.5);
            break;
        case E57_INT16:
            /// Calc (x-offset)/scale rounded to nearest integer, but keep in floating point until sure is in bounds
            doubleRawValue = floor((*reinterpret_cast<int16_t*>(p) - offset)/scale + 0.5);
            break;
        case E57_UINT16:
            /// Calc (x-offset)/scale rounded to nearest integer, but keep in floating point until sure is in bounds
            doubleRawValue = floor((*reinterpret_cast<uint16_t*>(p) - offset)/scale + 0.5);
            break;
        case E57_INT32:
            /// Calc (x-offset)/scale rounded to nearest integer, but keep in floating point until sure is in bounds
            doubleRawValue = floor((*reinterpret_cast<int32_t*>(p) - offset)/scale + 0.5);
            break;
        case E57_UINT32:
            /// Calc (x-offset)/scale rounded to nearest integer, but keep in floating point until sure is in bounds
            doubleRawValue = floor((*reinterpret_cast<uint32_t*>(p) - offset)/scale + 0.5);
            break;
        case E57_INT64:
            /// Calc (x-offset)/scale rounded to nearest integer, but keep in floating point until sure is in bounds
            doubleRawValue = floor((*reinterpret_cast<int64_t*>(p) - offset)/scale + 0.5);
            break;
        case E57_BOOL:
            if (*reinterpret_cast<bool*>(p))
                doubleRawValue = floor((1 - offset)/scale + 0.5);
            else
                doubleRawValue = floor((0 - offset)/scale + 0.5);
            break;
        case E57_REAL32:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            //??? fault if get special value: NaN, NegInf...

            /// Calc (x-offset)/scale rounded to nearest integer, but keep in floating point until sure is in bounds
            doubleRawValue = floor((*reinterpret_cast<float*>(p) - offset)/scale + 0.5);
            break;
        case E57_REAL64:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            //??? fault if get special value: NaN, NegInf...

            /// Calc (x-offset)/scale rounded to nearest integer, but keep in floating point until sure is in bounds
            doubleRawValue = floor((*reinterpret_cast<double*>(p) - offset)/scale + 0.5);
            break;
        case E57_USTRING:
            throw E57_EXCEPTION2(E57_ERROR_EXPECTING_NUMERIC, "pathName=" + pathName_);
        default:
            throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "pathName=" + pathName_);
    }

    /// Make sure that value is representable in an int64_t
    if (doubleRawValue < E57_INT64_MIN || E57_INT64_MAX < (int64_t)doubleRawValue) {
        throw E57_EXCEPTION2(E57_ERROR_SCALED_VALUE_NOT_REPRESENTABLE,
                             "pathName=" + pathName_
                             + " value=" + toString(doubleRawValue));
    }

    auto rawValue = static_cast<int64_t>(doubleRawValue);

    nextIndex_++;
    return(rawValue);
}

float SourceDestBufferImpl::getNextFloat()
{
    /// don't checkImageFileOpen

    /// Verify index is within bounds
    if (nextIndex_ >= capacity_)
        throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "pathName=" + pathName_);

    /// Fetch value from source buffer.
    /// Convert from other formats to floating point if requested
    char* p = &base_[nextIndex_*stride_];
    float value;
    switch (memoryRepresentation_) {
        case E57_INT8:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            value = static_cast<float>(*reinterpret_cast<int8_t*>(p));
            break;
        case E57_UINT8:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            value = static_cast<float>(*reinterpret_cast<uint8_t*>(p));
            break;
        case E57_INT16:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            value = static_cast<float>(*reinterpret_cast<int16_t*>(p));
            break;
        case E57_UINT16:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            value = static_cast<float>(*reinterpret_cast<uint16_t*>(p));
            break;
        case E57_INT32:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            value = static_cast<float>(*reinterpret_cast<int32_t*>(p));
            break;
        case E57_UINT32:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            value = static_cast<float>(*reinterpret_cast<uint32_t*>(p));
            break;
        case E57_INT64:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            value = static_cast<float>(*reinterpret_cast<int64_t*>(p));
            break;
        case E57_BOOL:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);

            /// Convert bool to 0/1, all non-zero values map to 1.0
            value = (*reinterpret_cast<bool*>(p)) ? 1.0F : 0.0F;
            break;
        case E57_REAL32:
            value = *reinterpret_cast<float*>(p);
            break;
        case E57_REAL64: {
            /// Check that exponent of user's value is not too large for single precision number in file.
            double d = *reinterpret_cast<double*>(p);

            // The cast here eliminates a compiler warning because the MAX value can't be
            // represented exactly as a double. It's rounded up, which is fine for our case
            if (d < double(E57_DOUBLE_MIN) || double(E57_DOUBLE_MAX) < d)
                throw E57_EXCEPTION2(E57_ERROR_REAL64_TOO_LARGE, "pathName=" + pathName_ + " value=" + toString(d));
            value = static_cast<float>(d);
            break;
        }
        case E57_USTRING:
            throw E57_EXCEPTION2(E57_ERROR_EXPECTING_NUMERIC, "pathName=" + pathName_);
        default:
            throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "pathName=" + pathName_);
    }
    nextIndex_++;
    return(value);
}

double SourceDestBufferImpl::getNextDouble()
{
    /// don't checkImageFileOpen

    /// Verify index is within bounds
    if (nextIndex_ >= capacity_)
        throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "pathName=" + pathName_);

    /// Fetch value from source buffer.
    /// Convert from other formats to floating point if requested
    char* p = &base_[nextIndex_*stride_];
    double value;
    switch (memoryRepresentation_) {
        case E57_INT8:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            value = static_cast<double>(*reinterpret_cast<int8_t*>(p));
            break;
        case E57_UINT8:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            value = static_cast<double>(*reinterpret_cast<uint8_t*>(p));
            break;
        case E57_INT16:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            value = static_cast<double>(*reinterpret_cast<int16_t*>(p));
            break;
        case E57_UINT16:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            value = static_cast<double>(*reinterpret_cast<uint16_t*>(p));
            break;
        case E57_INT32:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            value = static_cast<double>(*reinterpret_cast<int32_t*>(p));
            break;
        case E57_UINT32:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            value = static_cast<double>(*reinterpret_cast<uint32_t*>(p));
            break;
        case E57_INT64:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            value = static_cast<double>(*reinterpret_cast<int64_t*>(p));
            break;
        case E57_BOOL:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            /// Convert bool to 0/1, all non-zero values map to 1.0
            value = (*reinterpret_cast<bool*>(p)) ? 1.0 : 0.0;
            break;
        case E57_REAL32:
            value = static_cast<double>(*reinterpret_cast<float*>(p));
            break;
        case E57_REAL64:
            value = *reinterpret_cast<double*>(p);
            break;
        case E57_USTRING:
            throw E57_EXCEPTION2(E57_ERROR_EXPECTING_NUMERIC, "pathName=" + pathName_);
        default:
            throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "pathName=" + pathName_);
    }
    nextIndex_++;
    return(value);
}

ustring SourceDestBufferImpl::getNextString()
{
    /// don't checkImageFileOpen

    /// Check have correct type buffer
    if (memoryRepresentation_ != E57_USTRING)
        throw E57_EXCEPTION2(E57_ERROR_EXPECTING_USTRING, "pathName=" + pathName_);

    /// Verify index is within bounds
    if (nextIndex_ >= capacity_)
        throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "pathName=" + pathName_);

    /// Get ustring from vector
    return((*ustrings_)[nextIndex_++]);
}

void  SourceDestBufferImpl::setNextInt64(int64_t value)
{
    /// don't checkImageFileOpen

    /// Verify have room
    if (nextIndex_ >= capacity_)
        throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "pathName=" + pathName_);

    /// Calc start of memory location, index into buffer using stride_ (the distance between elements).
    char* p = &base_[nextIndex_*stride_];

    switch (memoryRepresentation_) {
        case E57_INT8:
            if (value < E57_INT8_MIN || E57_INT8_MAX < value)
                throw E57_EXCEPTION2(E57_ERROR_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " value=" + toString(value));
            *reinterpret_cast<int8_t*>(p) = static_cast<int8_t>(value);
            break;
        case E57_UINT8:
            if (value < E57_UINT8_MIN || E57_UINT8_MAX < value)
                throw E57_EXCEPTION2(E57_ERROR_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " value=" + toString(value));
            *reinterpret_cast<uint8_t*>(p) = static_cast<uint8_t>(value);
            break;
        case E57_INT16:
            if (value < E57_INT16_MIN || E57_INT16_MAX < value)
                throw E57_EXCEPTION2(E57_ERROR_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " value=" + toString(value));
            *reinterpret_cast<int16_t*>(p) = static_cast<int16_t>(value);
            break;
        case E57_UINT16:
            if (value < E57_UINT16_MIN || E57_UINT16_MAX < value)
                throw E57_EXCEPTION2(E57_ERROR_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " value=" + toString(value));
            *reinterpret_cast<uint16_t*>(p) = static_cast<uint16_t>(value);
            break;
        case E57_INT32:
            if (value < E57_INT32_MIN || E57_INT32_MAX < value)
                throw E57_EXCEPTION2(E57_ERROR_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " value=" + toString(value));
            *reinterpret_cast<int32_t*>(p) = static_cast<int32_t>(value);
            break;
        case E57_UINT32:
            if (value < E57_UINT32_MIN || E57_UINT32_MAX < value)
                throw E57_EXCEPTION2(E57_ERROR_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " value=" + toString(value));
            *reinterpret_cast<uint32_t*>(p) = static_cast<uint32_t>(value);
            break;
        case E57_INT64:
            *reinterpret_cast<int64_t*>(p) = static_cast<int64_t>(value);
            break;
        case E57_BOOL:
            *reinterpret_cast<bool*>(p) = (value ? false : true);
            break;
        case E57_REAL32:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            //??? very large integers may lose some lowest bits here. error?
            *reinterpret_cast<float*>(p) = static_cast<float>(value);
            break;
        case E57_REAL64:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            *reinterpret_cast<double*>(p) = static_cast<double>(value);
            break;
        case E57_USTRING:
            throw E57_EXCEPTION2(E57_ERROR_EXPECTING_NUMERIC, "pathName=" + pathName_);
    }

    nextIndex_++;
}

void  SourceDestBufferImpl::setNextInt64(int64_t value, double scale, double offset)
{
    /// don't checkImageFileOpen

    /// Apply a scale and offset to numbers from file before puting in user's buffer.

    /// Encorporating the scale is optional (requested by user when constructing the sdbuf).
    /// If the user did not request scaling, then we send raw values to user's buffer.
    if (!doScaling_) {
        /// Use raw value routine, then bail out.
        setNextInt64(value);
        return;
    }

    /// Verify have room
    if (nextIndex_ >= capacity_)
        throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "pathName=" + pathName_);

    /// Calc start of memory location, index into buffer using stride_ (the distance between elements).
    char* p = &base_[nextIndex_*stride_];

    /// Calc x*scale+offset
    double scaledValue;
    if (memoryRepresentation_ == E57_REAL32 || memoryRepresentation_ == E57_REAL64) {
        /// Value will be stored in some floating point rep in user's buffer, so keep full resolution here.
        scaledValue = value*scale + offset;
    } else {
        /// Value will represented as some integer in user's buffer, so round to nearest integer here.
        /// But keep in floating point rep until we know that the value is representable in the user's buffer.
        scaledValue = floor(value*scale + offset + 0.5);
    }

    switch (memoryRepresentation_) {
        case E57_INT8:
            if (scaledValue < E57_INT8_MIN || E57_INT8_MAX < scaledValue)
                throw E57_EXCEPTION2(E57_ERROR_SCALED_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " scaledValue=" + toString(scaledValue));
            *reinterpret_cast<int8_t*>(p) = static_cast<int8_t>(scaledValue);
            break;
        case E57_UINT8:
            if (scaledValue < E57_UINT8_MIN || E57_UINT8_MAX < scaledValue)
                throw E57_EXCEPTION2(E57_ERROR_SCALED_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " scaledValue=" + toString(scaledValue));
            *reinterpret_cast<uint8_t*>(p) = static_cast<uint8_t>(scaledValue);
            break;
        case E57_INT16:
            if (scaledValue < E57_INT16_MIN || E57_INT16_MAX < scaledValue)
                throw E57_EXCEPTION2(E57_ERROR_SCALED_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " scaledValue=" + toString(scaledValue));
            *reinterpret_cast<int16_t*>(p) = static_cast<int16_t>(scaledValue);
            break;
        case E57_UINT16:
            if (scaledValue < E57_UINT16_MIN || E57_UINT16_MAX < scaledValue)
                throw E57_EXCEPTION2(E57_ERROR_SCALED_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " scaledValue=" + toString(scaledValue));
            *reinterpret_cast<uint16_t*>(p) = static_cast<uint16_t>(scaledValue);
            break;
        case E57_INT32:
            if (scaledValue < E57_INT32_MIN || E57_INT32_MAX < scaledValue)
                throw E57_EXCEPTION2(E57_ERROR_SCALED_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " scaledValue=" + toString(scaledValue));
            *reinterpret_cast<int32_t*>(p) = static_cast<int32_t>(scaledValue);
            break;
        case E57_UINT32:
            if (scaledValue < E57_UINT32_MIN || E57_UINT32_MAX < scaledValue)
                throw E57_EXCEPTION2(E57_ERROR_SCALED_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " scaledValue=" + toString(scaledValue));
            *reinterpret_cast<uint32_t*>(p) = static_cast<uint32_t>(scaledValue);
            break;
        case E57_INT64:
            *reinterpret_cast<int64_t*>(p) = static_cast<int64_t>(scaledValue);
            break;
        case E57_BOOL:
            *reinterpret_cast<bool*>(p) = (scaledValue ? false : true);
            break;
        case E57_REAL32:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            /// Check that exponent of result is not too big for single precision float
            if (scaledValue < E57_DOUBLE_MIN || E57_DOUBLE_MAX < scaledValue)
                throw E57_EXCEPTION2(E57_ERROR_SCALED_VALUE_NOT_REPRESENTABLE, "pathName=" + pathName_ + " scaledValue=" + toString(scaledValue));
            *reinterpret_cast<float*>(p) = static_cast<float>(scaledValue);
            break;
        case E57_REAL64:
            if (!doConversion_)
                throw E57_EXCEPTION2(E57_ERROR_CONVERSION_REQUIRED, "pathName=" + pathName_);
            *reinterpret_cast<double*>(p) = scaledValue;
            break;
        case E57_USTRING:
            throw E57_EXCEPTION2(E57_ERROR_EXPECTING_NUMERIC, "pathName=" + pathName_);
    }

    nextIndex_++;
}

void SourceDestBufferImpl::setNextFloat(float value)
{
   _setNextReal( value );
}

void SourceDestBufferImpl::setNextDouble(double value)
{
    _setNextReal( value );
}

void SourceDestBufferImpl::setNextString(const ustring& value)
{
    /// don't checkImageFileOpen

    if (memoryRepresentation_ != E57_USTRING)
        throw E57_EXCEPTION2(E57_ERROR_EXPECTING_USTRING, "pathName=" + pathName_);

    /// Verify have room.
    if (nextIndex_ >= capacity_)
        throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "pathName=" + pathName_);

    /// Assign to already initialized element in vector
    (*ustrings_)[nextIndex_] = value;
    nextIndex_++;
}

void SourceDestBufferImpl::checkCompatible(const shared_ptr<SourceDestBufferImpl> &newBuf) const
{
    if (pathName_ != newBuf->pathName()) {
        throw E57_EXCEPTION2(E57_ERROR_BUFFERS_NOT_COMPATIBLE,
                             "pathName=" + pathName_
                             + " newPathName=" + newBuf->pathName());
    }
    if (memoryRepresentation_ != newBuf->memoryRepresentation()) {
        throw E57_EXCEPTION2(E57_ERROR_BUFFERS_NOT_COMPATIBLE,
                             "memoryRepresentation=" + toString(memoryRepresentation_)
                             + " newMemoryType=" + toString(newBuf->memoryRepresentation()));
    }
    if (capacity_ != newBuf->capacity()) {
        throw E57_EXCEPTION2(E57_ERROR_BUFFERS_NOT_COMPATIBLE,
                             "capacity=" + toString(capacity_)
                             + " newCapacity=" + toString(newBuf->capacity()));
    }
    if (doConversion_ != newBuf->doConversion()) {
        throw E57_EXCEPTION2(E57_ERROR_BUFFERS_NOT_COMPATIBLE,
                             "doConversion=" + toString(doConversion_)
                             + "newDoConversion=" + toString(newBuf->doConversion()));
    }
    if (doConversion_ != newBuf->doConversion()) {
        throw E57_EXCEPTION2(E57_ERROR_BUFFERS_NOT_COMPATIBLE,
                             "doConversion=" + toString(doConversion_)
                             + " newDoConversion=" + toString(newBuf->doConversion()));
    }
    if (stride_ != newBuf->stride()) {
        throw E57_EXCEPTION2(E57_ERROR_BUFFERS_NOT_COMPATIBLE,
                             "stride=" + toString(stride_)
                             + " newStride=" + toString(newBuf->stride()));
    }
}

#ifdef E57_DEBUG
void SourceDestBufferImpl::dump(int indent, ostream& os)
{
    /// don't checkImageFileOpen

    os << space(indent) << "pathName:             " << pathName_ << endl;
    os << space(indent) << "memoryRepresentation: ";
    switch (memoryRepresentation_) {
        case E57_INT8:      os << "int8_t" << endl;    break;
        case E57_UINT8:     os << "uint8_t" << endl;   break;
        case E57_INT16:     os << "int16_t" << endl;    break;
        case E57_UINT16:    os << "uint16_t" << endl;  break;
        case E57_INT32:     os << "int32_t" << endl;   break;
        case E57_UINT32:    os << "uint32_t" << endl;  break;
        case E57_INT64:     os << "int64_t" << endl;   break;
        case E57_BOOL:      os << "bool" << endl;      break;
        case E57_REAL32:    os << "float" << endl;     break;
        case E57_REAL64:    os << "double" << endl;    break;
        case E57_USTRING:   os << "ustring" << endl;   break;
        default:            os << "<unknown>" << endl; break;
    }
    os << space(indent) << "base:                 " << static_cast<const void*>(base_) << endl;
    os << space(indent) << "ustrings:             " << static_cast<const void*>(ustrings_) << endl;
    os << space(indent) << "capacity:             " << capacity_ << endl;
    os << space(indent) << "doConversion:         " << doConversion_ << endl;
    os << space(indent) << "doScaling:            " << doScaling_ << endl;
    os << space(indent) << "stride:               " << stride_ << endl;
    os << space(indent) << "nextIndex:            " << nextIndex_ << endl;
}
#endif
