/**
 * @file lldatapacker.h
 * @brief Data packer declaration for tightly storing binary data.
 *
 * $LicenseInfo:firstyear=2002&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_LLDATAPACKER_H
#define LL_LLDATAPACKER_H

class LLColor4;
class LLColor4U;
class LLVector2;
class LLVector3;
class LLVector4;
class LLUUID;

class LLDataPacker
{
public:
    virtual ~LLDataPacker() {}

    // Not required to override, but error to call?
    virtual void        reset();
    virtual void        dumpBufferToLog();

    virtual bool        hasNext() const = 0;

    virtual bool        packString(const std::string& value, const char *name) = 0;
    virtual bool        unpackString(std::string& value, const char *name) = 0;

    virtual bool        packBinaryData(const U8 *value, S32 size, const char *name) = 0;
    virtual bool        unpackBinaryData(U8 *value, S32 &size, const char *name) = 0;

    // Constant size binary data packing
    virtual bool        packBinaryDataFixed(const U8 *value, S32 size, const char *name) = 0;
    virtual bool        unpackBinaryDataFixed(U8 *value, S32 size, const char *name) = 0;

    virtual bool        packU8(const U8 value, const char *name) = 0;
    virtual bool        unpackU8(U8 &value, const char *name) = 0;

    virtual bool        packU16(const U16 value, const char *name) = 0;
    virtual bool        unpackU16(U16 &value, const char *name) = 0;
    bool                unpackU16s(U16 *value, S32 count, const char *name);

    virtual bool        packS16(const S16 value, const char *name) = 0;
    virtual bool        unpackS16(S16 &value, const char *name) = 0;
    bool                unpackS16s(S16 *value, S32 count, const char *name);

    virtual bool        packU32(const U32 value, const char *name) = 0;
    virtual bool        unpackU32(U32 &value, const char *name) = 0;

    virtual bool        packS32(const S32 value, const char *name) = 0;
    virtual bool        unpackS32(S32 &value, const char *name) = 0;

    virtual bool        packF32(const F32 value, const char *name) = 0;
    virtual bool        unpackF32(F32 &value, const char *name) = 0;
    bool                unpackF32s(F32 *values, S32 count, const char *name);

    // Packs a float into an integer, using the given size
    // and picks the right U* data type to pack into.
    bool                packFixed(const F32 value, const char *name,
                                const bool is_signed, const U32 int_bits, const U32 frac_bits);
    bool                unpackFixed(F32 &value, const char *name,
                                const bool is_signed, const U32 int_bits, const U32 frac_bits);

    virtual bool        packColor4(const LLColor4 &value, const char *name) = 0;
    virtual bool        unpackColor4(LLColor4 &value, const char *name) = 0;

    virtual bool        packColor4U(const LLColor4U &value, const char *name) = 0;
    virtual bool        unpackColor4U(LLColor4U &value, const char *name) = 0;
    bool                unpackColor4Us(LLColor4U *values, S32 count, const char *name);

    virtual bool        packVector2(const LLVector2 &value, const char *name) = 0;
    virtual bool        unpackVector2(LLVector2 &value, const char *name) = 0;

    virtual bool        packVector3(const LLVector3 &value, const char *name) = 0;
    virtual bool        unpackVector3(LLVector3 &value, const char *name) = 0;

    virtual bool        packVector4(const LLVector4 &value, const char *name) = 0;
    virtual bool        unpackVector4(LLVector4 &value, const char *name) = 0;

    virtual bool        packUUID(const LLUUID &value, const char *name) = 0;
    virtual bool        unpackUUID(LLUUID &value, const char *name) = 0;
    bool                unpackUUIDs(LLUUID *values, S32 count, const char *name);
            U32         getPassFlags() const    { return mPassFlags; }
            void        setPassFlags(U32 flags) { mPassFlags = flags; }
protected:
    LLDataPacker();
protected:
    U32 mPassFlags;
    bool mWriteEnabled; // disable this to do things like determine filesize without actually copying data
};

class LLDataPackerBinaryBuffer : public LLDataPacker
{
public:
    LLDataPackerBinaryBuffer(U8 *bufferp, S32 size)
    :   LLDataPacker(),
        mBufferp(bufferp),
        mCurBufferp(bufferp),
        mBufferSize(size)
    {
        mWriteEnabled = true;
    }

    LLDataPackerBinaryBuffer()
    :   LLDataPacker(),
        mBufferp(NULL),
        mCurBufferp(NULL),
        mBufferSize(0)
    {
    }

    /*virtual*/ bool        packString(const std::string& value, const char *name);
    /*virtual*/ bool        unpackString(std::string& value, const char *name);

    /*virtual*/ bool        packBinaryData(const U8 *value, S32 size, const char *name);
    /*virtual*/ bool        unpackBinaryData(U8 *value, S32 &size, const char *name);

    // Constant size binary data packing
    /*virtual*/ bool        packBinaryDataFixed(const U8 *value, S32 size, const char *name);
    /*virtual*/ bool        unpackBinaryDataFixed(U8 *value, S32 size, const char *name);

    /*virtual*/ bool        packU8(const U8 value, const char *name);
    /*virtual*/ bool        unpackU8(U8 &value, const char *name);

    /*virtual*/ bool        packU16(const U16 value, const char *name);
    /*virtual*/ bool        unpackU16(U16 &value, const char *name);

    /*virtual*/ bool        packS16(const S16 value, const char *name);
    /*virtual*/ bool        unpackS16(S16 &value, const char *name);

    /*virtual*/ bool        packU32(const U32 value, const char *name);
    /*virtual*/ bool        unpackU32(U32 &value, const char *name);

    /*virtual*/ bool        packS32(const S32 value, const char *name);
    /*virtual*/ bool        unpackS32(S32 &value, const char *name);

    /*virtual*/ bool        packF32(const F32 value, const char *name);
    /*virtual*/ bool        unpackF32(F32 &value, const char *name);

    /*virtual*/ bool        packColor4(const LLColor4 &value, const char *name);
    /*virtual*/ bool        unpackColor4(LLColor4 &value, const char *name);

    /*virtual*/ bool        packColor4U(const LLColor4U &value, const char *name);
    /*virtual*/ bool        unpackColor4U(LLColor4U &value, const char *name);

    /*virtual*/ bool        packVector2(const LLVector2 &value, const char *name);
    /*virtual*/ bool        unpackVector2(LLVector2 &value, const char *name);

    /*virtual*/ bool        packVector3(const LLVector3 &value, const char *name);
    /*virtual*/ bool        unpackVector3(LLVector3 &value, const char *name);

    /*virtual*/ bool        packVector4(const LLVector4 &value, const char *name);
    /*virtual*/ bool        unpackVector4(LLVector4 &value, const char *name);

    /*virtual*/ bool        packUUID(const LLUUID &value, const char *name);
    /*virtual*/ bool        unpackUUID(LLUUID &value, const char *name);

                S32         getCurrentSize() const  { return (S32)(mCurBufferp - mBufferp); }
                S32         getBufferSize() const   { return mBufferSize; }
                const U8*   getBuffer() const   { return mBufferp; }
                void        reset()             { mCurBufferp = mBufferp; mWriteEnabled = (mCurBufferp != NULL); }
                void        shift(S32 offset)   { reset(); mCurBufferp += offset;}
                void        freeBuffer()        { delete [] mBufferp; mBufferp = mCurBufferp = NULL; mBufferSize = 0; mWriteEnabled = false; }
                void        assignBuffer(U8 *bufferp, S32 size)
                {
                    if(mBufferp && mBufferp != bufferp)
                    {
                        freeBuffer() ;
                    }
                    mBufferp = bufferp;
                    mCurBufferp = bufferp;
                    mBufferSize = size;
                    mWriteEnabled = true;
                }
                const LLDataPackerBinaryBuffer& operator=(const LLDataPackerBinaryBuffer &a);

    /*virtual*/ bool        hasNext() const         { return getCurrentSize() < getBufferSize(); }

    /*virtual*/ void dumpBufferToLog();
protected:
    inline bool verifyLength(const S32 data_size, const char *name);

    U8 *mBufferp;
    U8 *mCurBufferp;
    S32 mBufferSize;
};

inline bool LLDataPackerBinaryBuffer::verifyLength(const S32 data_size, const char *name)
{
    if (mWriteEnabled && (mCurBufferp - mBufferp) > mBufferSize - data_size)
    {
        LL_WARNS() << "Buffer overflow in BinaryBuffer length verify, field name " << name << "!" << LL_ENDL;
        LL_WARNS() << "Current pos: " << (int)(mCurBufferp - mBufferp) << " Buffer size: " << mBufferSize << " Data size: " << data_size << LL_ENDL;
        return false;
    }

    return true;
}

class LLDataPackerAsciiBuffer : public LLDataPacker
{
public:
    LLDataPackerAsciiBuffer(char* bufferp, S32 size)
    {
        mBufferp = bufferp;
        mCurBufferp = bufferp;
        mBufferSize = size;
        mPassFlags = 0;
        mIncludeNames = false;
        mWriteEnabled = true;
    }

    LLDataPackerAsciiBuffer()
    {
        mBufferp = NULL;
        mCurBufferp = NULL;
        mBufferSize = 0;
        mPassFlags = 0;
        mIncludeNames = false;
        mWriteEnabled = false;
    }

    /*virtual*/ bool        packString(const std::string& value, const char *name);
    /*virtual*/ bool        unpackString(std::string& value, const char *name);

    /*virtual*/ bool        packBinaryData(const U8 *value, S32 size, const char *name);
    /*virtual*/ bool        unpackBinaryData(U8 *value, S32 &size, const char *name);

    // Constant size binary data packing
    /*virtual*/ bool        packBinaryDataFixed(const U8 *value, S32 size, const char *name);
    /*virtual*/ bool        unpackBinaryDataFixed(U8 *value, S32 size, const char *name);

    /*virtual*/ bool        packU8(const U8 value, const char *name);
    /*virtual*/ bool        unpackU8(U8 &value, const char *name);

    /*virtual*/ bool        packU16(const U16 value, const char *name);
    /*virtual*/ bool        unpackU16(U16 &value, const char *name);

    /*virtual*/ bool        packS16(const S16 value, const char *name);
    /*virtual*/ bool        unpackS16(S16 &value, const char *name);

    /*virtual*/ bool        packU32(const U32 value, const char *name);
    /*virtual*/ bool        unpackU32(U32 &value, const char *name);

    /*virtual*/ bool        packS32(const S32 value, const char *name);
    /*virtual*/ bool        unpackS32(S32 &value, const char *name);

    /*virtual*/ bool        packF32(const F32 value, const char *name);
    /*virtual*/ bool        unpackF32(F32 &value, const char *name);

    /*virtual*/ bool        packColor4(const LLColor4 &value, const char *name);
    /*virtual*/ bool        unpackColor4(LLColor4 &value, const char *name);

    /*virtual*/ bool        packColor4U(const LLColor4U &value, const char *name);
    /*virtual*/ bool        unpackColor4U(LLColor4U &value, const char *name);

    /*virtual*/ bool        packVector2(const LLVector2 &value, const char *name);
    /*virtual*/ bool        unpackVector2(LLVector2 &value, const char *name);

    /*virtual*/ bool        packVector3(const LLVector3 &value, const char *name);
    /*virtual*/ bool        unpackVector3(LLVector3 &value, const char *name);

    /*virtual*/ bool        packVector4(const LLVector4 &value, const char *name);
    /*virtual*/ bool        unpackVector4(LLVector4 &value, const char *name);

    /*virtual*/ bool        packUUID(const LLUUID &value, const char *name);
    /*virtual*/ bool        unpackUUID(LLUUID &value, const char *name);

    void        setIncludeNames(bool b) { mIncludeNames = b; }

    // Include the trailing NULL so it's always a valid string
    S32         getCurrentSize() const  { return (S32)(mCurBufferp - mBufferp) + 1; }

    S32         getBufferSize() const   { return mBufferSize; }
    /*virtual*/ void        reset()                 { mCurBufferp = mBufferp; mWriteEnabled = (mCurBufferp != NULL); }

    /*virtual*/ bool        hasNext() const         { return getCurrentSize() < getBufferSize(); }

    inline void freeBuffer();
    inline void assignBuffer(char* bufferp, S32 size);
    void        dump();

protected:
    void writeIndentedName(const char *name);
    bool getValueStr(const char *name, char *out_value, const S32 value_len);

protected:
    inline bool verifyLength(const S32 data_size, const char *name);

    char *mBufferp;
    char *mCurBufferp;
    S32 mBufferSize;
    bool mIncludeNames; // useful for debugging, print the name of each field
};

inline void LLDataPackerAsciiBuffer::freeBuffer()
{
    delete [] mBufferp;
    mBufferp = mCurBufferp = NULL;
    mBufferSize = 0;
    mWriteEnabled = false;
}

inline void LLDataPackerAsciiBuffer::assignBuffer(char* bufferp, S32 size)
{
    mBufferp = bufferp;
    mCurBufferp = bufferp;
    mBufferSize = size;
    mWriteEnabled = true;
}

inline bool LLDataPackerAsciiBuffer::verifyLength(const S32 data_size, const char *name)
{
    if (mWriteEnabled && (mCurBufferp - mBufferp) > mBufferSize - data_size)
    {
        LL_WARNS() << "Buffer overflow in AsciiBuffer length verify, field name " << name << "!" << LL_ENDL;
        LL_WARNS() << "Current pos: " << (int)(mCurBufferp - mBufferp) << " Buffer size: " << mBufferSize << " Data size: " << data_size << LL_ENDL;
        return false;
    }

    return true;
}

class LLDataPackerAsciiFile : public LLDataPacker
{
public:
    LLDataPackerAsciiFile(LLFILE *fp, const S32 indent = 2)
    :   LLDataPacker(),
        mIndent(indent),
        mFP(fp),
        mOutputStream(NULL),
        mInputStream(NULL)
    {
    }

    LLDataPackerAsciiFile(std::ostream& output_stream, const S32 indent = 2)
    :   LLDataPacker(),
        mIndent(indent),
        mFP(NULL),
        mOutputStream(&output_stream),
        mInputStream(NULL)
    {
        mWriteEnabled = true;
    }

    LLDataPackerAsciiFile(std::istream& input_stream, const S32 indent = 2)
    :   LLDataPacker(),
        mIndent(indent),
        mFP(NULL),
        mOutputStream(NULL),
        mInputStream(&input_stream)
    {
    }

    /*virtual*/ bool        packString(const std::string& value, const char *name);
    /*virtual*/ bool        unpackString(std::string& value, const char *name);

    /*virtual*/ bool        packBinaryData(const U8 *value, S32 size, const char *name);
    /*virtual*/ bool        unpackBinaryData(U8 *value, S32 &size, const char *name);

    /*virtual*/ bool        packBinaryDataFixed(const U8 *value, S32 size, const char *name);
    /*virtual*/ bool        unpackBinaryDataFixed(U8 *value, S32 size, const char *name);

    /*virtual*/ bool        packU8(const U8 value, const char *name);
    /*virtual*/ bool        unpackU8(U8 &value, const char *name);

    /*virtual*/ bool        packU16(const U16 value, const char *name);
    /*virtual*/ bool        unpackU16(U16 &value, const char *name);

    /*virtual*/ bool        packS16(const S16 value, const char *name);
    /*virtual*/ bool        unpackS16(S16 &value, const char *name);

    /*virtual*/ bool        packU32(const U32 value, const char *name);
    /*virtual*/ bool        unpackU32(U32 &value, const char *name);

    /*virtual*/ bool        packS32(const S32 value, const char *name);
    /*virtual*/ bool        unpackS32(S32 &value, const char *name);

    /*virtual*/ bool        packF32(const F32 value, const char *name);
    /*virtual*/ bool        unpackF32(F32 &value, const char *name);

    /*virtual*/ bool        packColor4(const LLColor4 &value, const char *name);
    /*virtual*/ bool        unpackColor4(LLColor4 &value, const char *name);

    /*virtual*/ bool        packColor4U(const LLColor4U &value, const char *name);
    /*virtual*/ bool        unpackColor4U(LLColor4U &value, const char *name);

    /*virtual*/ bool        packVector2(const LLVector2 &value, const char *name);
    /*virtual*/ bool        unpackVector2(LLVector2 &value, const char *name);

    /*virtual*/ bool        packVector3(const LLVector3 &value, const char *name);
    /*virtual*/ bool        unpackVector3(LLVector3 &value, const char *name);

    /*virtual*/ bool        packVector4(const LLVector4 &value, const char *name);
    /*virtual*/ bool        unpackVector4(LLVector4 &value, const char *name);

    /*virtual*/ bool        packUUID(const LLUUID &value, const char *name);
    /*virtual*/ bool        unpackUUID(LLUUID &value, const char *name);
protected:
    void writeIndentedName(const char *name);
    bool getValueStr(const char *name, char *out_value, const S32 value_len);

    /*virtual*/ bool        hasNext() const         { return true; }

protected:
    S32 mIndent;
    LLFILE *mFP;
    std::ostream* mOutputStream;
    std::istream* mInputStream;
};

#endif // LL_LLDATAPACKER