/**
 * @file llcategory.cpp
 *
 * $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$
 */

#include "linden_common.h"

#include "llcategory.h"

#include "message.h"

const LLCategory LLCategory::none;

///----------------------------------------------------------------------------
/// Local function declarations, constants, enums, and typedefs
///----------------------------------------------------------------------------

// This is the storage of the category names. It's loosely based on a
// heap-like structure with indices into it for faster searching and
// so that we don't have to maintain a balanced heap. It's *VITALLY*
// important that the CATEGORY_INDEX and CATEGORY_NAME tables are kept
// in synch.

// CATEGORY_INDEX indexes into CATEGORY_NAME at the first occurance of
// a child. Thus, the first child of root is "Object" which is located
// in CATEGORY_NAME[1].
const S32 CATEGORY_INDEX[] =
{
    1,  // ROOT
    6,  // object
    7,  // clothing
    7,  // texture
    7,  // sound
    7,  // landmark
    7,  // object|component
    7,  // off the end (required for child count calculations)
};

// The heap of names
const char* CATEGORY_NAME[] =
{
    "(none)",
    "Object",       // (none)
    "Clothing",
    "Texture",
    "Sound",
    "Landmark",
    "Component",    // object
    NULL
};

///----------------------------------------------------------------------------
/// Class llcategory
///----------------------------------------------------------------------------

LLCategory::LLCategory()
{
    // this is used as a simple compile time assertion. If this code
    // fails to compile, the depth has been changed, and we need to
    // clean up some of the code that relies on the depth, such as the
    // default constructor. If CATEGORY_DEPTH != 4, this code will
    // attempt to construct a zero length array - which the compiler
    // should balk at.
//  static const char CATEGORY_DEPTH_CHECK[(CATEGORY_DEPTH == 4)?1:0] = {' '};  // unused

    // actually initialize the object.
    mData[0] = 0;
    mData[1] = 0;
    mData[2] = 0;
    mData[3] = 0;
}

void LLCategory::init(U32 value)
{
    U8 v;
    for(S32 i = 0; i < CATEGORY_DEPTH; i++)
    {
        v = (U8)((0x000000ff) & value);
        mData[CATEGORY_DEPTH - 1 - i] = v;
        value >>= 8;
    }
}

U32 LLCategory::getU32() const
{
    U32 rv = 0;
    rv |= mData[0];
    rv <<= 8;
    rv |= mData[1];
    rv <<= 8;
    rv |= mData[2];
    rv <<= 8;
    rv |= mData[3];
    return rv;
}

S32 LLCategory::getSubCategoryCount() const
{
    S32 rv = CATEGORY_INDEX[mData[0] + 1] - CATEGORY_INDEX[mData[0]];
    return rv;
}

// This method will return a category that is the nth subcategory. If
// you're already at the bottom of the hierarchy, then the method will
// return a copy of this.
LLCategory LLCategory::getSubCategory(U8 n) const
{
    LLCategory rv(*this);
    for(S32 i = 0; i < (CATEGORY_DEPTH - 1); i++)
    {
        if(rv.mData[i] == 0)
        {
            rv.mData[i] = n + 1;
            break;
        }
    }
    return rv;
}

// This method will return the name of the leaf category type
const char* LLCategory::lookupName() const
{
    S32 i = 0;
    S32 index = mData[i++];
    while((i < CATEGORY_DEPTH) && (mData[i] != 0))
    {
        index = CATEGORY_INDEX[index];
        ++i;
    }
    return CATEGORY_NAME[index];
}

// message serialization
void LLCategory::packMessage(LLMessageSystem* msg) const
{
    U32 data = getU32();
    msg->addU32Fast(_PREHASH_Category, data);
}

// message serialization
void LLCategory::unpackMessage(LLMessageSystem* msg, const char* block)
{
    U32 data;
    msg->getU32Fast(block, _PREHASH_Category, data);
    init(data);
}

// message serialization
void LLCategory::unpackMultiMessage(LLMessageSystem* msg, const char* block,
                                    S32 block_num)
{
    U32 data;
    msg->getU32Fast(block, _PREHASH_Category, data, block_num);
    init(data);
}

///----------------------------------------------------------------------------
/// Local function definitions
///----------------------------------------------------------------------------