#include "animparts.h"

#include <QIODevice>

QDataStream &operator<<(QDataStream &aDataStream, const XAnimDynamicFrames &aAnimDynamicFramesIn)
{
    aDataStream << aAnimDynamicFramesIn.framesPtr;

    return aDataStream;
}

QDataStream &operator>>(QDataStream &aDataStream, XAnimDynamicFrames &aAnimDynamicFramesOut)
{
    aDataStream >> aAnimDynamicFramesOut.framesPtr;

    return aDataStream;
}
QString XAnimDynamicFramesToString(const XAnimDynamicFrames &aAnimDynamicFrames) {
    QString debug;

    debug.append("XAnimDynamicFrames(");

    debug.append(QString("\n  framesPtr: %1").arg(aAnimDynamicFrames.framesPtr));
    for (int i = 0; i < 3; i++) {
        debug.append(QString("\n  frames %1: %2").arg(i).arg(aAnimDynamicFrames.frames[i]));
    }

    debug.append("\n)");

    return debug;
}

QDataStream &operator<<(QDataStream &aDataStream, const XAnimDynamicIndices &aAnimDynamicIndicesIn)
{
    aDataStream << aAnimDynamicIndicesIn.indices[0];

    return aDataStream;
}

QDataStream &operator>>(QDataStream &aDataStream, XAnimDynamicIndices &aAnimDynamicIndicesOut)
{
    aDataStream >> aAnimDynamicIndicesOut.indices[0];

    return aDataStream;
}
QString XAnimDynamicIndicesToString(const XAnimDynamicIndices &aAnimDynamicIndices) {
    QString debug;

    debug.append("XAnimDynamicIndices(");

    debug.append(QString("\n  indices: %1").arg(aAnimDynamicIndices.indices[0]));

    debug.append("\n)");

    return debug;
}

QDataStream &operator<<(QDataStream &aDataStream, const XAnimPartTransFrames &aAnimPartTransFramesIn)
{
    for (int i = 0; i < 3; i++) {
        aDataStream << aAnimPartTransFramesIn.mins[i];
    }

    for (int i = 0; i < 3; i++) {
        aDataStream << aAnimPartTransFramesIn.size[i];
    }

    aDataStream
        << aAnimPartTransFramesIn.frames
        << aAnimPartTransFramesIn.indices;

    return aDataStream;
}

QDataStream &operator>>(QDataStream &aDataStream, XAnimPartTransFrames &aAnimPartTransFramesOut)
{
    for (int i = 0; i < 3; i++) {
        aDataStream >> aAnimPartTransFramesOut.mins[i];
    }

    for (int i = 0; i < 3; i++) {
        aDataStream >> aAnimPartTransFramesOut.size[i];
    }

    aDataStream
        >> aAnimPartTransFramesOut.frames
        >> aAnimPartTransFramesOut.indices;

    return aDataStream;
}
QString XAnimPartTransFramesToString(const XAnimPartTransFrames &aAnimPartTransFrames) {
    QString debug;

    debug.append("XAnimPartTransFrames(");

    for (int i = 0; i < 3; i++) {
        debug.append(QString("\n  mins %1: %2").arg(i).arg(aAnimPartTransFrames.mins[i]));
    }
    for (int i = 0; i < 3; i++) {
        debug.append(QString("\n  size %1: %2").arg(i).arg(aAnimPartTransFrames.size[i]));
    }
    debug.append(QString("\n  frames: %1").arg(XAnimDynamicFramesToString(aAnimPartTransFrames.frames)));
    debug.append(QString("\n  frames: %1").arg(XAnimDynamicIndicesToString(aAnimPartTransFrames.indices)));

    debug.append("\n)");

    return debug;
}

QDataStream &operator<<(QDataStream &aDataStream, const XAnimPartTransData &aAnimPartTransDataIn)
{
    aDataStream
        << aAnimPartTransDataIn.frames;

    for (int i = 0; i < 3; i++) {
        aDataStream << aAnimPartTransDataIn.frame0[i];
    }

    return aDataStream;
}

QDataStream &operator>>(QDataStream &aDataStream, XAnimPartTransData &aAnimPartTransDataOut)
{
    aDataStream
        >> aAnimPartTransDataOut.frames;

    for (int i = 0; i < 3; i++) {
        aDataStream >> aAnimPartTransDataOut.frame0[i];
    }

    return aDataStream;
}
QString XAnimPartTransDataToString(const XAnimPartTransData &aAnimPartTransData) {
    QString debug;

    debug.append("XAnimPartTransData(");

    debug.append(QString("\n  frames: %1").arg(XAnimPartTransFramesToString(aAnimPartTransData.frames)));

    for (int i = 0; i < 3; i++) {
        debug.append(QString("\n  smallTrans: %1").arg(aAnimPartTransData.frame0[i]));
    }
    debug.append("\n)");

    return debug;
}

QDataStream &operator<<(QDataStream &aDataStream, const XAnimPartTrans &aAnimPartTransIn)
{
    aDataStream
        << aAnimPartTransIn.size
        << aAnimPartTransIn.smallTrans
        << aAnimPartTransIn.data;

    return aDataStream;
}

QDataStream &operator>>(QDataStream &aDataStream, XAnimPartTrans &aAnimPartTransOut)
{
    aDataStream
        >> aAnimPartTransOut.size
        >> aAnimPartTransOut.smallTrans
        >> aAnimPartTransOut.data;

    return aDataStream;
}
QString XAnimPartTransToString(const XAnimPartTrans &aAnimPartTrans) {
    QString debug;

    debug.append("XAnimPartTrans(");

    debug.append(QString("\n  size: %1").arg(aAnimPartTrans.size));
    debug.append(QString("\n  smallTrans: %1").arg(aAnimPartTrans.smallTrans));
    debug.append(QString("\n  data: %1").arg(XAnimPartTransDataToString(aAnimPartTrans.data)));
    debug.append("\n)");

    return debug;
}

QDataStream &operator<<(QDataStream &aDataStream, const XAnimDeltaPartQuatDataFrames &aAnimDeltaPartQuatDataFramesIn)
{
    aDataStream
        << aAnimDeltaPartQuatDataFramesIn.framesPtr
        << aAnimDeltaPartQuatDataFramesIn.indices;

    return aDataStream;
}

QDataStream &operator>>(QDataStream &aDataStream, XAnimDeltaPartQuatDataFrames &aAnimDeltaPartQuatDataFramesOut)
{
    aDataStream
        >> aAnimDeltaPartQuatDataFramesOut.framesPtr
        >> aAnimDeltaPartQuatDataFramesOut.indices;

    return aDataStream;
}
QString XAnimDeltaPartQuatDataFramesToString(const XAnimDeltaPartQuatDataFrames &aAnimDeltaPartQuatDataFrames) {
    QString debug;

    debug.append("XAnimDeltaPartQuatDataFrames(");

    debug.append(QString("\n  framesPtr: %1").arg(aAnimDeltaPartQuatDataFrames.framesPtr));
    for (int i = 0; i < 2; i++) {
        debug.append(QString("\n  frames %1: %2").arg(i).arg(aAnimDeltaPartQuatDataFrames.frames[i]));
    }
    debug.append(QString("\n  indices: %1").arg(XAnimDynamicIndicesToString(aAnimDeltaPartQuatDataFrames.indices)));
    debug.append("\n)");

    return debug;
}

QDataStream &operator<<(QDataStream &aDataStream, const XAnimDeltaPartQuatData &aAnimDeltaPartQuatDataIn)
{
    aDataStream
        << aAnimDeltaPartQuatDataIn.frames;

    for (int i = 0; i < 2; i++) {
        aDataStream << aAnimDeltaPartQuatDataIn.frame0[i];
    }

    return aDataStream;
}

QDataStream &operator>>(QDataStream &aDataStream, XAnimDeltaPartQuatData &aAnimDeltaPartQuatDataOut)
{
    aDataStream
        >> aAnimDeltaPartQuatDataOut.frames;

    for (int i = 0; i < 2; i++) {
        aDataStream >> aAnimDeltaPartQuatDataOut.frame0[i];
    }

    return aDataStream;
}
QString XAnimDeltaPartQuatDataToString(const XAnimDeltaPartQuatData &aAnimDeltaPartQuatData) {
    QString debug;

    debug.append("XAnimDeltaPartQuatData(");

    debug.append(QString("\n  frames: %1").arg(XAnimDeltaPartQuatDataFramesToString(aAnimDeltaPartQuatData.frames)));
    for (int i = 0; i < 2; i++) {
        debug.append(QString("\n  frame0 %1: %2").arg(i).arg(aAnimDeltaPartQuatData.frame0[i]));
    }
    debug.append("\n)");

    return debug;
}

QDataStream &operator<<(QDataStream &aDataStream, const XAnimDeltaPartQuat &aAnimDeltaPartQuatIn)
{
    aDataStream
        << aAnimDeltaPartQuatIn.size
        << aAnimDeltaPartQuatIn.data;

    return aDataStream;
}

QDataStream &operator>>(QDataStream &aDataStream, XAnimDeltaPartQuat &aAnimDeltaPartQuatOut)
{
    aDataStream
        >> aAnimDeltaPartQuatOut.size
        >> aAnimDeltaPartQuatOut.data;

    return aDataStream;
}
QString XAnimDeltaPartQuatToString(const XAnimDeltaPartQuat &aAnimDeltaPartQuat) {
    QString debug;

    debug.append("XAnimDeltaPartQuat(");

    debug.append(QString("\n  size: %1").arg(aAnimDeltaPartQuat.size));
    debug.append(QString("\n  data: %1").arg(XAnimDeltaPartQuatDataToString(aAnimDeltaPartQuat.data)));
    debug.append("\n)");

    return debug;
}

QDataStream &operator<<(QDataStream &aDataStream, const XAnimDeltaPart &aAnimDeltaPartIn)
{
    aDataStream
        << aAnimDeltaPartIn.transPtr
        << aAnimDeltaPartIn.quatPtr;

    return aDataStream;
}

QDataStream &operator>>(QDataStream &aDataStream, XAnimDeltaPart &aAnimDeltaPartOut)
{
    aDataStream
        >> aAnimDeltaPartOut.transPtr
        >> aAnimDeltaPartOut.quatPtr;

    return aDataStream;
}
QString XAnimDeltaPartToString(const XAnimDeltaPart &aAnimDeltaPart) {
    QString debug;

    debug.append("XAnimDeltaPart(");

    debug.append(QString("\n  transPtr: %1").arg(aAnimDeltaPart.transPtr));
    debug.append(QString("\n  trans: %1").arg(XAnimPartTransToString(aAnimDeltaPart.trans)));
    debug.append(QString("\n  quatPtr: %1").arg(aAnimDeltaPart.quatPtr));
    debug.append(QString("\n  quat: %1").arg(XAnimDeltaPartQuatToString(aAnimDeltaPart.quat)));
    debug.append("\n)");

    return debug;
}

QDataStream &operator<<(QDataStream &aDataStream, const XAnimNotifyInfo &aAnimNotifyInfoIn)
{
    aDataStream
        << aAnimNotifyInfoIn.name
        << aAnimNotifyInfoIn.time;

    return aDataStream;
}
QDebug operator<<(QDebug debug, const XAnimNotifyInfo &aAnimNotifyInfo) {
    QDebugStateSaver saver(debug);
    debug.noquote().nospace();

    debug << "XAnimNotifyInfo(";

    debug << "\n  name: " << aAnimNotifyInfo.name;
    debug << "\n  time: " << aAnimNotifyInfo.time;
    debug << "\n)";

    return debug;
}
QString XAnimNotifyInfoToString(const XAnimNotifyInfo &aAnimNotifyInfo) {
    QString debug;

    debug.append("XAnimNotifyInfo(");

    debug.append(QString("\n  name: %1").arg(aAnimNotifyInfo.name));
    debug.append(QString("\n  time: %1").arg(aAnimNotifyInfo.time));
    debug.append("\n)");

    return debug;
}

QDataStream &operator>>(QDataStream &aDataStream, XAnimNotifyInfo &aAnimNotifyInfoOut)
{
    aDataStream
        >> aAnimNotifyInfoOut.name
        >> aAnimNotifyInfoOut.time;

    return aDataStream;
}

QDataStream &operator<<(QDataStream &aDataStream, const XAnimIndices &aAnimIndicesIn)
{
    aDataStream
        << aAnimIndicesIn.indexPtr
        << aAnimIndicesIn.index;

    return aDataStream;
}
QDebug operator<<(QDebug debug, const XAnimIndices &aAnimIndices) {
    QDebugStateSaver saver(debug);
    debug.noquote().nospace();

    debug << "XAnimIndices(";

    debug << "\n  name: " << aAnimIndices.indexPtr;
    debug << "\n  namePtr: " << aAnimIndices.index;
    debug << "\n)";

    return debug;
}
QString XAnimIndicesToString(const XAnimIndices &aAnimIndices) {
    QString debug;

    debug.append("XAnimIndices(");

    debug.append(QString("\n  name: %1").arg(aAnimIndices.indexPtr));
    debug.append(QString("\n  namePtr: %1").arg(aAnimIndices.index));
    debug.append("\n)");

    return debug;
}

QDataStream &operator>>(QDataStream &aDataStream, XAnimIndices &aAnimIndicesOut)
{
    aDataStream
        >> aAnimIndicesOut.indexPtr
        >> aAnimIndicesOut.index;

    return aDataStream;
}

QDataStream &operator<<(QDataStream &aDataStream, const XAnimParts &aAnimPartIn)
{
    aDataStream
        << aAnimPartIn.name
        << aAnimPartIn.dataByteCount
        << aAnimPartIn.dataShortCount
        << aAnimPartIn.dataIntCount
        << aAnimPartIn.randomDataByteCount
        << aAnimPartIn.randomDataIntCount
        << aAnimPartIn.numframes
        << aAnimPartIn.bLoop
        << aAnimPartIn.bDelta;

    for (int i = 0; i < 12; i++) {
        aDataStream << aAnimPartIn.boneCount[i];
    }

    aDataStream
        << aAnimPartIn.notifyCount
        << aAnimPartIn.pad
        << aAnimPartIn.randomDataShortCount
        << aAnimPartIn.indexCount
        << aAnimPartIn.framerate
        << aAnimPartIn.frequency
        << aAnimPartIn.namesPtr
        << aAnimPartIn.dataBytePtr
        << aAnimPartIn.dataShortPtr
        << aAnimPartIn.dataIntPtr
        << aAnimPartIn.randomDataShortPtr
        << aAnimPartIn.randomDataBytePtr
        << aAnimPartIn.randomDataIntPtr
        << aAnimPartIn.indices
        << aAnimPartIn.notifyPtr
        << aAnimPartIn.deltaPartPtr;

    return aDataStream;
}

quint32 ROL4(quint32 value, int positions) {
    return (value << positions) | (value >> (32 - positions));
}

QDataStream &operator>>(QDataStream &aDataStream, XAnimParts &aAnimPartOut)
{
    aDataStream
        >> aAnimPartOut.namePtr
        >> aAnimPartOut.dataByteCount
        >> aAnimPartOut.dataShortCount
        >> aAnimPartOut.dataIntCount
        >> aAnimPartOut.randomDataByteCount
        >> aAnimPartOut.randomDataIntCount
        >> aAnimPartOut.numframes
        >> aAnimPartOut.bLoop
        >> aAnimPartOut.bDelta;

    for (int i = 0; i < 12; i++) {
        aDataStream >> aAnimPartOut.boneCount[i];
    }

    aDataStream
        >> aAnimPartOut.notifyCount
        >> aAnimPartOut.assetType
        >> aAnimPartOut.pad;

    aDataStream.skipRawData(3);

    aDataStream
        >> aAnimPartOut.randomDataShortCount
        >> aAnimPartOut.indexCount;

    quint32 framerateInt, frequencyInt;
    aDataStream
        >> framerateInt
        >> frequencyInt
        >> aAnimPartOut.namesPtr
        >> aAnimPartOut.dataBytePtr
        >> aAnimPartOut.dataShortPtr
        >> aAnimPartOut.dataIntPtr
        >> aAnimPartOut.randomDataShortPtr
        >> aAnimPartOut.randomDataBytePtr
        >> aAnimPartOut.randomDataIntPtr
        >> aAnimPartOut.indices
        >> aAnimPartOut.notifyPtr
        >> aAnimPartOut.deltaPartPtr;

    aAnimPartOut.framerate = *reinterpret_cast<float*>(&framerateInt);
    aAnimPartOut.frequency = *reinterpret_cast<float*>(&frequencyInt);

    if (aAnimPartOut.namePtr) {
        aAnimPartOut.name = "";

        char animNameChar;
        aDataStream >> animNameChar;
        while (animNameChar != '\0') {
            aAnimPartOut.name += animNameChar;

            aDataStream >> animNameChar;
        }
    }
    if (aAnimPartOut.namesPtr) {
        int nameCount = aAnimPartOut.boneCount[11];
        aDataStream.skipRawData(2 * nameCount);

        for (int i = 0; i < nameCount; i++) {
            aDataStream.skipRawData(2);
        }
    }
    if (aAnimPartOut.notifyPtr) {
        int notifyCount = aAnimPartOut.notifyCount;
        aDataStream.skipRawData(8 * notifyCount);

        for (int i = 0; i < notifyCount; i++) {
            aDataStream.skipRawData(8);
            aDataStream >> aAnimPartOut.notify;
        }
    }
    if (aAnimPartOut.deltaPartPtr) {
        aDataStream >> aAnimPartOut.deltaPart;
        if (aAnimPartOut.deltaPart.transPtr) {
            aDataStream >> aAnimPartOut.deltaPart.trans;

            if (aAnimPartOut.deltaPart.trans.size) {
                aDataStream >> aAnimPartOut.deltaPart.trans.data.frames;
                quint32 size = aAnimPartOut.deltaPart.trans.size;
                quint32 readSize;
                if (aAnimPartOut.numframes >= 0x100) {
                    readSize = 2 * (size + 1);
                } else {
                    readSize = size + 1;
                }
                aDataStream >> aAnimPartOut.deltaPart.trans.data.frames.indices;

                if (aAnimPartOut.deltaPart.trans.smallTrans) {
                    if (aAnimPartOut.deltaPart.trans.data.frames.frames.framesPtr) {
                        aDataStream.skipRawData(3 * (aAnimPartOut.deltaPart.trans.size + 1));
                    }
                } else if (aAnimPartOut.deltaPart.trans.data.frames.frames.framesPtr) {
                    aDataStream.skipRawData(6 * (aAnimPartOut.deltaPart.trans.size + 1));
                }
            }
        }
        if (aAnimPartOut.deltaPart.quatPtr) {
            aDataStream >> aAnimPartOut.deltaPart.quat;

            if (aAnimPartOut.deltaPart.quat.size) {
                aDataStream >> aAnimPartOut.deltaPart.quat.data.frames;
                aDataStream >> aAnimPartOut.deltaPart.quat.data.frames.indices;

                if (aAnimPartOut.deltaPart.quat.data.frames.framesPtr) {
                    aDataStream.skipRawData(4 * (aAnimPartOut.deltaPart.quat.size + 1));
                }
            } else {
                aDataStream >> aAnimPartOut.deltaPart.quat.data;
            }
        }
    }
    if (aAnimPartOut.dataBytePtr) {
        aDataStream.skipRawData(aAnimPartOut.dataByteCount);
    }
    if (aAnimPartOut.dataShortPtr) {
        aDataStream.skipRawData(ROL4(aAnimPartOut.dataShortCount, 1));
    }
    if (aAnimPartOut.dataIntPtr) {
        aDataStream.skipRawData(ROL4(aAnimPartOut.dataIntCount, 2));
    }
    if (aAnimPartOut.randomDataShortPtr) {
        aDataStream.skipRawData(2 * aAnimPartOut.randomDataShortCount);
    }
    if (aAnimPartOut.randomDataBytePtr) {
        aDataStream.skipRawData(2 * aAnimPartOut.randomDataByteCount);
    }
    if (aAnimPartOut.randomDataIntPtr) {
        aDataStream.skipRawData(ROL4(aAnimPartOut.randomDataIntCount, 2));
    }

    qDebug() << aDataStream.device()->pos();

    return aDataStream;
}

QString XAnimPartsToString(const XAnimParts &xAnimParts) {
    QString debug = "";

    debug.append(QString("XAnimParts("));

    debug.append(QString("\n  name: %1").arg(xAnimParts.name));
    debug.append(QString("\n  namePtr: %1").arg(QString::number(xAnimParts.namePtr, 16)));

    debug.append(QString("\n  dataByteCount: %1").arg(xAnimParts.dataByteCount));
    debug.append(QString("\n  dataShortCount: %1").arg(xAnimParts.dataShortCount));
    debug.append(QString("\n  dataIntCount: %1").arg(xAnimParts.dataIntCount));
    debug.append(QString("\n  randomDataByteCount: %1").arg(xAnimParts.randomDataByteCount));
    debug.append(QString("\n  randomDataIntCount: %1").arg(xAnimParts.randomDataIntCount));
    debug.append(QString("\n  numframes: %1").arg(xAnimParts.numframes));

    debug.append(QString("\n  bLoop: %1").arg(xAnimParts.bLoop));
    debug.append(QString("\n  bDelta: %1").arg(xAnimParts.bDelta));

    for (int i = 0; i < 12; i++) {
        debug.append(QString("\n  boneCount %1: %2").arg(i).arg(xAnimParts.boneCount[i]));
    }

    debug.append(QString("\n  notifyCount: %1").arg(xAnimParts.notifyCount));
    debug.append(QString("\n  assetType: %1").arg(xAnimParts.assetType));

    debug.append(QString("\n  pad: %1").arg(xAnimParts.pad));

    debug.append(QString("\n  randomDataShortCount: %1").arg(xAnimParts.randomDataShortCount));
    debug.append(QString("\n  indexCount: %1").arg(xAnimParts.indexCount));

    debug.append(QString("\n  framerate: %1").arg(xAnimParts.framerate));
    debug.append(QString("\n  frequency: %1").arg(xAnimParts.frequency));

    debug.append(QString("\n  namesPtr: %1").arg(QString::number(xAnimParts.namesPtr, 16)));
    debug.append(QString("\n  names: %1").arg(xAnimParts.names));

    debug.append(QString("\n  dataBytePtr: %1").arg(QString::number(xAnimParts.dataBytePtr, 16)));
    debug.append(QString("\n  dataByte: %1").arg(xAnimParts.dataByte));

    debug.append(QString("\n  dataShortPtr: %1").arg(QString::number(xAnimParts.dataShortPtr, 16)));
    debug.append(QString("\n  dataShort: %1").arg(xAnimParts.dataShort));

    debug.append(QString("\n  dataIntPtr: %1").arg(QString::number(xAnimParts.dataIntPtr, 16)));
    debug.append(QString("\n  dataInt: %1").arg(xAnimParts.dataInt));

    debug.append(QString("\n  randomDataShortPtr: %1").arg(QString::number(xAnimParts.randomDataShortPtr, 16)));
    debug.append(QString("\n  randomDataShort: %1").arg(xAnimParts.randomDataShort));

    debug.append(QString("\n  randomDataBytePtr: %1").arg(QString::number(xAnimParts.randomDataBytePtr, 16)));
    debug.append(QString("\n  randomDataByte: %1").arg(xAnimParts.randomDataByte));

    debug.append(QString("\n  randomDataIntPtr: %1").arg(QString::number(xAnimParts.randomDataIntPtr, 16)));
    debug.append(QString("\n  randomDataInt: %1").arg(xAnimParts.randomDataInt));

    debug.append(QString("\n  indices: %1").arg(XAnimIndicesToString(xAnimParts.indices)));

    debug.append(QString("\n  notifyPtr: %1").arg(QString::number(xAnimParts.notifyPtr, 16)));
    debug.append(QString("\n  notify: %1").arg(XAnimNotifyInfoToString(xAnimParts.notify)));

    debug.append(QString("\n  deltaPartPtr: %1").arg(QString::number(xAnimParts.deltaPartPtr, 16)));
    debug.append(QString("\n  deltaPart: %1").arg(XAnimDeltaPartToString(xAnimParts.deltaPart)));

    debug.append(QString("\n)"));

    return debug;
}

QDebug operator<<(QDebug debug, const XAnimParts &xanimParts) {
    QDebugStateSaver saver(debug);
    debug.noquote().nospace();

    debug << XAnimPartsToString(xanimParts);

    return debug;
}



