switch( d->type )
    {
    case Ascii:
        return static_cast< const QExifAsciiValuePrivate * >( d.constData() )->value.toUtf8();
    case Undefined:
        return static_cast< const QExifUndefinedValuePrivate * >( d.constData() )->value;
    default:
        return QByteArray();
    }

/****************************************************************************
**
** This file is part of the Qt Extended Opensource Package.
**
**
** Contact: Qt Extended Information (example@nnthink.com)
**
**
**
** This file was modified by Philipp Berger for the openMittsu project.
** 2016/12: Bug fixes and refactoring.
**
**
****************************************************************************/
#include "src/utility/QExifImageHeader.h"

#include <QFile>
#include <QImage>
#include <QDataStream>
#include <QBuffer>
#include <QDateTime>
#include <QtDebug>
#include <QTextCodec>

/*!
    \typedef QExifSRational

    A synonym for \c QPair<qint32,qint32> representing a signed rational number as stored in EXIF
    headers.  The first integer in the pair is the numerator and the second the denominator.
*/

/*!
    \typedef QExifURational

    A synonym for \c QPair<qint32,qint32> representing an unsigned rational number as stored in
    EXIF headers.  The first integer in the pair is the numerator and the second the denominator.
*/

struct ExifIfdHeader
{
    quint16 tag;
    quint16 type;
    quint32 count;
    union
    {
        quint32 offset;
        quint8 offsetBytes[ 4 ];
        char offsetAscii[ 4 ];
        quint16 offsetShorts[ 2 ];
    };
};

QDataStream &operator >>( QDataStream &stream, ExifIfdHeader &header )
{
    stream >> header.tag;
    stream >> header.type;
    stream >> header.count;

    if( header.type == QExifValue::Byte && header.count <= 4 )
    {
        stream.readRawData( header.offsetAscii, 4 );
    }
    else if( header.type == QExifValue::Ascii && header.count <= 4 )
    {
        stream.readRawData( header.offsetAscii, 4 );
    }
    else if( header.type == QExifValue::Short && header.count <= 2 )
    {
        stream >> header.offsetShorts[ 0 ];
        stream >> header.offsetShorts[ 1 ];
    }
    else
    {
        stream >> header.offset;
    }

    return stream;
}

class QExifValuePrivate : public QSharedData
{
public:
    QExifValuePrivate( quint16 t, int c )
        : type( t ), count( c )
    {}
    virtual ~QExifValuePrivate(){}

    quint16 type;
    int count;
};

class QExifByteValuePrivate : public QExifValuePrivate
{
public:
    QExifByteValuePrivate()
        : QExifValuePrivate(QExifValue::Byte, 0)
    { ref.ref(); }
    QExifByteValuePrivate( const QVector< quint8 > &v )
        : QExifValuePrivate( QExifValue::Byte, v.size() ), value( v )
    {}

    QVector< quint8 > value;
};

class QExifUndefinedValuePrivate : public QExifValuePrivate
{
public:
    QExifUndefinedValuePrivate( const QByteArray &v )
        : QExifValuePrivate( QExifValue::Undefined, v.size() ), value( v )
    {}

    QByteArray value;
};

class QExifAsciiValuePrivate : public QExifValuePrivate
{
public:
    QExifAsciiValuePrivate( const QString &v )
        : QExifValuePrivate( QExifValue::Ascii, v.toUtf8().size() + 1 ), value( v )
    {}

    QString value;
};

class QExifShortValuePrivate : public QExifValuePrivate
{
public:
    QExifShortValuePrivate( const QVector< quint16 > &v )
        : QExifValuePrivate( QExifValue::Short, v.size() ), value( v )
    {}

    QVector< quint16 > value;
};

class QExifLongValuePrivate : public QExifValuePrivate
{
public:
    QExifLongValuePrivate( const QVector< quint32 > &v )
        : QExifValuePrivate( QExifValue::Long, v.size() ), value( v )
    {}

    QVector< quint32 > value;
};

class QExifSignedLongValuePrivate : public QExifValuePrivate
{
public:
    QExifSignedLongValuePrivate( const QVector< qint32 > &v )
        : QExifValuePrivate( QExifValue::SignedLong, v.size() ), value( v )
    {}

    QVector< qint32 > value;
};

class QExifRationalValuePrivate : public QExifValuePrivate
{
public:
    QExifRationalValuePrivate( const QVector< QExifURational > &v )
        : QExifValuePrivate( QExifValue::Rational, v.size() ), value( v )
    {}

    QVector< QExifURational > value;
};

class QExifSignedRationalValuePrivate : public QExifValuePrivate
{
public:
    QExifSignedRationalValuePrivate( const QVector< QExifSRational > &v )
        : QExifValuePrivate( QExifValue::SignedRational, v.size() ), value( v )
    {}

    QVector< QExifSRational > value;
};

Q_GLOBAL_STATIC(QExifByteValuePrivate,qExifValuePrivateSharedNull)

/*!
    \class QExifValue
    \inpublicgroup QtBaseModule
    \brief The QExifValue class represents data types found in EXIF image headers.

    Tag values in EXIF headers are stored as arrays of a limited number of data types. QExifValue
    encapsulates a union of these types and provides conversions to and from appropriate Qt types.

    \section1 String encoding

    Most tags with string values in EXIF headers are ASCII encoded and have the Ascii value type,
    but some tags allow other encodings.  In this case the value type is Undefined and the encoding
    of the text is given by the encoding function().

    \section1 Date-time values

    Date-time values in EXIF headers are stored in ASCII encoded strings of the form
    \c {yyyy:MM:dd HH:mm:ss}.  Constructing a QExifValue from a QDateTime will perform this
    conversion and likewise an appropriately formed QExifValue can be converted to a QDateTime
    using the toDateTime() function.

    \sa QExifImageHeader

    \preliminary
*/

/*!
    \enum QExifValue::Type

    Enumerates the possible types of EXIF values.

    \value Byte An unsigned 8 bit integer.
    \value Ascii A null terminated ascii string.
    \value Short An unsigned 16 bit integer.
    \value Long An unsigned 32 bit integer.
    \value Rational Two unsigned 32 bit integers, representing a the numerator and denominator of an unsigned rational number.
    \value Undefined An array of 8 bit integers.
    \value SignedLong A signed 32 bit integer.
    \value SignedRational Two signed 32 bit integers representing the numerator and denominator of a signed rational number.
*/

/*!
      \enum QExifValue::TextEncoding

      Enumerates the encodings of text strings in EXIF values of Undefined type.

      \value NoEncoding An ASCII string of Ascii type.
      \value AsciiEncoding An ASCII string of Undefined type.
      \value JisEncoding A JIS X208-1990 string of Undefined type.
      \value UnicodeEncoding A Unicode string of Undefined type.
      \value UndefinedEncoding An unspecified string encoding of Undefined type.  Assumed to be the local 8-bit encoding.
*/

/*!
    Constructs a null QExifValue.
*/
QExifValue::QExifValue()
    : d( qExifValuePrivateSharedNull() )
{
}

/*!
    Constructs a QExifValue with a \a value of type Byte.
*/
QExifValue::QExifValue( quint8 value )
    : d( new QExifByteValuePrivate( QVector< quint8 >( 1, value ) ) )
{
}

/*!
    Constructs a QExifValue with an array of \a values of type Byte.
*/
QExifValue::QExifValue( const QVector< quint8 > &values )
    : d( new QExifByteValuePrivate( values ) )
{
}

/*!
    Constructs a QExifValue with a \a value of type Ascii or Undefined.

    If the \a encoding is NoEncoding the value will be of type Ascii, otherwise it will be Undefined and the string
    encoded using the given \a encoding.
*/
QExifValue::QExifValue( const QString &value, TextEncoding encoding )
    : d( qExifValuePrivateSharedNull() )
{
    switch( encoding )
    {
    case AsciiEncoding:
        d = new QExifUndefinedValuePrivate( QByteArray::fromRawData( "ASCII\0\0\0", 8 ) + value.toUtf8() );
        break;
    case JisEncoding:
        {
            QTextCodec *codec = QTextCodec::codecForName( "JIS X 0208" );
            if( codec )
                d = new QExifUndefinedValuePrivate( QByteArray::fromRawData( "JIS\0\0\0\0\0", 8 ) + codec->fromUnicode( value ) );
        }
        break;
    case UnicodeEncoding:
        {
            QTextCodec *codec = QTextCodec::codecForName( "UTF-16" );
            if( codec )
                d = new QExifUndefinedValuePrivate( QByteArray::fromRawData( "UNICODE\0", 8 ) + codec->fromUnicode( value ) );
        }
        break;
    case UndefinedEncoding:
        d = new QExifUndefinedValuePrivate( QByteArray::fromRawData( "\0\0\0\0\0\0\0\\0", 8 ) + value.toLocal8Bit() );
        break;
    default:
        d = new QExifAsciiValuePrivate( value );
    }
}

/*!
    Constructs a QExifValue with a \a value of type Short.
*/
QExifValue::QExifValue( quint16 value )
    : d( new QExifShortValuePrivate( QVector< quint16 >( 1, value ) ) )
{
}

/*!
    Constructs a QExifValue with an array of \a values of type Short.
*/
QExifValue::QExifValue( const QVector< quint16 > &values )
    : d( new QExifShortValuePrivate( values ) )
{
}

/*!
    Constructs a QExifValue with a \a value of type Long.
*/
QExifValue::QExifValue( quint32 value )
    : d( new QExifLongValuePrivate( QVector< quint32 >( 1, value ) ) )
{
}

/*!
    Constructs a QExifValue with an array of \a values of type Long.
*/
QExifValue::QExifValue( const QVector< quint32 > &values )
    : d( new QExifLongValuePrivate( values ) )
{
}

/*!
    Constructs a QExifValue with a \a value of type Rational.
*/
QExifValue::QExifValue( const QExifURational &value )
    : d( new QExifRationalValuePrivate( QVector< QExifURational >( 1, value ) ) )
{
}

/*!
    Constructs a QExifValue with an array of \a values of type Rational.
*/
QExifValue::QExifValue( const QVector< QExifURational > &values )
    : d( new QExifRationalValuePrivate( values ) )
{
}

/*!
    Constructs a QExifValue with a \a value of type Undefined.
*/
QExifValue::QExifValue( const QByteArray &value )
    : d( new QExifUndefinedValuePrivate( value ) )
{
}

/*!
    Constructs a QExifValue with a \a value of type SignedLong.
*/
QExifValue::QExifValue( qint32 value )
    : d( new QExifSignedLongValuePrivate( QVector< qint32 >( 1, value ) ) )
{
}

/*!
    Constructs a QExifValue with an array of \a values of type SignedLong.
*/
QExifValue::QExifValue( const QVector< qint32 > &values )
    : d( new QExifSignedLongValuePrivate( values ) )
{
}

/*!
    Constructs a QExifValue with a \a value of type SignedRational.
*/
QExifValue::QExifValue( const QExifSRational &value )
    : d( new QExifSignedRationalValuePrivate( QVector< QExifSRational >( 1, value ) ) )
{
}

/*!
    Constructs a QExifValue with an array of \a values of type SignedRational.
*/
QExifValue::QExifValue( const QVector< QExifSRational > &values )
    : d( new QExifSignedRationalValuePrivate( values ) )
{
}

/*!
    Constructs a QExifValue of type Ascii with an ascii string formatted from a date-time \a value.

    Date-times are stored as strings in the format \c {yyyy:MM:dd HH:mm:ss}.
*/
QExifValue::QExifValue( const QDateTime &value )
    : d( new QExifAsciiValuePrivate( value.toString( QLatin1String( "yyyy:MM:dd HH:mm:ss" ) ) ) )
{
}

/*!
    Constructs a copy of the QExifValue \a other.
*/
QExifValue::QExifValue( const QExifValue &other )
    : d( other.d )
{
}

/*!
    Assigns the value of \a other to a QExifValue.
*/
QExifValue &QExifValue::operator =( const QExifValue &other )
{
    d = other.d;

    return *this;
}

/*!
    Destroys a QExifValue.
*/
QExifValue::~QExifValue()
{
}

/*!
    Compares a QExifValue to \a other.  Returns true if they are the same value and false otherwise.
*/
bool QExifValue::operator ==( const QExifValue &other ) const
{
    return d == other.d;
}

/*!
    Returns true if a QExifValue has a null value and false otherwise.
*/
bool QExifValue::isNull() const
{
    return d == qExifValuePrivateSharedNull();
}

/*!
    Returns the type of a QExifValue.
*/
int QExifValue::type() const
{
    return d->type;
}

/*!
    Returns the number of elements in a QExifValue.  For ascii strings this is the length of the string
    including the terminating null.
*/
int QExifValue::count() const
{
    return d->count;
}

/*!
      Returns the encoding of strings stored in Undefined values.
*/
QExifValue::TextEncoding QExifValue::encoding() const
{
    if( d->type == Undefined && d->count > 8 )
    {
        QByteArray value = static_cast< const QExifUndefinedValuePrivate * >( d.constData() )->value;

        if( value.startsWith( QByteArray::fromRawData( "ASCII\0\0\0", 8 ) ) )
            return AsciiEncoding;
        else if( value.startsWith( QByteArray::fromRawData( "JIS\0\0\0\0\0", 8 ) ) )
            return JisEncoding;
        else if( value.startsWith( QByteArray::fromRawData( "UNICODE\0", 8 ) ) )
            return UnicodeEncoding;
        else if( value.startsWith( QByteArray::fromRawData( "\0\0\0\0\0\0\0\0", 8 ) ) )
            return UndefinedEncoding;
    }
    return NoEncoding;
}

/*!
    Returns the value of a single element QExifValue of type Byte.
*/
quint8 QExifValue::toByte() const
{
    return d->type == Byte && d->count == 1
        ? static_cast< const QExifByteValuePrivate * >( d.constData() )->value.at( 0 )
        : 0;
}

/*!
    Returns the value of a multiple element QExifValue of type Byte.
*/
QVector< quint8 > QExifValue::toByteVector() const
{
    return d->type == Byte
        ? static_cast< const QExifByteValuePrivate * >( d.constData() )->value
        : QVector< quint8 >();
}

/*!
    Returns the value of a QExifValue of type Ascii.
*/
QString QExifValue::toString() const
{
    switch( d->type )
    {
    case Ascii:
        return static_cast< const QExifAsciiValuePrivate * >( d.constData() )->value;
    case Undefined:
        {
            QByteArray string = static_cast< const QExifUndefinedValuePrivate * >( d.constData() )->value.mid( 8 );

            switch( encoding() )
            {
            case AsciiEncoding:
                return QString::fromUtf8( string.constData(), string.length() );
            case JisEncoding:
                {
                    QTextCodec *codec = QTextCodec::codecForName( "JIS X 0208" );
                    if( codec )
                        return codec->toUnicode( string );
                }
            break;
            case UnicodeEncoding:
                {
                    QTextCodec *codec = QTextCodec::codecForName( "UTF-16" );
                    if( codec )
                        return codec->toUnicode( string );
                }
            case UndefinedEncoding:
                return QString::fromLocal8Bit( string.constData(), string.length() );
            default:
                break;
            }
        }
    default:
        return QString();
    }
}

/*!
    Returns the value of a single element QExifValue of type Byte or Short.
*/
quint16 QExifValue::toShort() const
{
    if( d->count == 1 )
    {
        switch( d->type )
        {
        case Byte:
            return static_cast< const QExifByteValuePrivate * >( d.constData() )->value.at( 0 );
        case Short:
            return static_cast< const QExifShortValuePrivate * >( d.constData() )->value.at( 0 );
        }
    }
    return 0;
}

/*!
    Returns the value of a single element QExifValue of type Short.
*/
QVector< quint16 > QExifValue::toShortVector() const
{
    return d->type == Short
        ? static_cast< const QExifShortValuePrivate * >( d.constData() )->value
        : QVector< quint16 >();
}

/*!
    Returns the value of a single element QExifValue of type Byte, Short, Long, or SignedLong.
*/
quint32 QExifValue::toLong() const
{
    if( d->count == 1 )
    {
        switch( d->type )
        {
        case Byte:
            return static_cast< const QExifByteValuePrivate * >( d.constData() )->value.at( 0 );
        case Short:
            return static_cast< const QExifShortValuePrivate * >( d.constData() )->value.at( 0 );
        case Long:
            return static_cast< const QExifLongValuePrivate * >( d.constData() )->value.at( 0 );
        case SignedLong:
            return static_cast< const QExifSignedLongValuePrivate * >( d.constData() )->value.at( 0 );
        }
    }
    return 0;
}

/*!
    Returns the value of a multiple element QExifValue of type Long.
*/
QVector< quint32 > QExifValue::toLongVector() const
{
    return d->type == Long
        ? static_cast< const QExifLongValuePrivate * >( d.constData() )->value
        : QVector< quint32 >();
}

/*!
    Returns the value of a multiple element QExifValue of type Rational.
*/
QExifURational QExifValue::toRational() const
{
    return d->type == Rational && d->count == 1
        ? static_cast< const QExifRationalValuePrivate * >( d.constData() )->value.at( 0 )
        : QExifURational();
}

/*!
    Returns the value of a multiple element QExifValue of type Rational.
*/
QVector< QExifURational > QExifValue::toRationalVector() const
{
    return d->type == Rational
        ? static_cast< const QExifRationalValuePrivate * >( d.constData() )->value
        : QVector< QExifURational >();
}

/*!
    Returns the value of a QExifValue of type Undefined.
*/
QByteArray QExifValue::toByteArray() const
{

}

/*!
    Returns the value of a single element QExifValue of type Byte, Short, Long, or SignedLong.
*/
qint32 QExifValue::toSignedLong() const
{
    if( d->count == 1 )
    {
        switch( d->type )
        {
        case Byte:
            return static_cast< const QExifByteValuePrivate * >( d.constData() )->value.at( 0 );
        case Short:
            return static_cast< const QExifShortValuePrivate * >( d.constData() )->value.at( 0 );
        case Long:
            return static_cast< const QExifLongValuePrivate * >( d.constData() )->value.at( 0 );
        case SignedLong:
            return static_cast< const QExifSignedLongValuePrivate * >( d.constData() )->value.at( 0 );
        }
    }
    return 0;
}

/*!
    Returns the value of a multiple element QExifValue of type SignedLong.
*/
QVector< qint32 > QExifValue::toSignedLongVector() const
{
    return d->type == SignedLong
        ? static_cast< const QExifSignedLongValuePrivate * >( d.constData() )->value
        : QVector< qint32 >();
}

/*!
    Returns the value of a single element QExifValue of type SignedRational.
*/
QExifSRational QExifValue::toSignedRational() const
{
    return d->type == SignedRational && d->count == 1
        ? static_cast< const QExifSignedRationalValuePrivate * >( d.constData() )->value.at( 0 )
        : QExifSRational();
}

/*!
    Returns the value of a multiple element QExifValue of type SignedRational.
*/
QVector< QExifSRational > QExifValue::toSignedRationalVector() const
{
    return d->type == SignedRational
        ? static_cast< const QExifSignedRationalValuePrivate * >( d.constData() )->value
        : QVector< QExifSRational >();
}

/*!
    Returns the value of QExifValue storing a date-time.

    Date-times are stored as ascii strings in the format \c {yyyy:MM:dd HH:mm:ss}.
*/
QDateTime QExifValue::toDateTime() const
{
    return d->type == Ascii && d->count == 20
        ? QDateTime::fromString( static_cast< const QExifAsciiValuePrivate * >( d.constData() )->value, QLatin1String( "yyyy:MM:dd HH:mm:ss" ) )
        : QDateTime();
}

class QExifImageHeaderPrivate
{
public:
    QSysInfo::Endian byteOrder;
    mutable qint64 size;
    QMap<QExifImageHeader::ImageTag, QExifValue > imageIfdValues;
    QMap<QExifImageHeader::ExifExtendedTag, QExifValue > exifIfdValues;
    QMap<QExifImageHeader::GpsTag, QExifValue > gpsIfdValues;

    QSize thumbnailSize;
    QByteArray thumbnailData;
    QExifValue thumbnailXResolution;
    QExifValue thumbnailYResolution;
    QExifValue thumbnailResolutionUnit;
    QExifValue thumbnailOrientation;
};

/*!
    \class QExifImageHeader
    \inpublicgroup QtBaseModule
    \brief The QExifImageHeader class provides functionality for reading and writing EXIF image headers.

    EXIF headers are a collection of properties that describe the image they're embedded in.
    Each property is identified by a tag of which there are three kinds.  \l {ImageTag}{Image tags}
    which mostly describe the format (dimensions, resolution, orientation) but also include some
    descriptive information (description, camera make and model, artist).  \l {ExifExtendedTag}
    {EXIF extended tags} which elaborate on some of the image tags and record the camera settings at
    time of capture among other things.  Finally there are \l {GpsTag}{GPS tags} which record the
    location the image was captured.

    EXIF tags are typically found in JPEG images but may be found in other image formats.  To read
    headers from a JPEG image QExifImageHeader provides the loadFromJpeg() function, and the
    complementary saveToJpeg() function for writing.  To allow reading and writing arbitrary
    formats QExifImageHeader provides the read() and write() functions which work with just the
    EXIF header data itself.

    \preliminary
*/

/*!
    \enum QExifImageHeader::ImageTag
    Enumerates the TIFF image tag IDs defined in the EXIF specification.

    \value ImageWidth
    \value ImageLength
    \value BitsPerSample
    \value Compression
    \value PhotometricInterpretation
    \value Orientation
    \value SamplesPerPixel
    \value PlanarConfiguration
    \value YCbCrSubSampling
    \value XResolution
    \value YResolution
    \value ResolutionUnit
    \value StripOffsets
    \value RowsPerStrip
    \value StripByteCounts
    \value TransferFunction
    \value WhitePoint
    \value PrimaryChromaciticies
    \value YCbCrCoefficients
    \value ReferenceBlackWhite
    \value DateTime
    \value ImageDescription
    \value Make
    \value Model
    \value Software
    \value Artist
    \value Copyright
*/

/*!
    \enum QExifImageHeader::ExifExtendedTag
    Enumerates the extended EXIF tag IDs defined in the EXIF specification.

    \value ExifVersion
    \value FlashPixVersion
    \value ColorSpace
    \value ComponentsConfiguration
    \value CompressedBitsPerPixel
    \value PixelXDimension
    \value PixelYDimension
    \value MakerNote
    \value UserComment
    \value RelatedSoundFile
    \value DateTimeOriginal
    \value DateTimeDigitized
    \value SubSecTime
    \value SubSecTimeOriginal
    \value SubSecTimeDigitized
    \value ImageUniqueId
    \value ExposureTime
    \value FNumber
    \value ExposureProgram
    \value SpectralSensitivity
    \value ISOSpeedRatings
    \value Oecf
    \value ShutterSpeedValue
    \value ApertureValue
    \value BrightnessValue
    \value ExposureBiasValue
    \value MaxApertureValue
    \value SubjectDistance
    \value MeteringMode
    \value LightSource
    \value Flash
    \value FocalLength
    \value SubjectArea
    \value FlashEnergy
    \value SpatialFrequencyResponse
    \value FocalPlaneXResolution
    \value FocalPlaneYResolution
    \value FocalPlaneResolutionUnit
    \value SubjectLocation
    \value ExposureIndex
    \value SensingMethod
    \value FileSource
    \value SceneType
    \value CfaPattern
    \value CustomRendered
    \value ExposureMode
    \value WhiteBalance
    \value DigitalZoomRatio
    \value FocalLengthIn35mmFilm
    \value SceneCaptureType
    \value GainControl
    \value Contrast
    \value Saturation
    \value Sharpness
    \value DeviceSettingDescription
    \value SubjectDistanceRange
*/

/*!
    \enum QExifImageHeader::GpsTag
    Enumerates the GPS tag IDs from the EXIF specification.

    \value GpsVersionId
    \value GpsLatitudeRef
    \value GpsLatitude
    \value GpsLongitudeRef
    \value GpsLongitude
    \value GpsAltitudeRef
    \value GpsAltitude
    \value GpsTimeStamp
    \value GpsSatellites
    \value GpsStatus
    \value GpsMeasureMode
    \value GpsDop
    \value GpsSpeedRef
    \value GpsSpeed
    \value GpsTrackRef
    \value GpsTrack
    \value GpsImageDirectionRef
    \value GpsImageDirection
    \value GpsMapDatum
    \value GpsDestLatitudeRef
    \value GpsDestLatitude
    \value GpsDestLongitudeRef
    \value GpsDestLongitude
    \value GpsDestBearingRef
    \value GpsDestBearing
    \value GpsDestDistanceRef
    \value GpsDestDistance
    \value GpsProcessingMethod
    \value GpsAreaInformation
    \value GpsDateStamp
    \value GpsDifferential
*/

/*!
    Constructs a new EXIF image data editor.
*/
QExifImageHeader::QExifImageHeader()
    : d( new QExifImageHeaderPrivate )
{
    d->byteOrder = QSysInfo::ByteOrder;
    d->size = -1;
}

/*!
    Constructs a new EXIF image data editor and reads the meta-data from a JPEG image with the given \a fileName.
*/
QExifImageHeader::QExifImageHeader(const QString &fileName)
    : d(new QExifImageHeaderPrivate)
{
    d->byteOrder = QSysInfo::ByteOrder;
    d->size = -1;

    loadFromJpeg(fileName);
}

/*!
    Destroys an EXIF image data editor.
*/
QExifImageHeader::~QExifImageHeader()
{
    clear();

    delete d;
}

/*!
    Reads meta-data from a JPEG image with the given \a fileName.

    Returns true if the data was successfully parsed and false otherwise.
*/
bool QExifImageHeader::loadFromJpeg(const QString &fileName)
{
    QFile file(fileName);

    if(file.open(QIODevice::ReadOnly))
        return loadFromJpeg(&file);
    else
        return false;
}

/*!
    Reads meta-data from an I/O \a device containing a JPEG image.

    Returns true if the data was successfully parsed and false otherwise.
*/
bool QExifImageHeader::loadFromJpeg(QIODevice *device)
{
    clear();

    QByteArray exifData = extractExif(device);

    if (!exifData.isEmpty()) {
        QBuffer buffer(&exifData);

        return buffer.open(QIODevice::ReadOnly) && read(&buffer);
    }

    return false;
}

/*!
    Saves meta-data to a JPEG image with the given \a fileName.

    Returns true if the data was successfully written.
*/
bool QExifImageHeader::saveToJpeg(const QString &fileName) const
{
    QFile file(fileName);

    if (file.open(QIODevice::ReadWrite))
        return saveToJpeg(&file);
    else
        return false;
}

/*!
    Save meta-data to the given I/O \a device.

    The device must be non-sequential and already contain a valid JPEG image.

    Returns true if the data was successfully written.
*/
bool QExifImageHeader::saveToJpeg(QIODevice *device) const {
    if(device->isSequential()) {
        return false;
	}

    QByteArray exif;

    {
        QBuffer buffer(&exif);

        if(!buffer.open(QIODevice::WriteOnly)) {
            return false;
		}

        write(&buffer);
        buffer.close();
        exif = QByteArray::fromRawData( "Exif\0\0", 6 ) + exif;
    }

    QDataStream stream(device);
	stream.setByteOrder(QDataStream::BigEndian);

	if (device->read(2) != "\xFF\xD8") {   // Not a valid JPEG image.
		return false;
	}

    quint16 segmentId;
    quint16 segmentLength;

    stream >> segmentId;
    stream >> segmentLength;

    if (segmentId == 0xFFE0) {
        QByteArray jfif = device->read(segmentLength - 2);

		if (!jfif.startsWith("JFIF")) {
			return false;
		}

        stream >> segmentId;
        stream >> segmentLength;

        if(segmentId == 0xFFE1) {
            QByteArray oldExif = device->read( segmentLength - 2 );

            if( !oldExif.startsWith( "Exif" ) )
                return false;

            int dSize = oldExif.size() - exif.size();

            if( dSize > 0 )
                exif += QByteArray( dSize, '\0' );

            QByteArray remainder = device->readAll();

            device->seek( 0 );

            stream << quint16( 0xFFD8 ); // SOI
            stream << quint16( 0xFFE0 ); // APP0
            stream << quint16( jfif.size() + 2 );
            device->write( jfif );
            stream << quint16( 0xFFE1 ); //APP1
            stream << quint16( exif.size() + 2 );
            device->write( exif );
            device->write( remainder );
        }
        else
        {
            QByteArray remainder = device->readAll();

            device->seek( 0 );

            stream << quint16( 0xFFD8 ); // SOI
            stream << quint16( 0xFFE0 ); // APP0
            stream << quint16( jfif.size() + 2 );
            device->write(jfif);
            stream << quint16( 0xFFE1 ); //APP1
            stream << quint16( exif.size() + 2 );
            device->write(exif);
            stream << segmentId;
            stream << segmentLength;
            device->write(remainder);
        }
    }
    else if( segmentId == 0xFFE1 )
    {
        QByteArray oldExif = device->read( segmentLength - 2 );

        if( !oldExif.startsWith( "Exif" ) )
            return false;

        int dSize = oldExif.size() - exif.size();

        if( dSize > 0 )
            exif += QByteArray( dSize, '\0' );

        QByteArray remainder = device->readAll();

        device->seek( 0 );

        stream << quint16( 0xFFD8 ); // SOI
        stream << quint16( 0xFFE1 ); //APP1
        stream << quint16( exif.size() + 2 );
        device->write( exif );
        device->write( remainder );
    }
    else
    {
        QByteArray remainder = device->readAll();

        device->seek( 0 );

        stream << quint16( 0xFFD8 ); // SOI
        stream << quint16( 0xFFE1 ); //APP1
        stream << quint16( exif.size() + 2 );
        device->write( exif );
        stream << segmentId;
        stream << segmentLength;
        device->write( remainder );
    }

    return true;
}

/*!
    Returns the byte order of EXIF file.
*/
QSysInfo::Endian QExifImageHeader::byteOrder() const
{
    return d->byteOrder;
}

quint32 QExifImageHeader::sizeOfData(const QExifValue &value) const
{
    switch (value.type()) {
    case QExifValue::Byte:
    case QExifValue::Undefined:
    case QExifValue::Ascii:
        return value.count() > 4
                ? value.count()
                : 0;
    case QExifValue::Short:
        return value.count() > 2
                ? value.count() * sizeof(quint16)
                : 0;
    case QExifValue::Long:
    case QExifValue::SignedLong:
        return value.count() > 1
                ? value.count() * sizeof(quint32)
                : 0;
    case QExifValue::Rational:
    case QExifValue::SignedRational:
        return value.count() > 0
                ? value.count() * sizeof(quint32) * 2
                : 0;
    default:
        return 0;
    }
}

quint32 QExifImageHeader::sizeOf(const QExifValue &value) const {
	switch (value.type()) {
	case QExifValue::Byte:
	case QExifValue::Undefined:
	case QExifValue::Ascii:
		return value.count() > 4
			? 12 + value.count()
			: 12;
	case QExifValue::Short:
		return value.count() > 2
			? 12 + value.count() * sizeof(quint16)
			: 12;
	case QExifValue::Long:
	case QExifValue::SignedLong:
		return value.count() > 1
			? 12 + value.count() * sizeof(quint32)
			: 12;
	case QExifValue::Rational:
	case QExifValue::SignedRational:
		return value.count() > 0
			? 12 + value.count() * sizeof(quint32) * 2
			: 12;
	default:
		return 0;
	}
}

template <typename T>
quint32 QExifImageHeader::calculateSize(const QMap<T, QExifValue> &values) const
{
    quint32 size = sizeof(quint16);

	foreach(const QExifValue &value, values) {
		size += sizeOf(value);
	}

	size += sizeof(quint32); // The Link-Offset

    return size;
}

template <typename T>
quint32 QExifImageHeader::calculateDataSize(const QMap<T, QExifValue> &values) const {
	quint32 size = 0;

	foreach(const QExifValue &value, values) {
		size += sizeOfData(value);
	}

	return size;
}

/*!
    Returns the size of EXIF data in bytes.
*/
qint64 QExifImageHeader::size() const
{
    if (d->size == -1) {
        d->size
                = 2                                    // Byte Order
                + 2                                    // Marker
                + 4                                    // Image Ifd offset
				+ 2									   // Number of Directory Entries in IFD0
                + 12                                   // ExifIfdPointer Ifd
                + 4                                    // Thumbnail Ifd offset
                + calculateSize(d->imageIfdValues)     // Image headers and values.
                + calculateSize(d->exifIfdValues);     // Exif headers and values.

        if (!d->gpsIfdValues.isEmpty()) {
            d->size
                    += 12                              // GpsInfoIfdPointer Ifd
                    +  calculateSize(d->gpsIfdValues); // Gps headers and values.
        }

        if (!d->thumbnailData.isEmpty()) {
            d->size
                    += 2                               // Thumbnail Ifd count
                    +  12                              // Compression Ifd
                    +  20                              // XResolution Ifd
                    +  20                              // YResolution Ifd
                    +  12                              // ResolutionUnit Ifd
                    +  12                              // JpegInterchangeFormat Ifd
                    +  12                              // JpegInterchangeFormatLength Ifd
                    +  d->thumbnailData.size();        // Thumbnail data size.
        }
    }

    return d->size;
}

/*!
    Clears all image meta-data.
*/
void QExifImageHeader::clear()
{
    d->imageIfdValues.clear();
    d->exifIfdValues.clear();
    d->gpsIfdValues.clear();
    d->thumbnailData.clear();

    d->size = -1;
}

/*!
    Returns a list of all image tags in an EXIF header.
*/
QList<QExifImageHeader::ImageTag> QExifImageHeader::imageTags() const
{
    return d->imageIfdValues.keys();
}

/*!
    Returns a list of all extended EXIF tags in a header.
*/
QList<QExifImageHeader::ExifExtendedTag> QExifImageHeader::extendedTags() const
{
    return d->exifIfdValues.keys();
}

/*!
    Returns a list of all GPS tags in an EXIF header.
*/
QList<QExifImageHeader::GpsTag> QExifImageHeader::gpsTags() const
{
    return d->gpsIfdValues.keys();
}

/*!
    Returns true if an EXIf header contains a value for an image \a tag and false otherwise.
*/
bool QExifImageHeader::contains(ImageTag tag) const
{
    return d->imageIfdValues.contains(tag);
}

/*!
    Returns true if a header contains a a value for an extended EXIF \a tag and false otherwise.
*/
bool QExifImageHeader::contains(ExifExtendedTag tag) const
{
    return d->exifIfdValues.contains(tag);
}

/*!
    Returns true if an EXIf header contains a value for a GPS \a tag and false otherwise.
*/
bool QExifImageHeader::contains(GpsTag tag) const
{
    return d->gpsIfdValues.contains(tag);
}

/*!
    Removes the value for an image \a tag.
*/
void QExifImageHeader::remove(ImageTag tag)
{
    d->imageIfdValues.remove(tag);

    d->size = -1;
}

/*!
    Removes the value for an extended EXIF \a tag.
*/
void QExifImageHeader::remove(ExifExtendedTag tag)
{
    d->exifIfdValues.remove(tag);

    d->size = -1;
}

/*!
    Removes the value for a GPS \a tag.
*/
void QExifImageHeader::remove(GpsTag tag)
{
    d->gpsIfdValues.remove(tag);

    d->size = -1;
}

/*!
    Returns the value for an image \a tag.
*/
QExifValue QExifImageHeader::value(ImageTag tag) const
{
    return d->imageIfdValues.value(tag);
}

/*!
    Returns the value for an extended EXIF \a tag.
*/
QExifValue QExifImageHeader::value(ExifExtendedTag tag) const
{
    return d->exifIfdValues.value(tag);
}

/*!
    Returns the value for a GPS tag.
*/
QExifValue QExifImageHeader::value(GpsTag tag) const
{
    return d->gpsIfdValues.value(tag);
}

/*!
    Sets the \a value for an image \a tag.
*/
void QExifImageHeader::setValue(ImageTag tag, const QExifValue &value)
{
    d->imageIfdValues[tag] = value;

    d->size = -1;
}

/*!
    Sets the \a value for an extended EXIF \a tag.
*/
void QExifImageHeader::setValue(ExifExtendedTag tag, const QExifValue &value)
{
    d->exifIfdValues[tag] = value;

    d->size = -1;
}

/*!
    Sets the \a value for an GPS \a tag.
*/
void QExifImageHeader::setValue(GpsTag tag, const QExifValue &value)
{
    d->gpsIfdValues[tag] = value;

    d->size = -1;
}

/*!
    Returns the image thumbnail.
*/
QImage QExifImageHeader::thumbnail() const
{
    QImage image;

    image.loadFromData(d->thumbnailData, "JPG");

    if (!d->thumbnailOrientation.isNull()) {
        switch (d->thumbnailOrientation.toShort()) {
        case 1:
            return image;
        case 2:
            return image.transformed(QTransform().rotate(180, Qt::YAxis));
        case 3:
            return image.transformed(QTransform().rotate(180, Qt::ZAxis));
        case 4:
            return image.transformed(QTransform().rotate(180, Qt::XAxis));
        case 5:
            return image.transformed(QTransform().rotate(180, Qt::YAxis).rotate(90, Qt::ZAxis));
        case 6:
            return image.transformed(QTransform().rotate(90, Qt::ZAxis));
        case 7:
            return image.transformed(QTransform().rotate(180, Qt::XAxis).rotate(90, Qt::ZAxis));
        case 8:
            return image.transformed(QTransform().rotate(270, Qt::ZAxis));
        }
    }

    return image;
}

/*!
    Sets the image \a thumbnail.
*/
void QExifImageHeader::setThumbnail( const QImage &thumbnail )
{
    if (!thumbnail.isNull()) {
        QBuffer buffer;

        if (buffer.open(QIODevice::WriteOnly) && thumbnail.save(&buffer, "JPG")) {
            buffer.close();

            d->thumbnailSize = thumbnail.size();
            d->thumbnailData = buffer.data();
            d->thumbnailOrientation = QExifValue();
        }
    } else {
        d->thumbnailSize = QSize();
        d->thumbnailData = QByteArray();
    }

    d->size = -1;
}

QByteArray QExifImageHeader::extractExif(QIODevice *device) const {
    QDataStream stream( device );

    stream.setByteOrder( QDataStream::BigEndian );

	if (device->read(2) != "\xFF\xD8") {
		return QByteArray();
	}

	while (true) {
		if (stream.atEnd()) {
			return QByteArray();
		}

		quint8 markerA;
		quint8 markerB;

		stream >> markerA;
		if ((markerA != 0xFF) || stream.atEnd()) {
			return QByteArray();
		} else {
			stream >> markerB;
			if (markerB == 0xD9) { // End of Image
				return QByteArray();
			} else if (markerB == 0xDD) { // Define Restart Interval, 4Byte payload
				device->read(4);
				continue;
			} else if (markerB == 0xDA) { // Start of Scan, contains no usable length information, can not be skipped without parsing the data
				return QByteArray();
			} else if ((markerB >= 0xD0) && (markerB <= 0xD7)) { // Restart Marker, no Payload
				continue;
			} else if (markerB == 0xE1) { // EXIF Marker
				break;
			} else {
				// This should be a payload-frame
				quint16 length;
				stream >> length;
				if (!device->seek(device->pos() + length - 2)) {
					return QByteArray();
				}
			}
		}
	}

	quint16 length;
    stream >> length;

	if (device->read(4) != "Exif") {
		return QByteArray();
	}

    device->read(2);
    return device->read(length - 8);
}

QList<ExifIfdHeader> QExifImageHeader::readIfdHeaders(QDataStream &stream) const {
    QList<ExifIfdHeader> headers;

    quint16 count;

    stream >> count;

    for (quint16 i = 0; i < count; i++) {
        ExifIfdHeader header;

        stream >> header;

        headers.append(header);
    }

    return headers;
}

QExifValue QExifImageHeader::readIfdValue(QDataStream &stream, int startPos, const ExifIfdHeader &header) const
{
    switch (header.type) {
    case QExifValue::Byte:
        {
            QVector<quint8> value( header.count );

            if (header.count > 4) {
                stream.device()->seek(startPos + header.offset);

                for (quint32 i = 0; i < header.count; i++)
                    stream >> value[i];
            } else {
                for( quint32 i = 0; i < header.count; i++ )
                    value[ i ] = header.offsetBytes[ i ];

            }
            return QExifValue(value);
        }
    case QExifValue::Undefined:
        if (header.count > 4) {
            stream.device()->seek(startPos + header.offset);

            return QExifValue(stream.device()->read(header.count));
        } else {
            return QExifValue(QByteArray::fromRawData(header.offsetAscii, header.count));
        }
    case QExifValue::Ascii:
        if (header.count > 4) {
            stream.device()->seek(startPos + header.offset);

            QByteArray ascii = stream.device()->read(header.count);

            return QExifValue(QString::fromUtf8(ascii.constData(), ascii.size() - 1));
        } else {
            return QExifValue(QString::fromUtf8(header.offsetAscii, header.count - 1));
        }
    case QExifValue::Short:
        {
            QVector<quint16> value(header.count);

            if (header.count > 2) {
                stream.device()->seek(startPos + header.offset);

                for (quint32 i = 0; i < header.count; i++)
                    stream >> value[i];
            } else {
                for (quint32 i = 0; i < header.count; i++)
                    value[i] = header.offsetShorts[i];

            }
            return QExifValue(value);
        }
    case QExifValue::Long:
        {
            QVector<quint32> value(header.count);

            if (header.count > 1) {
                stream.device()->seek(startPos + header.offset);

                for (quint32 i = 0; i < header.count; i++)
                    stream >> value[i];
            } else if(header.count == 1) {
                value[0] = header.offset;
            }
            return QExifValue(value);
        }
    case QExifValue::SignedLong:
        {
            QVector<qint32> value(header.count);

            if (header.count > 1) {
                stream.device()->seek(startPos + header.offset);

                for (quint32 i = 0; i < header.count; i++)
                    stream >> value[i];
            } else if (header.count == 1) {
                value[0] = header.offset;
            }
            return QExifValue(value);
        }
        break;
    case QExifValue::Rational:
        {
            QVector<QExifURational> value(header.count);

            stream.device()->seek(startPos + header.offset);

            for (quint32 i = 0; i < header.count; i++)
                stream >> value[i];

            return QExifValue(value);
        }
    case QExifValue::SignedRational:
        {
            QVector<QExifSRational> value(header.count);

            stream.device()->seek(startPos + header.offset);

            for(quint32 i = 0; i < header.count; i++)
                stream >> value[i];

            return QExifValue(value);
        }
    default:
        qWarning() << "Invalid Ifd Type" << header.type;

        return QExifValue();
    }
}

template <typename T> QMap<T, QExifValue> QExifImageHeader::readIfdValues(
        QDataStream &stream, int startPos, const QList<ExifIfdHeader> &headers) const
{
    QMap<T, QExifValue> values;

    // This needs to be non-const so it works with gcc3
    QList<ExifIfdHeader> headers_ = headers;
    foreach (const ExifIfdHeader &header, headers_)
        values[T(header.tag)] = readIfdValue(stream, startPos, header);

    return values;
}

template <typename T>
QMap<T, QExifValue> QExifImageHeader::readIfdValues(
        QDataStream &stream, int startPos, const QExifValue &pointer) const
{
    if (pointer.type() == QExifValue::Long && pointer.count() == 1) {
        stream.device()->seek(startPos + pointer.toLong());

        QList<ExifIfdHeader> headers = readIfdHeaders(stream);

        return readIfdValues<T>(stream, startPos, headers);
    } else {
        return QMap<T, QExifValue >();
    }
}

/*!
    Reads the contents of an EXIF header from an I/O \a device.

    Returns true if the header was read and false otherwise.

    \sa loadFromJpeg(), write()
*/
bool QExifImageHeader::read(QIODevice *device)
{
    clear();

    int startPos = device->pos();

    QDataStream stream(device);

    QByteArray byteOrder = device->read(2);

    if (byteOrder == "II") {
        d->byteOrder = QSysInfo::LittleEndian;

        stream.setByteOrder( QDataStream::LittleEndian );
    } else if (byteOrder == "MM") {
        d->byteOrder = QSysInfo::BigEndian;

        stream.setByteOrder( QDataStream::BigEndian );
    } else {
        return false;
    }

    quint16 id;
    quint32 offset;

    stream >> id;
    stream >> offset;

    if (id != 0x002A)
        return false;

    device->seek(startPos + offset);

    QList<ExifIfdHeader> headers = readIfdHeaders(stream);

    stream >> offset;

    d->imageIfdValues = readIfdValues<ImageTag>(stream, startPos, headers);

    QExifValue exifIfdPointer = d->imageIfdValues.take(ImageTag(ExifIfdPointer));
    QExifValue gpsIfdPointer = d->imageIfdValues.take(ImageTag(GpsInfoIfdPointer));

    d->exifIfdValues = readIfdValues<ExifExtendedTag>(stream, startPos, exifIfdPointer);
    d->gpsIfdValues = readIfdValues<GpsTag>(stream, startPos, gpsIfdPointer);

    d->exifIfdValues.remove(ExifExtendedTag(InteroperabilityIfdPointer));

    if (offset) {
        device->seek(startPos + offset);

        QMap<quint16, QExifValue> thumbnailIfdValues = readIfdValues<quint16>(
                stream, startPos, readIfdHeaders( stream));

        QExifValue jpegOffset = thumbnailIfdValues.value(JpegInterchangeFormat);
        QExifValue jpegLength = thumbnailIfdValues.value(JpegInterchangeFormatLength);

        if (jpegOffset.type() == QExifValue::Long && jpegOffset.count() == 1
            && jpegLength.type() == QExifValue::Long && jpegLength.count() == 1)
        {
            device->seek(startPos + jpegOffset.toLong());

            d->thumbnailData = device->read( jpegLength.toLong() );

            d->thumbnailXResolution = thumbnailIfdValues.value(XResolution);
            d->thumbnailYResolution = thumbnailIfdValues.value(YResolution);
            d->thumbnailResolutionUnit = thumbnailIfdValues.value(ResolutionUnit);
            d->thumbnailOrientation = thumbnailIfdValues.value(Orientation);
        }
    }
    return true;
}

quint32 QExifImageHeader::writeExifHeader(QDataStream &stream, quint16 tag, const QExifValue &value, quint32 offset) const
{
    stream << tag;
    stream << quint16(value.type());
    stream << quint32(value.count());

    switch (value.type()) {
    case QExifValue::Byte:
        if (value.count() <= 4) {
            foreach (quint8 byte, value.toByteVector())
                stream << byte;
            for (int j = value.count(); j < 4; j++)
                stream << quint8(0);
        } else {
            stream << offset;

            offset += value.count();
        }
        break;
    case QExifValue::Undefined:
        if (value.count() <= 4) {
            stream.device()->write(value.toByteArray());

            if ( value.count() < 4)
                stream.writeRawData("\0\0\0\0", 4 -  value.count());
        } else {
            stream << offset;

            offset += value.count();
        }
        break;
    case QExifValue::Ascii:
        if (value.count() <= 4) {
            QByteArray bytes = value.toByteArray();

            stream.writeRawData(bytes.constData(), value.count());
            if (value.count() < 4)
                stream.writeRawData("\0\0\0\0", 4 - value.count());
        } else {
            stream << offset;

            offset += value.count();
        }
        break;
    case QExifValue::Short:
        if (value.count() <= 2) {
            foreach (quint16 shrt, value.toShortVector())
                stream << shrt;
            for (int j = value.count(); j < 2; j++)
                stream << quint16(0);
        } else {
            stream << offset;

            offset += value.count() * sizeof(quint16);
        }
        break;
    case QExifValue::Long:
        if(value.count() == 0) {
            stream << quint32(0);
        } else if(value.count() == 1) {
            stream << value.toLong();
        } else {
            stream << offset;

            offset += value.count()  * sizeof(quint32);
        }
        break;
    case QExifValue::SignedLong:
        if (value.count() == 0) {
            stream << quint32( 0 );
        } else if (value.count() == 1) {
            stream << value.toSignedLong();
        } else {
            stream << offset;

            offset += value.count()  * sizeof(qint32);
        }
        break;
    case QExifValue::Rational:
        if(value.count() == 0) {
            stream << quint32(0);
        } else {
            stream << offset;

            offset += value.count() * sizeof(quint32) * 2;
        }
        break;
    case QExifValue::SignedRational:
        if (value.count() == 0) {
            stream << quint32(0);
        } else {
            stream << offset;

            offset += value.count() * sizeof(qint32) * 2;
        }
        break;
    default:
        qWarning() << "Invalid Ifd Type" << value.type();
        stream << quint32(0);
    }

    return offset;
}

quint32 QExifImageHeader::writeExifValue(QDataStream &stream, const QExifValue &value) const
{
	quint32 bytesWritten = 0;

    switch (value.type()) {
    case QExifValue::Byte:
		if (value.count() > 4) {
			foreach(quint8 byte, value.toByteVector()) {
				stream << byte;
				bytesWritten++;
			}
			Q_ASSERT(bytesWritten == static_cast<quint32>(value.count()));
		}
        break;
    case QExifValue::Undefined:
		if (value.count() > 4) {
			bytesWritten = stream.device()->write(value.toByteArray());
			Q_ASSERT(bytesWritten == static_cast<quint32>(value.count()));
		}
        break;
    case QExifValue::Ascii:
        if (value.count() > 4) {
            QByteArray bytes = value.toByteArray();

            bytesWritten = stream.writeRawData(bytes.constData(), bytes.size() + 1);
			Q_ASSERT(bytesWritten == static_cast<quint32>(value.count()));
        }
        break;
    case QExifValue::Short:
		if (value.count() > 2) {
			foreach(quint16 shrt, value.toShortVector()) {
				stream << shrt;
				bytesWritten += sizeof(quint16);
			}
			Q_ASSERT(bytesWritten == static_cast<quint32>((value.count() * sizeof(quint16))));
		}
        break;
    case QExifValue::Long:
		if (value.count() > 1) {
			foreach(quint32 lng, value.toLongVector()) {
				stream << lng;
				bytesWritten += sizeof(quint32);
			}
			Q_ASSERT(bytesWritten == static_cast<quint32>((value.count() * sizeof(quint32))));
		}
        break;
    case QExifValue::SignedLong:
		if (value.count() > 1) {
			foreach(qint32 lng, value.toSignedLongVector()) {
				stream << lng;
				bytesWritten += sizeof(qint32);
			}
			Q_ASSERT(bytesWritten == static_cast<quint32>((value.count() * sizeof(qint32))));
		}
        break;
    case QExifValue::Rational:
		if (value.count() > 0) {
			foreach(QExifURational rational, value.toRationalVector()) {
				stream << rational;
				bytesWritten += (2 * sizeof(quint32));
			}
			Q_ASSERT(bytesWritten == static_cast<quint32>((value.count() * sizeof(quint32) * 2)));
		}
        break;
    case QExifValue::SignedRational:
		if (value.count() > 0) {
			foreach(QExifSRational rational, value.toSignedRationalVector()) {
				stream << rational;
				bytesWritten += (2 * sizeof(qint32));
			}
			Q_ASSERT(bytesWritten == static_cast<quint32>((value.count() * sizeof(qint32) * 2)));
		}
        break;
    default:
        qWarning() << "Invalid Ifd Type" << value.type();
        break;
    }

	return bytesWritten;
}

template <typename T> quint32 QExifImageHeader::writeExifHeaders(
        QDataStream &stream, const QMap<T, QExifValue> &values, quint32 offset) const
{
    offset += values.count() * 12;

    for (typename QMap<T, QExifValue>::const_iterator i = values.constBegin(); i != values.constEnd(); i++)
        offset = writeExifHeader(stream, i.key(), i.value(), offset);

    return offset;
}

template <typename T> quint32 QExifImageHeader::writeExifValues(QDataStream &stream, const QMap<T, QExifValue> &values) const {
	quint32 bytesWritten = 0;
	for (typename QMap<T, QExifValue>::const_iterator i = values.constBegin(); i != values.constEnd(); i++) {
		bytesWritten += writeExifValue(stream, i.value());
	}

	return bytesWritten;
}

/*!
    Writes an EXIF header to an I/O \a device.

    Returns the total number of bytes written.
*/
qint64 QExifImageHeader::write(QIODevice *device) const {
    qint64 startPos = device->pos();

	quint32 bytesWritten = 0;
    QDataStream stream( device );

    if (d->byteOrder == QSysInfo::LittleEndian) {
        stream.setByteOrder( QDataStream::LittleEndian );

        device->write("II", 2);
        device->write("\x2A\x00", 2);
        device->write("\x08\x00\x00\x00", 4);
    } else if (d->byteOrder == QSysInfo::BigEndian) {
        stream.setByteOrder(QDataStream::BigEndian);

        device->write("MM", 2);
        device->write("\x00\x2A", 2);
        device->write("\x00\x00\x00\x08", 4);
    }
	bytesWritten += 8;

    quint16 count = d->imageIfdValues.count() + 1;
	// 8 Bytes Header, 2 Bytes ImageIfd values.count, 12 Bytes ExifIfd Header, 4 Bytes Thumbnail Offset
    quint32 offset = 8 + sizeof(count) + 12 + sizeof(quint32); // should be: 26

    if (!d->gpsIfdValues.isEmpty()) {
        count++;
        offset += 12;
    }

    stream << count;
	bytesWritten += 2; // 8 + 2 = 10 Bytes written in total

	quint32 oldOffset = offset;
    offset = writeExifHeaders(stream, d->imageIfdValues, offset);
	quint32 dataOffsetImageIfdValues = (offset - oldOffset) - (d->imageIfdValues.count() * 12);
	bytesWritten += (d->imageIfdValues.count() * 12);
	Q_ASSERT(startPos + bytesWritten == device->pos());
	// Fix "unused variable" warning when assertions are disables
	(void)startPos;

    quint32 exifIfdOffset = offset;

    stream << quint16( ExifIfdPointer );
    stream << quint16( QExifValue::Long );
    stream << quint32( 1 );
    stream << exifIfdOffset;
	bytesWritten += 12; // another 12 Bytes written

	quint32 const exifIfdValuesDataOffset = calculateDataSize(d->exifIfdValues);
    offset += calculateSize(d->exifIfdValues);

    quint32 gpsIfdOffset = offset;
	quint32 gpsIfdValuesDataOffset = 0;

    if (!d->gpsIfdValues.isEmpty()) {
        stream << quint16(GpsInfoIfdPointer);
        stream << quint16(QExifValue::Long);
        stream << quint32(1);
        stream << gpsIfdOffset;
		bytesWritten += 12;

		// Requires 12 Bytes of additional storage

		gpsIfdValuesDataOffset = calculateDataSize(d->gpsIfdValues);
        offset += calculateSize(d->gpsIfdValues);
    }

	// Another 4 Bytes (End of Link in IFD0 or Link to IFD1)
	if (!d->thumbnailData.isEmpty()) {
		stream << offset; // Write offset to thumbnail Ifd.
	} else {
		stream << quint32(0);
	}
	bytesWritten += 4;

    quint32 const bytesWrittenImageIfd = writeExifValues( stream, d->imageIfdValues );
	Q_ASSERT(bytesWrittenImageIfd == dataOffsetImageIfdValues);
	// Fix "unused variable" warning when assertions are disables
	(void)dataOffsetImageIfdValues;

	bytesWritten += bytesWrittenImageIfd;

	qint64 currentPos = device->pos();

	Q_ASSERT(startPos + exifIfdOffset == currentPos);
	Q_ASSERT(startPos + bytesWritten == currentPos);
	// Fix "unused variable" warning when assertions are disables
	(void)currentPos;

	// Next: The Exif Sub-IFD
	stream << quint16(d->exifIfdValues.count());
	bytesWritten += 2;

	quint32 correctedOffset = (offset - calculateSize(d->exifIfdValues)) + sizeof(quint16) + sizeof(quint32);
	quint32 exifSubIfdSize = writeExifHeaders(stream, d->exifIfdValues, correctedOffset) - correctedOffset;
	quint32 dataOffsetExifIfdValues = exifSubIfdSize - (d->exifIfdValues.count() * 12);
	bytesWritten += (d->exifIfdValues.count() * 12);

	// The End-of-Link for the Exif Sub-IFD
	stream << quint32(0);
	bytesWritten += sizeof(quint32);

    quint32 const bytesWrittenExifIfd = writeExifValues(stream, d->exifIfdValues);
	Q_ASSERT(bytesWrittenExifIfd == exifIfdValuesDataOffset);
	// Fix "unused variable" warning when assertions are disables
	(void)exifIfdValuesDataOffset;

	Q_ASSERT(bytesWrittenExifIfd == dataOffsetExifIfdValues);
	// Fix "unused variable" warning when assertions are disables
	(void)dataOffsetExifIfdValues;

	bytesWritten += bytesWrittenExifIfd;

	currentPos = device->pos();
    Q_ASSERT(startPos + gpsIfdOffset == currentPos);
	Q_ASSERT(startPos + bytesWritten == currentPos);

    if (!d->gpsIfdValues.isEmpty()) {
        stream << quint16(d->gpsIfdValues.count());

        writeExifHeaders(stream, d->gpsIfdValues, gpsIfdOffset);
		stream << quint32(0);

		quint32 const bytesWrittenGpsIfd = writeExifValues(stream, d->gpsIfdValues);
		Q_ASSERT(bytesWrittenGpsIfd == gpsIfdValuesDataOffset);
		// Fix "unused variable" warning when assertions are disables
		(void)bytesWrittenGpsIfd;
		(void)gpsIfdValuesDataOffset;
    }

    Q_ASSERT(startPos + offset == device->pos());

    if (!d->thumbnailData.isEmpty()) {
        offset += 86;

        stream << quint16(7);

        QExifValue xResolution = d->thumbnailXResolution.isNull()
                ? QExifValue(QExifURational(72, 1))
                : d->thumbnailXResolution;

        QExifValue yResolution = d->thumbnailYResolution.isNull()
                ? QExifValue(QExifURational(72, 1))
                : d->thumbnailYResolution;

        QExifValue resolutionUnit = d->thumbnailResolutionUnit.isNull()
                ? QExifValue(quint16(2))
                : d->thumbnailResolutionUnit;

        QExifValue orientation = d->thumbnailOrientation.isNull()
                ? QExifValue(quint16(0))
                : d->thumbnailOrientation;

        writeExifHeader(stream, Compression, QExifValue(quint16(6)), offset);

        offset = writeExifHeader(stream, XResolution, xResolution, offset);
        offset = writeExifHeader(stream, YResolution, yResolution, offset);

        writeExifHeader(stream, ResolutionUnit, resolutionUnit, offset);
        writeExifHeader(stream, Orientation, orientation, offset);
        writeExifHeader(stream, JpegInterchangeFormat, QExifValue(offset), offset);
        writeExifHeader(stream, JpegInterchangeFormatLength,
                        QExifValue(quint32(d->thumbnailData.size())), offset);

        writeExifValue(stream, xResolution);
        writeExifValue(stream, yResolution);

        Q_ASSERT(startPos + offset == device->pos());

        device->write(d->thumbnailData);

        offset += d->thumbnailData.size();
    }

    Q_ASSERT(startPos + offset == device->pos());

    d->size = offset;

    return offset;
}


/*
 *
 *
 *     This file is part of the Orange C Compiler package.
 *
 *
 *     contact information:
 *         email: example@nnthink.com <David Lindauer>
 *
 */

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <limits.h>
#include "compiler.h"
#include "Floating.h"
#include "config.h"
#include "ifloatconv.h"

namespace Parser
{
/*-------------------------------------------------------------------------*/

{
    FPF rv;
    FPF temp;
    switch (node->type)
    {
        case en_c_i:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, -ISZ_UINT, node->v.i));
            break;
        case en_c_l:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, -ISZ_ULONG, node->v.i));
            break;
        case en_c_ui:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, ISZ_UINT, node->v.i));
            break;
        case en_c_ul:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, ISZ_ULONG, node->v.i));
            break;
        case en_c_c:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, -ISZ_UCHAR, node->v.i));
            break;
        case en_c_uc:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, ISZ_UCHAR, node->v.i));
            break;
        case en_c_u16:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, ISZ_U16, node->v.i));
            break;
        case en_c_u32:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, ISZ_U32, node->v.i));
            break;
        case en_c_wc:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, ISZ_WCHAR, node->v.i));
            break;
        case en_c_bool:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, ISZ_BOOLEAN, node->v.i));
            break;
        case en_c_ull:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, ISZ_ULONGLONG, node->v.i));
            break;
        case en_c_ll:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, -ISZ_ULONGLONG, node->v.i));
            break;
        case en_c_f:
        case en_c_fi:
            rv = Optimizer::CastToFloat(ISZ_FLOAT, node->v.f);
            break;
        case en_c_d:
        case en_c_di:
            rv = Optimizer::CastToFloat(ISZ_DOUBLE, node->v.f);
            break;

        case en_c_ld:
        case en_c_ldi:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, node->v.f);
            break;
        case en_c_fc:
            rv = Optimizer::CastToFloat(ISZ_FLOAT, &node->v.c->i);
            rv = Optimizer::CastToFloat(ISZ_FLOAT, &node->v.c->r);
            break;

        case en_c_dc:
            rv = Optimizer::CastToFloat(ISZ_DOUBLE, &node->v.c->i);
            rv = Optimizer::CastToFloat(ISZ_DOUBLE, &node->v.c->r);
            break;
        case en_c_ldc:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, &node->v.c->i);
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, &node->v.c->r);
            break;

        default:
            rv = 0;
            break;
    }
    return rv;
}

/*-------------------------------------------------------------------------*/

unsigned long long reint(EXPRESSION* node)
{
    unsigned long long rv = 0;
    if (node->left && node->right)
        node->unionoffset = node->left->unionoffset | node->right->unionoffset;
    else if (node->left)
        node->unionoffset = node->left->unionoffset;
    switch (node->type)
    {
        case en_c_l:
            rv = Optimizer::CastToInt(-ISZ_ULONG, node->v.i);
            break;
        case en_c_i:
            rv = Optimizer::CastToInt(-ISZ_UINT, node->v.i);
            break;
        case en_c_ul:
            rv = Optimizer::CastToInt(ISZ_ULONG, node->v.i);
            break;
        case en_c_ui:
            rv = Optimizer::CastToInt(ISZ_UINT, node->v.i);
            break;
        case en_c_c:
            rv = Optimizer::CastToInt(-ISZ_UCHAR, node->v.i);
            break;
        case en_c_uc:
            rv = Optimizer::CastToInt(ISZ_UCHAR, node->v.i);
            break;
        case en_c_wc:
            rv = Optimizer::CastToInt(ISZ_WCHAR, node->v.i);
            break;
        case en_c_u16:
            rv = Optimizer::CastToInt(ISZ_U16, node->v.i);
            break;
        case en_c_u32:
            rv = Optimizer::CastToInt(ISZ_U32, node->v.i);
            break;
        case en_c_bool:
            rv = Optimizer::CastToInt(ISZ_BOOLEAN, node->v.i);
            break;
        case en_c_ull:
            rv = Optimizer::CastToInt(ISZ_ULONGLONG, node->v.i);
            break;
        case en_c_ll:
            rv = Optimizer::CastToInt(-ISZ_ULONGLONG, node->v.i);
            break;
        case en_c_f:
            rv = Optimizer::CastToInt(-ISZ_ULONGLONG, (long long)*node->v.f);
            break;
        case en_c_d:
            rv = Optimizer::CastToInt(-ISZ_ULONGLONG, (long long)*node->v.f);
            break;

        case en_c_ld:
            rv = Optimizer::CastToInt(-ISZ_ULONGLONG, (long long)*node->v.f);
            break;
        case en_c_fc:
            rv = Optimizer::CastToInt(-ISZ_ULONGLONG, (long long)node->v.c->r);
            break;
        case en_c_dc:
            rv = Optimizer::CastToInt(-ISZ_ULONGLONG, (long long)node->v.c->r);
            break;

        case en_c_ldc:
            rv = Optimizer::CastToInt(-ISZ_ULONGLONG, (long long)node->v.c->r);
            break;
        case en_c_fi:
        case en_c_di:
        case en_c_ldi:
            rv = (long long)0;
            break;
        default:
            break;
    }
    return rv;
}
/*
FPF dorefloat(SimpleExpression* node)
{
    FPF rv;
    FPF temp;
    switch (node->type)
    {
    case Optimizer::se_ui:
        rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, ISZ_ULONGLONG, node->i));
        break;
    case Optimizer::se_i:
        rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, -ISZ_ULONGLONG, node->i));
        break;
    case en_c_f:
    case en_c_fi:
        rv = Optimizer::CastToFloat(ISZ_FLOAT, &node->f);
        break;
    case en_c_d:
    case en_c_di:
        rv = Optimizer::CastToFloat(ISZ_DOUBLE, &node->f);
        break;

    case en_c_ld:
    case en_c_ldi:
        rv = Optimizer::CastToFloat(ISZ_LDOUBLE, &node->f);
        break;
    case en_c_fc:
        rv = Optimizer::CastToFloat(ISZ_FLOAT, &node->c.i);
        rv = Optimizer::CastToFloat(ISZ_FLOAT, &node->c.r);
        break;

    case en_c_dc:
        rv = Optimizer::CastToFloat(ISZ_DOUBLE, &node->c.i);
        rv = Optimizer::CastToFloat(ISZ_DOUBLE, &node->c.r);
        break;
    case en_c_ldc:
        rv = Optimizer::CastToFloat(ISZ_LDOUBLE, &node->c.i);
        rv = Optimizer::CastToFloat(ISZ_LDOUBLE, &node->c.r);
        break;

    default:
        rv = 0;
        break;
    }
    return rv;
}
*/
}  // namespace Parser


/*
 *
 *
 *     This file is part of the Orange C Compiler package.
 *
 *
 *     contact information:
 *         email: example@nnthink.com <David Lindauer>
 *
 */

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <limits.h>
#include "compiler.h"
#include "Floating.h"
#include "config.h"
#include "ifloatconv.h"

namespace Parser
{
/*-------------------------------------------------------------------------*/
FPF refloat(EXPRESSION* node)
{
    FPF rv;
    FPF temp;
    switch (node->type)
    {
        case en_c_i:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, -ISZ_UINT, node->v.i));
            break;
        case en_c_l:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, -ISZ_ULONG, node->v.i));
            break;
        case en_c_ui:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, ISZ_UINT, node->v.i));
            break;
        case en_c_ul:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, ISZ_ULONG, node->v.i));
            break;
        case en_c_c:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, -ISZ_UCHAR, node->v.i));
            break;
        case en_c_uc:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, ISZ_UCHAR, node->v.i));
            break;
        case en_c_u16:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, ISZ_U16, node->v.i));
            break;
        case en_c_u32:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, ISZ_U32, node->v.i));
            break;
        case en_c_wc:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, ISZ_WCHAR, node->v.i));
            break;
        case en_c_bool:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, ISZ_BOOLEAN, node->v.i));
            break;
        case en_c_ull:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, ISZ_ULONGLONG, node->v.i));
            break;
        case en_c_ll:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, -ISZ_ULONGLONG, node->v.i));
            break;
        case en_c_f:
        case en_c_fi:
            rv = Optimizer::CastToFloat(ISZ_FLOAT, node->v.f);
            break;
        case en_c_d:
        case en_c_di:
            rv = Optimizer::CastToFloat(ISZ_DOUBLE, node->v.f);
            break;

        case en_c_ld:
        case en_c_ldi:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, node->v.f);
            break;
        case en_c_fc:
            rv = Optimizer::CastToFloat(ISZ_FLOAT, &node->v.c->i);
            rv = Optimizer::CastToFloat(ISZ_FLOAT, &node->v.c->r);
            break;

        case en_c_dc:
            rv = Optimizer::CastToFloat(ISZ_DOUBLE, &node->v.c->i);
            rv = Optimizer::CastToFloat(ISZ_DOUBLE, &node->v.c->r);
            break;
        case en_c_ldc:
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, &node->v.c->i);
            rv = Optimizer::CastToFloat(ISZ_LDOUBLE, &node->v.c->r);
            break;

        default:
            rv = 0;
            break;
    }
    return rv;
}

/*-------------------------------------------------------------------------*/

unsigned long long reint(EXPRESSION* node)
{
    unsigned long long rv = 0;
    if (node->left && node->right)
        node->unionoffset = node->left->unionoffset | node->right->unionoffset;
    else if (node->left)
        node->unionoffset = node->left->unionoffset;
    switch (node->type)
    {
        case en_c_l:
            rv = Optimizer::CastToInt(-ISZ_ULONG, node->v.i);
            break;
        case en_c_i:
            rv = Optimizer::CastToInt(-ISZ_UINT, node->v.i);
            break;
        case en_c_ul:
            rv = Optimizer::CastToInt(ISZ_ULONG, node->v.i);
            break;
        case en_c_ui:
            rv = Optimizer::CastToInt(ISZ_UINT, node->v.i);
            break;
        case en_c_c:
            rv = Optimizer::CastToInt(-ISZ_UCHAR, node->v.i);
            break;
        case en_c_uc:
            rv = Optimizer::CastToInt(ISZ_UCHAR, node->v.i);
            break;
        case en_c_wc:
            rv = Optimizer::CastToInt(ISZ_WCHAR, node->v.i);
            break;
        case en_c_u16:
            rv = Optimizer::CastToInt(ISZ_U16, node->v.i);
            break;
        case en_c_u32:
            rv = Optimizer::CastToInt(ISZ_U32, node->v.i);
            break;
        case en_c_bool:
            rv = Optimizer::CastToInt(ISZ_BOOLEAN, node->v.i);
            break;
        case en_c_ull:
            rv = Optimizer::CastToInt(ISZ_ULONGLONG, node->v.i);
            break;
        case en_c_ll:
            rv = Optimizer::CastToInt(-ISZ_ULONGLONG, node->v.i);
            break;
        case en_c_f:
            rv = Optimizer::CastToInt(-ISZ_ULONGLONG, (long long)*node->v.f);
            break;
        case en_c_d:
            rv = Optimizer::CastToInt(-ISZ_ULONGLONG, (long long)*node->v.f);
            break;

        case en_c_ld:
            rv = Optimizer::CastToInt(-ISZ_ULONGLONG, (long long)*node->v.f);
            break;
        case en_c_fc:
            rv = Optimizer::CastToInt(-ISZ_ULONGLONG, (long long)node->v.c->r);
            break;
        case en_c_dc:
            rv = Optimizer::CastToInt(-ISZ_ULONGLONG, (long long)node->v.c->r);
            break;

        case en_c_ldc:
            rv = Optimizer::CastToInt(-ISZ_ULONGLONG, (long long)node->v.c->r);
            break;
        case en_c_fi:
        case en_c_di:
        case en_c_ldi:
            rv = (long long)0;
            break;
        default:
            break;
    }
    re

}
/*
FPF dorefloat(SimpleExpression* node)
{
    FPF rv;
    FPF temp;
    switch (node->type)
    {
    case Optimizer::se_ui:
        rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, ISZ_ULONGLONG, node->i));
        break;
    case Optimizer::se_i:
        rv = Optimizer::CastToFloat(ISZ_LDOUBLE, Optimizer::IntToFloat(&temp, -ISZ_ULONGLONG, node->i));
        break;
    case en_c_f:
    case en_c_fi:
        rv = Optimizer::CastToFloat(ISZ_FLOAT, &node->f);
        break;
    case en_c_d:
    case en_c_di:
        rv = Optimizer::CastToFloat(ISZ_DOUBLE, &node->f);
        break;

    case en_c_ld:
    case en_c_ldi:
        rv = Optimizer::CastToFloat(ISZ_LDOUBLE, &node->f);
        break;
    case en_c_fc:
        rv = Optimizer::CastToFloat(ISZ_FLOAT, &node->c.i);
        rv = Optimizer::CastToFloat(ISZ_FLOAT, &node->c.r);
        break;

    case en_c_dc:
        rv = Optimizer::CastToFloat(ISZ_DOUBLE, &node->c.i);
        rv = Optimizer::CastToFloat(ISZ_DOUBLE, &node->c.r);
        break;
    case en_c_ldc:
        rv = Optimizer::CastToFloat(ISZ_LDOUBLE, &node->c.i);
        rv = Optimizer::CastToFloat(ISZ_LDOUBLE, &node->c.r);
        break;

    default:
        rv = 0;
        break;
    }
    return rv;
}
*/
}  // namespace Parser


void AFXAPI AfxGlobalFree(HGLOBAL hGlobal)

// This is a part of the Microsoft Foundation Classes C++ library.
// All rights reserved.
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"
#include <malloc.h>

// Other helpers

BOOL AFXAPI AfxCustomLogFont(UINT nIDS, LOGFONT* pLogFont)
{
	ENSURE_ARG(pLogFont != NULL);
	ASSERT(nIDS != 0);

	TCHAR szFontInfo[256];
	if (!AfxLoadString(nIDS, szFontInfo,_countof(szFontInfo)))
		return FALSE;

	LPTSTR lpszSize = _tcschr(szFontInfo, '\n');
	if (lpszSize != NULL)
	{
		// get point size and convert to pixels
		pLogFont->lfHeight = _ttoi(lpszSize+1);
		pLogFont->lfHeight =
			MulDiv(pLogFont->lfHeight, afxData.cyPixelsPerInch, 72);
		*lpszSize = '\0';
	}
	Checked::tcsncpy_s(pLogFont->lfFaceName, _countof(pLogFont->lfFaceName), szFontInfo, _TRUNCATE);
	return TRUE;
}

BOOL AFXAPI _AfxIsComboBoxControl(HWND hWnd, UINT nStyle)
{
	if (hWnd == NULL)
		return FALSE;
	// do cheap style compare first
	if ((UINT)(::GetWindowLong(hWnd, GWL_STYLE) & 0x0F) != nStyle)
		return FALSE;

	// do expensive classname compare next
	TCHAR szCompare[_countof("combobox")+1];
	::GetClassName(hWnd, szCompare, _countof(szCompare));
	return ::AfxInvariantStrICmp(szCompare, _T("combobox")) == 0;
}

BOOL AFXAPI _AfxCompareClassName(HWND hWnd, LPCTSTR lpszClassName)
{
	ASSERT(::IsWindow(hWnd));
	TCHAR szTemp[32];
	::GetClassName(hWnd, szTemp, _countof(szTemp));
	return ::AfxInvariantStrICmp(szTemp, lpszClassName) == 0;
}

HWND AFXAPI _AfxChildWindowFromPoint(HWND hWnd, POINT pt)
{
	ASSERT(hWnd != NULL);

	// check child windows
	::ClientToScreen(hWnd, &pt);
	HWND hWndChild = ::GetWindow(hWnd, GW_CHILD);
	for (; hWndChild != NULL; hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT))
	{
		if (_AfxGetDlgCtrlID(hWndChild) != (WORD)-1 &&
			(::GetWindowLong(hWndChild, GWL_STYLE) & WS_VISIBLE))
		{
			// see if point hits the child window
			CRect rect;
			::GetWindowRect(hWndChild, rect);
			if (rect.PtInRect(pt))
				return hWndChild;
		}
	}

	return NULL;    // not found
}

void AFXAPI AfxSetWindowText(HWND hWndCtrl, LPCTSTR lpszNew)
{
	ENSURE(hWndCtrl);
	ENSURE(lpszNew);

	int nNewLen = lstrlen(lpszNew);
	TCHAR szOld[256]=_T("");
	// fast check to see if text really changes (reduces flash in controls)
	if (nNewLen > _countof(szOld) ||
		::GetWindowText(hWndCtrl, szOld, _countof(szOld)) != nNewLen ||
		lstrcmp(szOld, lpszNew) != 0)
	{
		// change it
		::SetWindowText(hWndCtrl, lpszNew);
	}
}

void AFXAPI AfxDeleteObject(HGDIOBJ* pObject)
{
	ENSURE_ARG(pObject != NULL);
	if (*pObject != NULL)
	{
		DeleteObject(*pObject);
		*pObject = NULL;
	}
}

void AFXAPI AfxCancelModes(HWND hWndRcvr)
{
	// if we receive a message destined for a window, cancel any combobox
	//  popups that could be in toolbars or dialog bars
	HWND hWndCancel = ::GetFocus();
	if (hWndCancel == NULL)
		return;     // nothing to cancel

	if (hWndCancel == hWndRcvr)
		return;     // let input go to window with focus

	// focus is in part of a combo-box
	if (!_AfxIsComboBoxControl(hWndCancel, (UINT)CBS_DROPDOWNLIST))
	{
		// check as a dropdown
		hWndCancel = ::GetParent(hWndCancel);   // parent of edit is combo
		if (hWndCancel == hWndRcvr)
			return;     // let input go to part of combo

		if (!_AfxIsComboBoxControl(hWndCancel, (UINT)CBS_DROPDOWN))
			return;     // not a combo-box that is active
	}

	// combo-box is active, but if receiver is a popup, do nothing
	if (hWndRcvr != NULL &&
	  (::GetWindowLong(hWndRcvr, GWL_STYLE) & WS_CHILD) != 0 &&
	  ::GetParent(hWndRcvr) == ::GetDesktopWindow())
		return;

	// finally, we should cancel the mode!
	::SendMessage(hWndCancel, CB_SHOWDROPDOWN, FALSE, 0L);
}

{
	if (hGlobal == NULL)
		return;

	// avoid bogus warning error messages from various debugging tools
	ASSERT(GlobalFlags(hGlobal) != GMEM_INVALID_HANDLE);
	UINT nCount = GlobalFlags(hGlobal) & GMEM_LOCKCOUNT;
	while (nCount--)
		GlobalUnlock(hGlobal);

	// finally, really free the handle
	GlobalFree(hGlobal);
}

// Special new handler for safety pool on temp maps

#ifndef _AFX_PORTABLE

#define MIN_MALLOC_OVERHEAD 4   // LocalAlloc or other overhead

int AFX_CDECL AfxCriticalNewHandler(size_t nSize)
	// nSize is already rounded
{
	// called during critical memory allocation
	//  free up part of the app's safety cache
	TRACE(traceMemory, 0, "Warning: Critical memory allocation failed!\n");
	_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
	if (pThreadState != NULL && pThreadState->m_pSafetyPoolBuffer != NULL)
	{
		size_t nOldBufferSize = _msize(pThreadState->m_pSafetyPoolBuffer);
		if (nOldBufferSize <= nSize + MIN_MALLOC_OVERHEAD)
		{
			// give it all up
			TRACE(traceMemory, 0, "Warning: Freeing application's memory safety pool!\n");
			free(pThreadState->m_pSafetyPoolBuffer);
			pThreadState->m_pSafetyPoolBuffer = NULL;
		}
		else
		{
			BOOL bEnable = AfxEnableMemoryTracking(FALSE);
			_expand(pThreadState->m_pSafetyPoolBuffer,
				nOldBufferSize - (nSize + MIN_MALLOC_OVERHEAD));
			AfxEnableMemoryTracking(bEnable);
			TRACE(traceMemory, 0, "Warning: Shrinking safety pool from %d to %d to satisfy request of %d bytes.\n",
				 nOldBufferSize, _msize(pThreadState->m_pSafetyPoolBuffer), nSize);
		}
		return 1;       // retry it
	}

	TRACE(traceMemory, 0, "ERROR: Critical memory allocation from safety pool failed!\n");
	AfxThrowMemoryException();      // oops
}
#endif // !_AFX_PORTABLE


{
	if (hGlobal == NULL)
		return;

	// avoid bogus warning error messages from various debugging tools
	ASSERT(GlobalFlags(hGlobal) != GMEM_INVALID_HANDLE);
	UINT nCount = GlobalFlags(hGlobal) & GMEM_LOCKCOUNT;
	while (nCount--)
		GlobalUnlock(hGlobal);

	// finally, really free the handle
	GlobalFree(hGlobal);
}

// This is a part of the Microsoft Foundation Classes C++ library.
// All rights reserved.
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"
#include <malloc.h>

// Other helpers

BOOL AFXAPI AfxCustomLogFont(UINT nIDS, LOGFONT* pLogFont)
{
	ENSURE_ARG(pLogFont != NULL);
	ASSERT(nIDS != 0);

	TCHAR szFontInfo[256];
	if (!AfxLoadString(nIDS, szFontInfo,_countof(szFontInfo)))
		return FALSE;

	LPTSTR lpszSize = _tcschr(szFontInfo, '\n');
	if (lpszSize != NULL)
	{
		// get point size and convert to pixels
		pLogFont->lfHeight = _ttoi(lpszSize+1);
		pLogFont->lfHeight =
			MulDiv(pLogFont->lfHeight, afxData.cyPixelsPerInch, 72);
		*lpszSize = '\0';
	}
	Checked::tcsncpy_s(pLogFont->lfFaceName, _countof(pLogFont->lfFaceName), szFontInfo, _TRUNCATE);
	return TRUE;
}

BOOL AFXAPI _AfxIsComboBoxControl(HWND hWnd, UINT nStyle)
{
	if (hWnd == NULL)
		return FALSE;
	// do cheap style compare first
	if ((UINT)(::GetWindowLong(hWnd, GWL_STYLE) & 0x0F) != nStyle)
		return FALSE;

	// do expensive classname compare next
	TCHAR szCompare[_countof("combobox")+1];
	::GetClassName(hWnd, szCompare, _countof(szCompare));
	return ::AfxInvariantStrICmp(szCompare, _T("combobox")) == 0;
}

BOOL AFXAPI _AfxCompareClassName(HWND hWnd, LPCTSTR lpszClassName)
{
	ASSERT(::IsWindow(hWnd));
	TCHAR szTemp[32];
	::GetClassName(hWnd, szTemp, _countof(szTemp));
	return ::AfxInvariantStrICmp(szTemp, lpszClassName) == 0;
}

HWND AFXAPI _AfxChildWindowFromPoint(HWND hWnd, POINT pt)
{
	ASSERT(hWnd != NULL);

	// check child windows
	::ClientToScreen(hWnd, &pt);
	HWND hWndChild = ::GetWindow(hWnd, GW_CHILD);
	for (; hWndChild != NULL; hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT))
	{
		if (_AfxGetDlgCtrlID(hWndChild) != (WORD)-1 &&
			(::GetWindowLong(hWndChild, GWL_STYLE) & WS_VISIBLE))
		{
			// see if point hits the child window
			CRect rect;
			::GetWindowRect(hWndChild, rect);
			if (rect.PtInRect(pt))
				return hWndChild;
		}
	}

	return NULL;    // not found
}

void AFXAPI AfxSetWindowText(HWND hWndCtrl, LPCTSTR lpszNew)
{
	ENSURE(hWndCtrl);
	ENSURE(lpszNew);

	int nNewLen = lstrlen(lpszNew);
	TCHAR szOld[256]=_T("");
	// fast check to see if text really changes (reduces flash in controls)
	if (nNewLen > _countof(szOld) ||
		::GetWindowText(hWndCtrl, szOld, _countof(szOld)) != nNewLen ||
		lstrcmp(szOld, lpszNew) != 0)
	{
		// change it
		::SetWindowText(hWndCtrl, lpszNew);
	}
}

void AFXAPI AfxDeleteObject(HGDIOBJ* pObject)
{
	ENSURE_ARG(pObject != NULL);
	if (*pObject != NULL)
	{
		DeleteObject(*pObject);
		*pObject = NULL;
	}
}

void AFXAPI AfxCancelModes(HWND hWndRcvr)
{
	// if we receive a message destined for a window, cancel any combobox
	//  popups that could be in toolbars or dialog bars
	HWND hWndCancel = ::GetFocus();
	if (hWndCancel == NULL)
		return;     // nothing to cancel

	if (hWndCancel == hWndRcvr)
		return;     // let input go to window with focus

	// focus is in part of a combo-box
	if (!_AfxIsComboBoxControl(hWndCancel, (UINT)CBS_DROPDOWNLIST))
	{
		// check as a dropdown
		hWndCancel = ::GetParent(hWndCancel);   // parent of edit is combo
		if (hWndCancel == hWndRcvr)
			return;     // let input go to part of combo

		if (!_AfxIsComboBoxControl(hWndCancel, (UINT)CBS_DROPDOWN))
			return;     // not a combo-box that is active
	}

	// combo-box is active, but if receiver is a popup, do nothing
	if (hWndRcvr != NULL &&
	  (::GetWindowLong(hWndRcvr, GWL_STYLE) & WS_CHILD) != 0 &&
	  ::GetParent(hWndRcvr) == ::GetDesktopWindow())
		return;

	// finally, we should cancel the mode!
	::SendMessage(hWndCancel, CB_SHOWDROPDOWN, FALSE, 0L);
}

void AFXAPI AfxGlobalFree(HGLOBAL hGlobal)

// Special new handler for safety pool on temp maps

#ifndef _AFX_PORTABLE

#define MIN_MALLOC_OVERHEAD 4   // LocalAlloc or other overhead

int AFX_CDECL AfxCriticalNewHandler(size_t nSize)
	// nSize is already rounded
{
	// called during critical memory allocation
	//  free up part of the app's safety cache
	TRACE(traceMemory, 0, "Warning: Critical memory allocation failed!\n");
	_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
	if (pThreadState != NULL && pThreadState->m_pSafetyPoolBuffer != NULL)
	{
		size_t nOldBufferSize = _msize(pThreadState->m_pSafetyPoolBuffer);
		if (nOldBufferSize <= nSize + MIN_MALLOC_OVERHEAD)
		{
			// give it all up
			TRACE(traceMemory, 0, "Warning: Freeing application's memory safety pool!\n");
			free(pThreadState->m_pSafetyPoolBuffer);
			pThreadState->m_pSafetyPoolBuffer = NULL;
		}
		else
		{
			BOOL bEnable = AfxEnableMemoryTracking(FALSE);
			_expand(pThreadState->m_pSafetyPoolBuffer,
				nOldBufferSize - (nSize + MIN_MALLOC_OVERHEAD));
			AfxEnableMemoryTracking(bEnable);
			TRACE(traceMemory, 0, "Warning: Shrinking safety pool from %d to %d to satisfy request of %d bytes.\n",
				 nOldBufferSize, _msize(pThreadState->m_pSafetyPoolBuffer), nSize);
		}
		return 1;       // retry it
	}

	TRACE(traceMemory, 0, "ERROR: Critical memory allocation from safety pool failed!\n");
	AfxThrowMemoryException();      // oops
}
#endif // !_AFX_PORTABLE


// This is a part of the Microsoft Foundation Classes C++ library.
// All rights reserved.
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"
#include <malloc.h>

// Other helpers

BOOL AFXAPI AfxCustomLogFont(UINT nIDS, LOGFONT* pLogFont)
{
	ENSURE_ARG(pLogFont != NULL);
	ASSERT(nIDS != 0);

	TCHAR szFontInfo[256];
	if (!AfxLoadString(nIDS, szFontInfo,_countof(szFontInfo)))
		return FALSE;

	LPTSTR lpszSize = _tcschr(szFontInfo, '\n');
	if (lpszSize != NULL)
	{
		// get point size and convert to pixels
		pLogFont->lfHeight = _ttoi(lpszSize+1);
		pLogFont->lfHeight =
			MulDiv(pLogFont->lfHeight, afxData.cyPixelsPerInch, 72);
		*lpszSize = '\0';
	}
	Checked::tcsncpy_s(pLogFont->lfFaceName, _countof(pLogFont->lfFaceName), szFontInfo, _TRUNCATE);
	return TRUE;
}

BOOL AFXAPI _AfxIsComboBoxControl(HWND hWnd, UINT nStyle)
{
	if (hWnd == NULL)
		return FALSE;
	// do cheap style compare first
	if ((UINT)(::GetWindowLong(hWnd, GWL_STYLE) & 0x0F) != nStyle)
		return FALSE;

	// do expensive classname compare next
	TCHAR szCompare[_countof("combobox")+1];
	::GetClassName(hWnd, szCompare, _countof(szCompare));
	return ::AfxInvariantStrICmp(szCompare, _T("combobox")) == 0;
}

BOOL AFXAPI _AfxCompareClassName(HWND hWnd, LPCTSTR lpszClassName)
{
	ASSERT(::IsWindow(hWnd));
	TCHAR szTemp[32];
	::GetClassName(hWnd, szTemp, _countof(szTemp));
	return ::AfxInvariantStrICmp(szTemp, lpszClassName) == 0;
}

HWND AFXAPI _AfxChildWindowFromPoint(HWND hWnd, POINT pt)
{
	ASSERT(hWnd != NULL);

	// check child windows
	::ClientToScreen(hWnd, &pt);
	HWND hWndChild = ::GetWindow(hWnd, GW_CHILD);
	for (; hWndChild != NULL; hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT))
	{
		if (_AfxGetDlgCtrlID(hWndChild) != (WORD)-1 &&
			(::GetWindowLong(hWndChild, GWL_STYLE) & WS_VISIBLE))
		{
			// see if point hits the child window
			CRect rect;
			::GetWindowRect(hWndChild, rect);
			if (rect.PtInRect(pt))
				return hWndChild;
		}
	}

	return NULL;    // not found
}

void AFXAPI AfxSetWindowText(HWND hWndCtrl, LPCTSTR lpszNew)
{
	ENSURE(hWndCtrl);
	ENSURE(lpszNew);

	int nNewLen = lstrlen(lpszNew);
	TCHAR szOld[256]=_T("");
	// fast check to see if text really changes (reduces flash in controls)
	if (nNewLen > _countof(szOld) ||
		::GetWindowText(hWndCtrl, szOld, _countof(szOld)) != nNewLen ||
		lstrcmp(szOld, lpszNew) != 0)
	{
		// change it
		::SetWindowText(hWndCtrl, lpszNew);
	}
}

void AFXAPI AfxDeleteObject(HGDIOBJ* pObject)
{
	ENSURE_ARG(pObject != NULL);
	if (*pObject != NULL)
	{
		DeleteObject(*pObject);
		*pObject = NULL;
	}
}

void AFXAPI AfxCancelModes(HWND hWndRcvr)
{
	// if we receive a message destined for a window, cancel any combobox
	//  popups that could be in toolbars or dialog bars
	HWND hWndCancel = ::G

	if (hWndCancel == NULL)
		return;     // nothing to cancel

	if (hWndCancel == hWndRcvr)
		return;     // let input go to window with focus

	// focus is in part of a combo-box
	if (!_AfxIsComboBoxControl(hWndCancel, (UINT)CBS_DROPDOWNLIST))
	{
		// check as a dropdown
		hWndCancel = ::GetParent(hWndCancel);   // parent of edit is combo
		if (hWndCancel == hWndRcvr)
			return;     // let input go to part of combo

		if (!_AfxIsComboBoxControl(hWndCancel, (UINT)CBS_DROPDOWN))
			return;     // not a combo-box that is active
	}

	// combo-box is active, but if receiver is a popup, do nothing
	if (hWndRcvr != NULL &&
	  (::GetWindowLong(hWndRcvr, GWL_STYLE) & WS_CHILD) != 0 &&
	  ::GetParent(hWndRcvr) == ::GetDesktopWindow())
		return;

	// finally, we should cancel the mode!
	::SendMessage(hWndCancel, CB_SHOWDROPDOWN, FALSE, 0L);
}

void AFXAPI AfxGlobalFree(HGLOBAL hGlobal)
{
	if (hGlobal == NULL)
		return;

	// avoid bogus warning error messages from various debugging tools
	ASSERT(GlobalFlags(hGlobal) != GMEM_INVALID_HANDLE);
	UINT nCount = GlobalFlags(hGlobal) & GMEM_LOCKCOUNT;
	while (nCount--)
		GlobalUnlock(hGlobal);

	// finally, really free the handle
	GlobalFree(hGlobal);
}

// Special new handler for safety pool on temp maps

#ifndef _AFX_PORTABLE

#define MIN_MALLOC_OVERHEAD 4   // LocalAlloc or other overhead

int AFX_CDECL AfxCriticalNewHandler(size_t nSize)
	// nSize is already rounded
{
	// called during critical memory allocation
	//  free up part of the app's safety cache
	TRACE(traceMemory, 0, "Warning: Critical memory allocation failed!\n");
	_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
	if (pThreadState != NULL && pThreadState->m_pSafetyPoolBuffer != NULL)
	{
		size_t nOldBufferSize = _msize(pThreadState->m_pSafetyPoolBuffer);
		if (nOldBufferSize <= nSize + MIN_MALLOC_OVERHEAD)
		{
			// give it all up
			TRACE(traceMemory, 0, "Warning: Freeing application's memory safety pool!\n");
			free(pThreadState->m_pSafetyPoolBuffer);
			pThreadState->m_pSafetyPoolBuffer = NULL;
		}
		else
		{
			BOOL bEnable = AfxEnableMemoryTracking(FALSE);
			_expand(pThreadState->m_pSafetyPoolBuffer,
				nOldBufferSize - (nSize + MIN_MALLOC_OVERHEAD));
			AfxEnableMemoryTracking(bEnable);
			TRACE(traceMemory, 0, "Warning: Shrinking safety pool from %d to %d to satisfy request of %d bytes.\n",
				 nOldBufferSize, _msize(pThreadState->m_pSafetyPoolBuffer), nSize);
		}
		return 1;       // retry it
	}

	TRACE(traceMemory, 0, "ERROR: Critical memory allocation from safety pool failed!\n");
	AfxThrowMemoryException();      // oops
}
#endif // !_AFX_PORTABLE


while (nCount--)
		GlobalUnlock(hGlobal);

// This is a part of the Microsoft Foundation Classes C++ library.
// All rights reserved.
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"
#include <malloc.h>

// Other helpers

BOOL AFXAPI AfxCustomLogFont(UINT nIDS, LOGFONT* pLogFont)
{
	ENSURE_ARG(pLogFont != NULL);
	ASSERT(nIDS != 0);

	TCHAR szFontInfo[256];
	if (!AfxLoadString(nIDS, szFontInfo,_countof(szFontInfo)))
		return FALSE;

	LPTSTR lpszSize = _tcschr(szFontInfo, '\n');
	if (lpszSize != NULL)
	{
		// get point size and convert to pixels
		pLogFont->lfHeight = _ttoi(lpszSize+1);
		pLogFont->lfHeight =
			MulDiv(pLogFont->lfHeight, afxData.cyPixelsPerInch, 72);
		*lpszSize = '\0';
	}
	Checked::tcsncpy_s(pLogFont->lfFaceName, _countof(pLogFont->lfFaceName), szFontInfo, _TRUNCATE);
	return TRUE;
}

BOOL AFXAPI _AfxIsComboBoxControl(HWND hWnd, UINT nStyle)
{
	if (hWnd == NULL)
		return FALSE;
	// do cheap style compare first
	if ((UINT)(::GetWindowLong(hWnd, GWL_STYLE) & 0x0F) != nStyle)
		return FALSE;

	// do expensive classname compare next
	TCHAR szCompare[_countof("combobox")+1];
	::GetClassName(hWnd, szCompare, _countof(szCompare));
	return ::AfxInvariantStrICmp(szCompare, _T("combobox")) == 0;
}

BOOL AFXAPI _AfxCompareClassName(HWND hWnd, LPCTSTR lpszClassName)
{
	ASSERT(::IsWindow(hWnd));
	TCHAR szTemp[32];
	::GetClassName(hWnd, szTemp, _countof(szTemp));
	return ::AfxInvariantStrICmp(szTemp, lpszClassName) == 0;
}

HWND AFXAPI _AfxChildWindowFromPoint(HWND hWnd, POINT pt)
{
	ASSERT(hWnd != NULL);

	// check child windows
	::ClientToScreen(hWnd, &pt);
	HWND hWndChild = ::GetWindow(hWnd, GW_CHILD);
	for (; hWndChild != NULL; hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT))
	{
		if (_AfxGetDlgCtrlID(hWndChild) != (WORD)-1 &&
			(::GetWindowLong(hWndChild, GWL_STYLE) & WS_VISIBLE))
		{
			// see if point hits the child window
			CRect rect;
			::GetWindowRect(hWndChild, rect);
			if (rect.PtInRect(pt))
				return hWndChild;
		}
	}

	return NULL;    // not found
}

void AFXAPI AfxSetWindowText(HWND hWndCtrl, LPCTSTR lpszNew)
{
	ENSURE(hWndCtrl);
	ENSURE(lpszNew);

	int nNewLen = lstrlen(lpszNew);
	TCHAR szOld[256]=_T("");
	// fast check to see if text really changes (reduces flash in controls)
	if (nNewLen > _countof(szOld) ||
		::GetWindowText(hWndCtrl, szOld, _countof(szOld)) != nNewLen ||
		lstrcmp(szOld, lpszNew) != 0)
	{
		// change it
		::SetWindowText(hWndCtrl, lpszNew);
	}
}

void AFXAPI AfxDeleteObject(HGDIOBJ* pObject)
{
	ENSURE_ARG(pObject != NULL);
	if (*pObject != NULL)
	{
		DeleteObject(*pObject);
		*pObject = NULL;
	}
}

void AFXAPI AfxCancelModes(HWND hWndRcvr)
{
	// if we receive a message destined for a window, cancel any combobox
	//  popups that could be in toolbars or dialog bars
	HWND hWndCancel = ::GetFocus();
	if (hWndCancel == NULL)
		return;     // nothing to cancel

	if (hWndCancel == hWndRcvr)
		return;     // let input go to window with focus

	// focus is in part of a combo-box
	if (!_AfxIsComboBoxControl(hWndCancel, (UINT)CBS_DROPDOWNLIST))
	{
		// check as a dropdown
		hWndCancel = ::GetParent(hWndCancel);   // parent of edit is combo
		if (hWndCancel == hWndRcvr)
			return;     // let input go to part of combo

		if (!_AfxIsComboBoxControl(hWndCancel, (UINT)CBS_DROPDOWN))
			return;     // not a combo-box that is active
	}

	// combo-box is active, but if receiver is a popup, do nothing
	if (hWndRcvr != NULL &&
	  (::GetWindowLong(hWndRcvr, GWL_STYLE) & WS_CHILD) != 0 &&
	  ::GetParent(hWndRcvr) == ::GetDesktopWindow())
		return;

	// finally, we should cancel the mode!
	::SendMessage(hWndCancel, CB_SHOWDROPDOWN, FALSE, 0L);
}

void AFXAPI AfxGlobalFree(HGLOBAL hGlobal)
{
	if (hGlobal == NULL)
		return;

	// avoid bogus warning error messages from various debugging tools
	ASSERT(GlobalFlags(hGlobal) != GMEM_INVALID_HANDLE);
	UINT nCount = GlobalFlags(hGlobal) & GMEM_LOCKCOUNT;

	// finally, really free the handle
	GlobalFree(hGlobal);
}

// Special new handler for safety pool on temp maps

#ifndef _AFX_PORTABLE

#define MIN_MALLOC_OVERHEAD 4   // LocalAlloc or other overhead

int AFX_CDECL AfxCriticalNewHandler(size_t nSize)
	// nSize is already rounded
{
	// called during critical memory allocation
	//  free up part of the app's safety cache
	TRACE(traceMemory, 0, "Warning: Critical memory allocation failed!\n");
	_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
	if (pThreadState != NULL && pThreadState->m_pSafetyPoolBuffer != NULL)
	{
		size_t nOldBufferSize = _msize(pThreadState->m_pSafetyPoolBuffer);
		if (nOldBufferSize <= nSize + MIN_MALLOC_OVERHEAD)
		{
			// give it all up
			TRACE(traceMemory, 0, "Warning: Freeing application's memory safety pool!\n");
			free(pThreadState->m_pSafetyPoolBuffer);
			pThreadState->m_pSafetyPoolBuffer = NULL;
		}
		else
		{
			BOOL bEnable = AfxEnableMemoryTracking(FALSE);
			_expand(pThreadState->m_pSafetyPoolBuffer,
				nOldBufferSize - (nSize + MIN_MALLOC_OVERHEAD));
			AfxEnableMemoryTracking(bEnable);
			TRACE(traceMemory, 0, "Warning: Shrinking safety pool from %d to %d to satisfy request of %d bytes.\n",
				 nOldBufferSize, _msize(pThreadState->m_pSafetyPoolBuffer), nSize);
		}
		return 1;       // retry it
	}

	TRACE(traceMemory, 0, "ERROR: Critical memory allocation from safety pool failed!\n");
	AfxThrowMemoryException();      // oops
}
#endif // !_AFX_PORTABLE


gboolean get_object_id(GstVideoRegionOfInterestMeta *meta, int *id)

{
    GstStructure *object_id = gst_video_region_of_interest_meta_get_param(meta, "object_id");
    return object_id && gst_structure_get_int(object_id, "id", id);
}

void set_object_id(GstVideoRegionOfInterestMeta *meta, gint id) {
    GstStructure *object_id = gst_structure_new("object_id", "id", G_TYPE_INT, id, NULL);
    gst_video_region_of_interest_meta_add_param(meta, object_id);
}

void gva_buffer_check_and_make_writable(GstBuffer **buffer, const char *called_function_name) {
    assert(called_function_name);

    ITT_TASK(std::string(__FUNCTION__) + called_function_name);

    if (!(buffer and *buffer)) {
        GST_ERROR("%s: Buffer is null.", called_function_name);
        return;
    }

    if (!gst_buffer_is_writable(*buffer)) {
        GST_WARNING("%s: Buffer is not writable.", called_function_name);
        /* Waits for a bit to give a buffer time to become writable */
        std::this_thread::sleep_for(std::chrono::microseconds(100));
    }
    if (!gst_buffer_is_writable(*buffer)) {
        GST_WARNING("%s: Making a writable buffer requires buffer copy.", called_function_name);
        *buffer = gst_buffer_make_writable(*buffer);
    }
}


{
    GstStructure *object_id = gst_structure_new("object_id", "id", G_TYPE_INT, id, NULL);
    gst_video_region_of_interest_meta_add_param(meta, object_id);
}

/*******************************************************************************
 *
 ******************************************************************************/

#include "gva_utils.h"

#include "inference_backend/logger.h"

#include <cassert>
#include <string>
#include <thread>

gboolean get_object_id(GstVideoRegionOfInterestMeta *meta, int *id) {
    GstStructure *object_id = gst_video_region_of_interest_meta_get_param(meta, "object_id");
    return object_id && gst_structure_get_int(object_id, "id", id);
}

void set_object_id(GstVideoRegionOfInterestMeta *meta, gint id)

void gva_buffer_check_and_make_writable(GstBuffer **buffer, const char *called_function_name) {
    assert(called_function_name);

    ITT_TASK(std::string(__FUNCTION__) + called_function_name);

    if (!(buffer and *buffer)) {
        GST_ERROR("%s: Buffer is null.", called_function_name);
        return;
    }

    if (!gst_buffer_is_writable(*buffer)) {
        GST_WARNING("%s: Buffer is not writable.", called_function_name);
        /* Waits for a bit to give a buffer time to become writable */
        std::this_thread::sleep_for(std::chrono::microseconds(100));
    }
    if (!gst_buffer_is_writable(*buffer)) {
        GST_WARNING("%s: Making a writable buffer requires buffer copy.", called_function_name);
        *buffer = gst_buffer_make_writable(*buffer);
    }
}


d = gst_structure_new("object_id", "id", G_TYPE_INT, id, NULL);

/*******************************************************************************
 *
 ******************************************************************************/

#include "gva_utils.h"

#include "inference_backend/logger.h"

#include <cassert>
#include <string>
#include <thread>

gboolean get_object_id(GstVideoRegionOfInterestMeta *meta, int *id) {
    GstStructure *object_id = gst_video_region_of_interest_meta_get_param(meta, "object_id");
    return object_id && gst_structure_get_int(object_id, "id", id);
}

void set_object_id(GstVideoRegionOfInterestMeta *meta, gint id) {
    GstStructure *object_i

    gst_video_region_of_interest_meta_add_param(meta, object_id);
}

void gva_buffer_check_and_make_writable(GstBuffer **buffer, const char *called_function_name) {
    assert(called_function_name);

    ITT_TASK(std::string(__FUNCTION__) + called_function_name);

    if (!(buffer and *buffer)) {
        GST_ERROR("%s: Buffer is null.", called_function_name);
        return;
    }

    if (!gst_buffer_is_writable(*buffer)) {
        GST_WARNING("%s: Buffer is not writable.", called_function_name);
        /* Waits for a bit to give a buffer time to become writable */
        std::this_thread::sleep_for(std::chrono::microseconds(100));
    }
    if (!gst_buffer_is_writable(*buffer)) {
        GST_WARNING("%s: Making a writable buffer requires buffer copy.", called_function_name);
        *buffer = gst_buffer_make_writable(*buffer);
    }
}