/**
 * @file llbvhloader.h
 * @brief Translates a BVH files to LindenLabAnimation format.
 *
 * $LicenseInfo:firstyear=2004&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#ifndef LL_LLBVHLOADER_H
#define LL_LLBVHLOADER_H

#include "v3math.h"
#include "m3math.h"
#include "llmath.h"
#include "llapr.h"
#include "llbvhconsts.h"

const S32 BVH_PARSER_LINE_SIZE = 2048;
class LLDataPacker;

//------------------------------------------------------------------------
// FileCloser
//------------------------------------------------------------------------
class FileCloser
{
public:
    FileCloser( apr_file_t *file )
    {
        mFile = file;
    }

    ~FileCloser()
    {
        apr_file_close(mFile);
    }
protected:
    apr_file_t* mFile;
};


//------------------------------------------------------------------------
// Key
//------------------------------------------------------------------------
struct Key
{
    Key()
    {
        mPos[0] = mPos[1] = mPos[2] = 0.0f;
        mRot[0] = mRot[1] = mRot[2] = 0.0f;
        mIgnorePos = false;
        mIgnoreRot = false;
    }

    F32 mPos[3];
    F32 mRot[3];
    bool    mIgnorePos;
    bool    mIgnoreRot;
};


//------------------------------------------------------------------------
// KeyVector
//------------------------------------------------------------------------
typedef  std::vector<Key> KeyVector;

//------------------------------------------------------------------------
// Joint
//------------------------------------------------------------------------
struct Joint
{
    Joint(const char *name)
    {
        mName = name;
        mIgnore = false;
        mIgnorePositions = false;
        mRelativePositionKey = false;
        mRelativeRotationKey = false;
        mOutName = name;
        mOrder[0] = 'X';
        mOrder[1] = 'Y';
        mOrder[2] = 'Z';
        mOrder[3] = 0;
        mNumPosKeys = 0;
        mNumRotKeys = 0;
        mChildTreeMaxDepth = 0;
        mPriority = 0;
        mNumChannels = 3;
    }

    // Include aligned members first
    LLMatrix3       mFrameMatrix;
    LLMatrix3       mOffsetMatrix;
    LLVector3       mRelativePosition;
    //
    std::string     mName;
    bool            mIgnore;
    bool            mIgnorePositions;
    bool            mRelativePositionKey;
    bool            mRelativeRotationKey;
    std::string     mOutName;
    std::string     mMergeParentName;
    std::string     mMergeChildName;
    char            mOrder[4];          /* Flawfinder: ignore */
    KeyVector       mKeys;
    S32             mNumPosKeys;
    S32             mNumRotKeys;
    S32             mChildTreeMaxDepth;
    S32             mPriority;
    S32             mNumChannels;
};


struct Constraint
{
    char            mSourceJointName[16];       /* Flawfinder: ignore */
    char            mTargetJointName[16];       /* Flawfinder: ignore */
    S32             mChainLength;
    LLVector3       mSourceOffset;
    LLVector3       mTargetOffset;
    LLVector3       mTargetDir;
    F32             mEaseInStart;
    F32             mEaseInStop;
    F32             mEaseOutStart;
    F32             mEaseOutStop;
    EConstraintType mConstraintType;
};

//------------------------------------------------------------------------
// JointVector
//------------------------------------------------------------------------
typedef std::vector<Joint*> JointVector;

//------------------------------------------------------------------------
// ConstraintVector
//------------------------------------------------------------------------
typedef std::vector<Constraint> ConstraintVector;

//------------------------------------------------------------------------
// Translation
//------------------------------------------------------------------------
class Translation
{
public:
    Translation()
    {
        mIgnore = false;
        mIgnorePositions = false;
        mRelativePositionKey = false;
        mRelativeRotationKey = false;
        mPriorityModifier = 0;
    }

    std::string mOutName;
    bool        mIgnore;
    bool        mIgnorePositions;
    bool        mRelativePositionKey;
    bool        mRelativeRotationKey;
    LLMatrix3   mFrameMatrix;
    LLMatrix3   mOffsetMatrix;
    LLVector3   mRelativePosition;
    std::string mMergeParentName;
    std::string mMergeChildName;
    S32         mPriorityModifier;
};

typedef enum e_load_status
    {
        E_ST_OK,
        E_ST_EOF,
        E_ST_NO_CONSTRAINT,
        E_ST_NO_FILE,
        E_ST_NO_HIER,
        E_ST_NO_JOINT,
        E_ST_NO_NAME,
        E_ST_NO_OFFSET,
        E_ST_NO_CHANNELS,
        E_ST_NO_ROTATION,
        E_ST_NO_AXIS,
        E_ST_NO_MOTION,
        E_ST_NO_FRAMES,
        E_ST_NO_FRAME_TIME,
        E_ST_NO_POS,
        E_ST_NO_ROT,
        E_ST_NO_XLT_FILE,
        E_ST_NO_XLT_HEADER,
        E_ST_NO_XLT_NAME,
        E_ST_NO_XLT_IGNORE,
        E_ST_NO_XLT_RELATIVE,
        E_ST_NO_XLT_OUTNAME,
        E_ST_NO_XLT_MATRIX,
        E_ST_NO_XLT_MERGECHILD,
        E_ST_NO_XLT_MERGEPARENT,
        E_ST_NO_XLT_PRIORITY,
        E_ST_NO_XLT_LOOP,
        E_ST_NO_XLT_EASEIN,
        E_ST_NO_XLT_EASEOUT,
        E_ST_NO_XLT_HAND,
        E_ST_NO_XLT_EMOTE,
        E_ST_BAD_ROOT
    } ELoadStatus;

//------------------------------------------------------------------------
// TranslationMap
//------------------------------------------------------------------------
typedef std::map<std::string, Translation> TranslationMap;

class LLBVHLoader
{
    friend class LLKeyframeMotion;
public:
    // Constructor
    LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &errorLine, std::map<std::string, std::string>& joint_alias_map );
    ~LLBVHLoader();

/*
    // Status Codes
    typedef const char *status_t;
    static const char *ST_OK;
    static const char *ST_EOF;
    static const char *ST_NO_CONSTRAINT;
    static const char *ST_NO_FILE;
    static const char *ST_NO_HIER;
    static const char *ST_NO_JOINT;
    static const char *ST_NO_NAME;
    static const char *ST_NO_OFFSET;
    static const char *ST_NO_CHANNELS;
    static const char *ST_NO_ROTATION;
    static const char *ST_NO_AXIS;
    static const char *ST_NO_MOTION;
    static const char *ST_NO_FRAMES;
    static const char *ST_NO_FRAME_TIME;
    static const char *ST_NO_POS;
    static const char *ST_NO_ROT;
    static const char *ST_NO_XLT_FILE;
    static const char *ST_NO_XLT_HEADER;
    static const char *ST_NO_XLT_NAME;
    static const char *ST_NO_XLT_IGNORE;
    static const char *ST_NO_XLT_RELATIVE;
    static const char *ST_NO_XLT_OUTNAME;
    static const char *ST_NO_XLT_MATRIX;
    static const char *ST_NO_XLT_MERGECHILD;
    static const char *ST_NO_XLT_MERGEPARENT;
    static const char *ST_NO_XLT_PRIORITY;
    static const char *ST_NO_XLT_LOOP;
    static const char *ST_NO_XLT_EASEIN;
    static const char *ST_NO_XLT_EASEOUT;
    static const char *ST_NO_XLT_HAND;
    static const char *ST_NO_XLT_EMOTE;
    static const char *ST_BAD_ROOT;
*/

    // Loads the specified translation table.
    ELoadStatus loadTranslationTable(const char *fileName);

    //Create a new joint alias
    void makeTranslation(std::string key, std::string value);

    // Loads joint aliases from XML file.
    ELoadStatus loadAliases(const char * filename);

    // Load the specified BVH file.
    // Returns status code.
    ELoadStatus loadBVHFile(const char *buffer, char *error_text, S32 &error_line);

    void dumpBVHInfo();

    // Applies translations to BVH data loaded.
    void applyTranslations();

    // Returns the number of lines scanned.
    // Useful for error reporting.
    S32 getLineNumber() { return mLineNumber; }

    // returns required size of output buffer
    U32 getOutputSize();

    // writes contents to datapacker
    bool serialize(LLDataPacker& dp);

    // flags redundant keyframe data
    void optimize();

    void reset();

    F32 getDuration() { return mDuration; }

    bool isInitialized() { return mInitialized; }

    ELoadStatus getStatus() { return mStatus; }

protected:
    // Consumes one line of input from file.
    bool getLine(apr_file_t *fp);

    // parser state
    char        mLine[BVH_PARSER_LINE_SIZE];        /* Flawfinder: ignore */
    S32         mLineNumber;

    // parsed values
    S32                 mNumFrames;
    F32                 mFrameTime;
    JointVector         mJoints;
    ConstraintVector    mConstraints;
    TranslationMap      mTranslations;

    S32                 mPriority;
    bool                mLoop;
    F32                 mLoopInPoint;
    F32                 mLoopOutPoint;
    F32                 mEaseIn;
    F32                 mEaseOut;
    S32                 mHand;
    std::string         mEmoteName;

    bool                mInitialized;
    ELoadStatus         mStatus;

    // computed values
    F32 mDuration;
};

#endif // LL_LLBVHLOADER_H