/**********************************************************************************************
   Copyright (C) 2015 Ivo Kronenberg <>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

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

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.

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

#include "gis/fit/decoder/CFitByteDataTransformer.h"
#include "gis/fit/decoder/CFitDefinitionMessage.h"
#include "gis/fit/defs/CFitBaseType.h"
#include "gis/fit/defs/fit_const.h"

quint64 CFitByteDataTransformer::getUIntValue(const CFitBaseType& baseType, quint8* rawData)
{
    switch(baseType.nr())
    {
    case eBaseTypeNrUint8:
    case eBaseTypeNrUint8z:
    case eBaseTypeNrEnum:
        return getUint8(rawData);

    case eBaseTypeNrUint16:
    case eBaseTypeNrUint16z:
        return getUint16(rawData);

    case eBaseTypeNrUint32:
    case eBaseTypeNrUint32z:
        return getUint32(rawData);

    case eBaseTypeNrUint64:
    case eBaseTypeNrUint64z:
        return getUint64(rawData);

    default:
        return 0;
    }
}

qint64 CFitByteDataTransformer::getSIntValue(const CFitBaseType& baseType, quint8 *rawData)
{
    switch(baseType.nr())
    {
    case eBaseTypeNrSint8:
        return getSint8(rawData);

    case eBaseTypeNrSint16:
        return getSint16(rawData);

    case eBaseTypeNrSint32:
        return getSint32(rawData);

    case eBaseTypeNrSint64:
        return getSint64(rawData);

    default:
        return 0;
    }
}

qreal CFitByteDataTransformer::getFloatValue(const CFitBaseType& baseType, quint8* rawData)
{
    switch(baseType.nr())
    {
    case eBaseTypeNrFloat32:
        return getFloat32(rawData);

    case eBaseTypeNrFloat64:
        return getFloat64(rawData);

    default:
        return 0;
    }
}

quint8 CFitByteDataTransformer::getUint8(quint8* rawData)
{
    return (quint8) rawData[0];
}

quint16 CFitByteDataTransformer::getUint16(quint8* rawData)
{
    return ((quint16)rawData[1] << 8) | (quint16)rawData[0];
}

quint32 CFitByteDataTransformer::getUint32(quint8* rawData)
{
    return ((quint32)rawData[3] << 24) | ((quint32)rawData[2] << 16) | ((quint32)rawData[1] << 8) | (quint32)rawData[0];
}

quint64 CFitByteDataTransformer::getUint64(quint8* rawData)
{
    return ((quint64) rawData[7] << 56) | ((quint64) rawData[6] << 48)
           | ((quint64) rawData[5] << 40) | ((quint64) rawData[4] << 32)
           | ((quint64) rawData[3] << 24) | ((quint64) rawData[2] << 16)
           | ((quint64) rawData[1] << 8) | rawData[0];
}

qint8 CFitByteDataTransformer::getSint8(quint8* rawData)
{
    return (qint8) rawData[0];
}

qint16 CFitByteDataTransformer::getSint16(quint8* rawData)
{
    return ((qint16)rawData[1] << 8) | (qint16)rawData[0];
}

qint32 CFitByteDataTransformer::getSint32(quint8* rawData)
{
    return ((qint32)rawData[3] << 24) | ((qint32)rawData[2] << 16) | ((qint32)rawData[1] << 8) | (qint32)rawData[0];
}


qint64 CFitByteDataTransformer::getSint64(quint8* rawData)
{
    return ((qint64) rawData[7] << 56) | ((qint64) rawData[6] << 48)
           | ((qint64) rawData[5] << 40) | ((qint64) rawData[4] << 32)
           | ((qint64) rawData[3] << 24) | ((qint64) rawData[2] << 16)
           | ((qint64) rawData[1] << 8) | rawData[0];
}

qreal CFitByteDataTransformer::getFloat32(quint8* rawData)
{
    qint32 fValue = (qint32) (((qint32)rawData[3] << 24) | ((qint32)rawData[2] << 16) | ((qint32)rawData[1] << 8) | rawData[0]);
    // comment: qreal is a double type (on almost all systems). Here we need to go through a 32 bit floating type.
    float tmp;
    memcpy(&tmp, &fValue, sizeof(tmp));
    qreal value = tmp;
    return value;
}

qreal CFitByteDataTransformer::getFloat64(quint8* rawData)
{
    unsigned long long dValue = ((unsigned long long) rawData[7] << 56) | ((unsigned long long) rawData[6] << 48)
                                | ((unsigned long long) rawData[5] << 40) | ((unsigned long long) rawData[4] << 32)
                                | ((unsigned long long) rawData[3] << 24) | ((unsigned long long) rawData[2] << 16)
                                | ((unsigned long long) rawData[1] << 8) | rawData[0];
    // comment: qreal is a double type (on almost all systems). Here we need to go through a 64 bit floating type.
    double tmp;
    memcpy(&tmp, &dValue, sizeof(tmp));
    qreal value = tmp;
    return value;
}

QString CFitByteDataTransformer::getString(quint8* rawData, quint8 length)
{
    // find the 0 termination
    quint8 i = 0;
    while(rawData[i] != 0 )
    {
        i++;
        // no 0 termination found, but length exceeded
        if(i > length)
        {
            break;
        }
    }

    return QString::fromUtf8((const char*)rawData, i);
}

QByteArray CFitByteDataTransformer::getBytes(quint8* rawData, quint8 length)
{
    return QByteArray((const char*)rawData, length);
}

void CFitByteDataTransformer::swapFieldData(const CFitFieldDefinition& fieldDef, quint8* fieldData)
{
    if (fieldDef.getEndianAbilityFlag() && (fieldDef.parent().getArchitectureBit() != eFitArchEndianLittle))
    {
        // Swap the bytes for each element.
        int typeSize = fieldDef.getBaseType().size();
        int elements = fieldDef.getSize() / typeSize;

        for (int element = 0; element < elements; element++)
        {
            for (int i = 0; i < (typeSize / 2); i++)
            {
                quint8 tmp = (quint8)fieldData[element * typeSize + i];
                fieldData[element * typeSize + i] = fieldData[element * typeSize + typeSize - i - 1];
                fieldData[element * typeSize + typeSize - i - 1] = tmp;
            }
        }
    }
}
