/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

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/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#include "hsKeys.h"
#include "hsStream.h"

const int hsKeyFrame::kMaxFrameNumber = 65535;

///////////////////////////////////////////////////////////////

void hsPoint3Key::Read(hsStream *stream)
{
    fFrame = stream->ReadSwap16();
    fValue.Read(stream);
}

void hsPoint3Key::Write(hsStream *stream)
{
    stream->WriteSwap16(fFrame);
    fValue.Write(stream);
}

hsBool hsPoint3Key::CompareValue(hsPoint3Key *key)
{
    return hsABS(fValue.fX - key->fValue.fX) < .01 &&
           hsABS(fValue.fY - key->fValue.fY) < .01 &&
           hsABS(fValue.fZ - key->fValue.fZ) < .01;
}

void hsBezPoint3Key::Read(hsStream *stream)
{
    fFrame = stream->ReadSwap16();
    fInTan.Read(stream);
    fOutTan.Read(stream);
    fValue.Read(stream);
}

void hsBezPoint3Key::Write(hsStream *stream)
{
    stream->WriteSwap16(fFrame);
    fInTan.Write(stream);
    fOutTan.Write(stream);
    fValue.Write(stream);
}

hsBool hsBezPoint3Key::CompareValue(hsBezPoint3Key *key)
{
    return hsABS(fValue.fX - key->fValue.fX) < .01 &&
           hsABS(fValue.fY - key->fValue.fY) < .01 &&
           hsABS(fValue.fZ - key->fValue.fZ) < .01;
}

/////////////////////////////////////////

void hsScalarKey::Read(hsStream *stream)
{
    fFrame = stream->ReadSwap16();
    fValue = stream->ReadSwapScalar();
}

void hsScalarKey::Write(hsStream *stream)
{
    stream->WriteSwap16(fFrame);
    stream->WriteSwapScalar(fValue);
}

hsBool hsScalarKey::CompareValue(hsScalarKey *key)
{
    return fValue == key->fValue;
}

void hsBezScalarKey::Read(hsStream *stream)
{
    fFrame = stream->ReadSwap16();
    fInTan  = stream->ReadSwapScalar();
    fOutTan = stream->ReadSwapScalar();
    fValue  = stream->ReadSwapScalar();
}

void hsBezScalarKey::Write(hsStream *stream)
{
    stream->WriteSwap16(fFrame);
    stream->WriteSwapScalar(fInTan);
    stream->WriteSwapScalar(fOutTan);
    stream->WriteSwapScalar(fValue);
}

hsBool hsBezScalarKey::CompareValue(hsBezScalarKey *key)
{
    return fValue == key->fValue;
}

/////////////////////////////////////////

void hsQuatKey::Read(hsStream *stream)
{
    fFrame = stream->ReadSwap16();
    fValue.Read(stream);
}

void hsQuatKey::Write(hsStream *stream)
{
    stream->WriteSwap16(fFrame);
    fValue.Write(stream);
}

hsBool hsQuatKey::CompareValue(hsQuatKey *key)
{
    return fValue == key->fValue;
}

//////////////////////////////////////////////////////////////////////////////

const hsScalar hsCompressedQuatKey32::kOneOverRootTwo = 0.70710678;
const hsScalar hsCompressedQuatKey32::k10BitScaleRange = 1023 / (2 * kOneOverRootTwo);

void hsCompressedQuatKey32::Read(hsStream *stream)
{
    fFrame = stream->ReadSwap16();
    fData = stream->ReadSwap32();
}

void hsCompressedQuatKey32::Write(hsStream *stream)
{
    stream->WriteSwap16(fFrame);
    stream->WriteSwap32(fData);
}

hsBool hsCompressedQuatKey32::CompareValue(hsCompressedQuatKey32 *key)
{
    return fData == key->fData;
}

// To store a quat in 32 bits, we find which element is the largest and use 2 bits to
// store which one it is. We now know the other 3 elements fall in the range
// of [-kOneOverRootTwo, kOneOverRootTwo]. We scale that range across 10 bits
// and store each. When extracting, we use the fact that the quat was normalized 
// to compute the 4th element.
void hsCompressedQuatKey32::SetQuat(hsQuat &q)
{
    q.Normalize();
    UInt32 maxElement = kCompQuatNukeX;
    hsScalar maxVal = hsABS(q.fX);
    if (hsABS(q.fY) > maxVal)
    {
        maxElement = kCompQuatNukeY;
        maxVal = hsABS(q.fY);
    }
    if (hsABS(q.fZ) > maxVal)
    {
        maxElement = kCompQuatNukeZ;
        maxVal = hsABS(q.fZ);
    }
    if (hsABS(q.fW) > maxVal)
    {
        maxElement = kCompQuatNukeW;
        maxVal = hsABS(q.fW);
    }
    switch (maxElement)
    {
    case kCompQuatNukeX:
        {
            // Invert the quat so that the largest element is positive.
            // We need to do this so that later we know to use the positive root.
            if (q.fX < 0)
                q = -q;

            fData = (maxElement << 30) |
                (((UInt32)(k10BitScaleRange * (q.fY + kOneOverRootTwo))) << 20) |
                (((UInt32)(k10BitScaleRange * (q.fZ + kOneOverRootTwo))) << 10) |
                (((UInt32)(k10BitScaleRange * (q.fW + kOneOverRootTwo))));
            break;
        }
    case kCompQuatNukeY:
        {
            if (q.fY < 0)
                q = -q;

            fData = (maxElement << 30) |
                (((UInt32)(k10BitScaleRange * (q.fX + kOneOverRootTwo))) << 20) |
                (((UInt32)(k10BitScaleRange * (q.fZ + kOneOverRootTwo))) << 10) |
                (((UInt32)(k10BitScaleRange * (q.fW + kOneOverRootTwo))));
            break;
        }
    case kCompQuatNukeZ:
        {
            if (q.fZ < 0)
                q = -q;

            fData = (maxElement << 30) |
                (((UInt32)(k10BitScaleRange * (q.fX + kOneOverRootTwo))) << 20) |
                (((UInt32)(k10BitScaleRange * (q.fY + kOneOverRootTwo))) << 10) |
                (((UInt32)(k10BitScaleRange * (q.fW + kOneOverRootTwo))));
            break;
        }
    case kCompQuatNukeW:
    default:
        {
            if (q.fW < 0)
                q = -q;

            fData = (maxElement << 30) |
                (((UInt32)(k10BitScaleRange * (q.fX + kOneOverRootTwo))) << 20) |
                (((UInt32)(k10BitScaleRange * (q.fY + kOneOverRootTwo))) << 10) |
                (((UInt32)(k10BitScaleRange * (q.fZ + kOneOverRootTwo))));
            break;
        }
    }
}

void hsCompressedQuatKey32::GetQuat(hsQuat &q)
{
    UInt32 maxElement = fData >> 30;
    switch (maxElement)
    {
    case kCompQuatNukeX:
        {
            q.fY = (fData >> 20 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
            q.fZ = (fData >> 10 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
            q.fW = (fData & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
            q.fX = hsSquareRoot(1 - q.fY * q.fY - q.fZ * q.fZ - q.fW *q.fW);
            break;
        }
    case kCompQuatNukeY:
        {
            q.fX = (fData >> 20 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
            q.fZ = (fData >> 10 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
            q.fW = (fData & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
            q.fY = hsSquareRoot(1 - q.fX * q.fX - q.fZ * q.fZ - q.fW *q.fW);
            break;
        }
    case kCompQuatNukeZ:
        {
            q.fX = (fData >> 20 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
            q.fY = (fData >> 10 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
            q.fW = (fData & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
            q.fZ = hsSquareRoot(1 - q.fX * q.fX - q.fY * q.fY - q.fW *q.fW);
            break;
        }
    case kCompQuatNukeW:
    default:
        {
            q.fX = (fData >> 20 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
            q.fY = (fData >> 10 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
            q.fZ = (fData & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
            q.fW = hsSquareRoot(1 - q.fX * q.fX - q.fY * q.fY - q.fZ * q.fZ);
            break;
        }
    }
}

/////////////////////////////////////////////////////////////////////////////

const hsScalar hsCompressedQuatKey64::kOneOverRootTwo = 0.70710678;
const hsScalar hsCompressedQuatKey64::k20BitScaleRange = 1048575 / (2 * kOneOverRootTwo);
const hsScalar hsCompressedQuatKey64::k21BitScaleRange = 2097151 / (2 * kOneOverRootTwo);

void hsCompressedQuatKey64::Read(hsStream *stream)
{
    fFrame = stream->ReadSwap16();
    fData[0] = stream->ReadSwap32();
    fData[1] = stream->ReadSwap32();
}

void hsCompressedQuatKey64::Write(hsStream *stream)
{
    stream->WriteSwap16(fFrame);
    stream->WriteSwap32(fData[0]);
    stream->WriteSwap32(fData[1]);
}

hsBool hsCompressedQuatKey64::CompareValue(hsCompressedQuatKey64 *key)
{
    return (fData[0] == key->fData[0]) && (fData[1] == key->fData[1]);
}

// To store a quat in 64 bits, we find which element is the largest and use 2 bits to
// store which one it is. We now know the other 3 elements fall in the range
// of [-kOneOverRootTwo, kOneOverRootTwo]. We scale that range across 20/21/21 bits
// and store each. When extracting, we use the fact that the quat was normalized 
// to compute the 4th element.
void hsCompressedQuatKey64::SetQuat(hsQuat &q)
{
    q.Normalize();
    UInt32 maxElement = kCompQuatNukeX;
    hsScalar maxVal = hsABS(q.fX);
    if (hsABS(q.fY) > maxVal)
    {
        maxElement = kCompQuatNukeY;
        maxVal = hsABS(q.fY);
    }
    if (hsABS(q.fZ) > maxVal)
    {
        maxElement = kCompQuatNukeZ;
        maxVal = hsABS(q.fZ);
    }
    if (hsABS(q.fW) > maxVal)
    {
        maxElement = kCompQuatNukeW;
        maxVal = hsABS(q.fW);
    }
    switch (maxElement)
    {
    case kCompQuatNukeX:
        {
            // Invert the quat so that the largest element is positive.
            // We need to do this so that later we know to use the positive root.
            if (q.fX < 0)
                q = -q;

            fData[0] = (maxElement << 30) |
                (((UInt32)(k20BitScaleRange * (q.fY + kOneOverRootTwo))) << 10) |
                (((UInt32)(k21BitScaleRange * (q.fZ + kOneOverRootTwo))) >> 11);
            fData[1] =
                (((UInt32)(k21BitScaleRange * (q.fZ + kOneOverRootTwo))) << 21) |
                (((UInt32)(k21BitScaleRange * (q.fW + kOneOverRootTwo))));
            break;
        }
    case kCompQuatNukeY:
        {
            if (q.fY < 0)
                q = -q;

            fData[0] = (maxElement << 30) |
                (((UInt32)(k20BitScaleRange * (q.fX + kOneOverRootTwo))) << 10) |
                (((UInt32)(k21BitScaleRange * (q.fZ + kOneOverRootTwo))) >> 11);
            fData[1] =
                (((UInt32)(k21BitScaleRange * (q.fZ + kOneOverRootTwo))) << 21) |
                (((UInt32)(k21BitScaleRange * (q.fW + kOneOverRootTwo))));
            break;
        }
    case kCompQuatNukeZ:
        {
            if (q.fZ < 0)
                q = -q;

            fData[0] = (maxElement << 30) |
                (((UInt32)(k20BitScaleRange * (q.fX + kOneOverRootTwo))) << 10) |
                (((UInt32)(k21BitScaleRange * (q.fY + kOneOverRootTwo))) >> 11);
            fData[1] =
                (((UInt32)(k21BitScaleRange * (q.fY + kOneOverRootTwo))) << 21) |
                (((UInt32)(k21BitScaleRange * (q.fW + kOneOverRootTwo))));
            break;
        }
    case kCompQuatNukeW:
    default:
        {
            if (q.fW < 0)
                q = -q;

            fData[0] = (maxElement << 30) |
                (((UInt32)(k20BitScaleRange * (q.fX + kOneOverRootTwo))) << 10) |
                (((UInt32)(k21BitScaleRange * (q.fY + kOneOverRootTwo))) >> 11);
            fData[1] =
                (((UInt32)(k21BitScaleRange * (q.fY + kOneOverRootTwo))) << 21) |
                (((UInt32)(k21BitScaleRange * (q.fZ + kOneOverRootTwo))));
            break;
        }
    }
}

void hsCompressedQuatKey64::GetQuat(hsQuat &q)
{
    UInt32 maxElement = fData[0] >> 30;
    switch (maxElement)
    {
    case kCompQuatNukeX:
        {
            q.fY = ((fData[0] >> 10) & 0x000fffff) / k20BitScaleRange - kOneOverRootTwo;
            q.fZ = (((fData[0] & 0x000003ff) << 11) | (fData[1] >> 21)) / k21BitScaleRange - kOneOverRootTwo;
            q.fW = (fData[1] & 0x001fffff) / k21BitScaleRange - kOneOverRootTwo;
            q.fX = hsSquareRoot(1 - q.fY * q.fY - q.fZ * q.fZ - q.fW *q.fW);
            break;
        }
    case kCompQuatNukeY:
        {
            q.fX = ((fData[0] >> 10) & 0x000fffff) / k20BitScaleRange - kOneOverRootTwo;
            q.fZ = (((fData[0] & 0x000003ff) << 11) | (fData[1] >> 21)) / k21BitScaleRange - kOneOverRootTwo;
            q.fW = (fData[1] & 0x001fffff) / k21BitScaleRange - kOneOverRootTwo;
            q.fY = hsSquareRoot(1 - q.fX * q.fX - q.fZ * q.fZ - q.fW *q.fW);
            break;
        }
    case kCompQuatNukeZ:
        {
            q.fX = ((fData[0] >> 10) & 0x000fffff) / k20BitScaleRange - kOneOverRootTwo;
            q.fY = (((fData[0] & 0x000003ff) << 11) | (fData[1] >> 21)) / k21BitScaleRange - kOneOverRootTwo;
            q.fW = (fData[1] & 0x001fffff) / k21BitScaleRange - kOneOverRootTwo;
            q.fZ = hsSquareRoot(1 - q.fX * q.fX - q.fY * q.fY - q.fW *q.fW);
            break;
        }
    case kCompQuatNukeW:
    default:
        {
            q.fX = ((fData[0] >> 10) & 0x000fffff) / k20BitScaleRange - kOneOverRootTwo;
            q.fY = (((fData[0] & 0x000003ff) << 11) | (fData[1] >> 21)) / k21BitScaleRange - kOneOverRootTwo;
            q.fZ = (fData[1] & 0x001fffff) / k21BitScaleRange - kOneOverRootTwo;
            q.fW = hsSquareRoot(1 - q.fX * q.fX - q.fY * q.fY - q.fZ * q.fZ);
            break;
        }
    }
}

/////////////////////////////////////////
// Not a key
//
void hsScaleValue::Read(hsStream *stream)
{
    fS.Read(stream);
    fQ.Read(stream);
}

void hsScaleValue::Write(hsStream *stream)
{
    fS.Write(stream);
    fQ.Write(stream);
}

/////////////////////////////////////////
void hsScaleKey::Read(hsStream *stream)
{
    fFrame = stream->ReadSwap16();
    fValue.Read(stream);
}

void hsScaleKey::Write(hsStream *stream)
{
    stream->WriteSwap16(fFrame);
    fValue.Write(stream);
}

hsBool hsScaleKey::CompareValue(hsScaleKey *key)
{
    return fValue == key->fValue;
}

void hsBezScaleKey::Read(hsStream *stream)
{
    fFrame = stream->ReadSwap16();
    fInTan.Read(stream);
    fOutTan.Read(stream);
    fValue.Read(stream);
}

void hsBezScaleKey::Write(hsStream *stream)
{
    stream->WriteSwap16(fFrame);
    fInTan.Write(stream);
    fOutTan.Write(stream);
    fValue.Write(stream);
}

hsBool hsBezScaleKey::CompareValue(hsBezScaleKey *key)
{
    return fValue == key->fValue;
}

//////////////////////

void hsG3DSMaxKeyFrame::Set(hsMatrix44 *mat, UInt16 frame)
{
    fFrame = frame;
    gemAffineParts parts;
    decomp_affine(mat->fMap, &parts);
    AP_SET(fParts, parts);
}

void hsG3DSMaxKeyFrame::Set(const hsAffineParts &parts, UInt16 frame)
{
    fFrame = frame;
    fParts = parts;
}

void hsG3DSMaxKeyFrame::Read(hsStream *stream)
{
    fFrame = stream->ReadSwap16();
    fParts.Read(stream);
}

void hsG3DSMaxKeyFrame::Write(hsStream *stream)
{
    stream->WriteSwap16(fFrame);
    fParts.Write(stream);
}

hsBool hsG3DSMaxKeyFrame::CompareValue(hsG3DSMaxKeyFrame *key)
{
    return fParts == key->fParts;
}

/////////////////////////////////////////

void hsMatrix33Key::Read(hsStream *stream)
{
    fFrame = stream->ReadSwap16();
    Int32 i,j;
    for(i=0;i<3;i++)
        for(j=0;j<3;j++)
            fValue.fMap[j][i] = stream->ReadSwapScalar();
}

void hsMatrix33Key::Write(hsStream *stream)
{
    stream->WriteSwap16(fFrame);
    Int32 i,j;
    for(i=0;i<3;i++)
        for(j=0;j<3;j++)
            stream->WriteSwapScalar(fValue.fMap[j][i]);
}

hsBool hsMatrix33Key::CompareValue(hsMatrix33Key *key)
{
    return fValue == key->fValue;
}

/////////////////////////////////////////

void hsMatrix44Key::Read(hsStream *stream)
{
    fFrame = stream->ReadSwap16();
    fValue.Read(stream);
}

void hsMatrix44Key::Write(hsStream *stream)
{
    stream->WriteSwap16(fFrame);
    fValue.Write(stream);
}

hsBool hsMatrix44Key::CompareValue(hsMatrix44Key *key)
{
    return fValue == key->fValue;
}