/** 
 * @file llcategory.cpp
 *
 * $LicenseInfo:firstyear=2002&license=viewergpl$
 * 
 * Copyright (c) 2002-2009, Linden Research, Inc.
 * 
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 * 
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 * 
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 * 
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/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
///----------------------------------------------------------------------------