/** 
 * @file llsdparam.h
 * @brief parameter block abstraction for creating complex objects and 
 * parsing construction parameters from xml and LLSD
 *
 * $LicenseInfo:firstyear=2008&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_LLSDPARAM_H
#define LL_LLSDPARAM_H

#include "llinitparam.h"
#include "boost/function.hpp"
#include "llfasttimer.h"

struct LL_COMMON_API LLParamSDParserUtilities
{
	static LLSD& getSDWriteNode(LLSD& input, LLInitParam::Parser::name_stack_range_t& name_stack_range);

	typedef boost::function<void (const LLSD&, LLInitParam::Parser::name_stack_t&)> read_sd_cb_t;
	static void readSDValues(read_sd_cb_t cb, const LLSD& sd, LLInitParam::Parser::name_stack_t& stack);
	static void readSDValues(read_sd_cb_t cb, const LLSD& sd);
};

class LL_COMMON_API LLParamSDParser 
:	public LLInitParam::Parser
{
LOG_CLASS(LLParamSDParser);

typedef LLInitParam::Parser parser_t;

public:
	LLParamSDParser();
	void readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool silent = false);
	template<typename BLOCK>
	void writeSD(LLSD& sd, 
		const BLOCK& block, 
		const LLInitParam::predicate_rule_t rules = LLInitParam::default_parse_rules(),
		const LLInitParam::BaseBlock* diff_block = NULL)
	{
		if (!diff_block 
			&& !rules.isAmbivalent(LLInitParam::HAS_DEFAULT_VALUE))
		{
			diff_block = &LLInitParam::defaultValue<BLOCK>();
		}
		writeSDImpl(sd, block, rules, diff_block);
	}

	/*virtual*/ std::string getCurrentElementName();
	/*virtual*/ std::string getCurrentFileName(){ return LLStringUtil::null; }

private:
	void writeSDImpl(LLSD& sd, 
		const LLInitParam::BaseBlock& block, 
		const LLInitParam::predicate_rule_t,
		const LLInitParam::BaseBlock* diff_block);

	void submit(LLInitParam::BaseBlock& block, const LLSD& sd, LLInitParam::Parser::name_stack_t& name_stack);

	template<typename T>
	static bool writeTypedValue(Parser& parser, const void* val_ptr, parser_t::name_stack_t& name_stack)
	{
		LLParamSDParser& sdparser = static_cast<LLParamSDParser&>(parser);
		if (!sdparser.mWriteRootSD) return false;
		
		LLInitParam::Parser::name_stack_range_t range(name_stack.begin(), name_stack.end());
		LLSD& sd_to_write = LLParamSDParserUtilities::getSDWriteNode(*sdparser.mWriteRootSD, range);

		sd_to_write.assign(*((const T*)val_ptr));
		return true;
	}

	static bool writeU32Param(Parser& parser, const void* value_ptr, parser_t::name_stack_t& name_stack);
	static bool writeFlag(Parser& parser, const void* value_ptr, parser_t::name_stack_t& name_stack);

	static bool readFlag(Parser& parser, void* val_ptr);
	static bool readS32(Parser& parser, void* val_ptr);
	static bool readU32(Parser& parser, void* val_ptr);
	static bool readF32(Parser& parser, void* val_ptr);
	static bool readF64(Parser& parser, void* val_ptr);
	static bool readBool(Parser& parser, void* val_ptr);
	static bool readString(Parser& parser, void* val_ptr);
	static bool readUUID(Parser& parser, void* val_ptr);
	static bool readDate(Parser& parser, void* val_ptr);
	static bool readURI(Parser& parser, void* val_ptr);
	static bool readSD(Parser& parser, void* val_ptr);

	Parser::name_stack_t	mNameStack;
	const LLSD*				mCurReadSD;
	LLSD*					mWriteRootSD;
};


template<typename T>
class LLSDParamAdapter : public T
{
public:
	LLSDParamAdapter() {}
	LLSDParamAdapter(const LLSD& sd)
	{
        LL_PROFILE_ZONE_SCOPED;
		LLParamSDParser parser;
		// don't spam for implicit parsing of LLSD, as we want to allow arbitrary freeform data and ignore most of it
		bool parse_silently = true;
		parser.readSD(sd, *this, parse_silently);
	}

	operator LLSD() const
	{
		LLParamSDParser parser;
		LLSD sd;
		parser.writeSD(sd, *this);
		return sd;
	}
		
	LLSDParamAdapter(const T& val)
	: T(val)
	{
		T::operator=(val);
	}
};

#endif // LL_LLSDPARAM_H