/** 
 * @file llestateinfomodel.cpp
 * @brief Estate info model
 *
 * $LicenseInfo:firstyear=2011&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2011, 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 "llviewerprecompiledheaders.h"

#include "llestateinfomodel.h"

// libs
#include "llhttpclient.h"
#include "llregionflags.h"
#include "message.h"

// viewer
#include "llagent.h"
#include "llfloaterregioninfo.h" // for invoice id
#include "llviewerregion.h"

class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy estateChangeInfoResponder_timeout;

LLEstateInfoModel::LLEstateInfoModel()
:	mID(0)
,	mFlags(0)
,	mSunHour(0)
{
}

boost::signals2::connection LLEstateInfoModel::setUpdateCallback(const update_signal_t::slot_type& cb)
{
	return mUpdateSignal.connect(cb);
}

boost::signals2::connection LLEstateInfoModel::setCommitCallback(const update_signal_t::slot_type& cb)
{
	return mCommitSignal.connect(cb);
}

void LLEstateInfoModel::sendEstateInfo()
{
	if (!commitEstateInfoCaps())
	{
		// the caps method failed, try the old way
		LLFloaterRegionInfo::nextInvoice();
		commitEstateInfoDataserver();
	}
}

bool LLEstateInfoModel::getUseFixedSun()			const {	return mFlags & REGION_FLAGS_SUN_FIXED;				}
bool LLEstateInfoModel::getIsExternallyVisible()	const {	return mFlags & REGION_FLAGS_EXTERNALLY_VISIBLE;	}
bool LLEstateInfoModel::getAllowDirectTeleport()	const {	return mFlags & REGION_FLAGS_ALLOW_DIRECT_TELEPORT;	}
bool LLEstateInfoModel::getDenyAnonymous()			const {	return mFlags & REGION_FLAGS_DENY_ANONYMOUS; 		}
bool LLEstateInfoModel::getDenyAgeUnverified()		const {	return mFlags & REGION_FLAGS_DENY_AGEUNVERIFIED;	}
bool LLEstateInfoModel::getAllowVoiceChat()			const {	return mFlags & REGION_FLAGS_ALLOW_VOICE;			}

void LLEstateInfoModel::setUseFixedSun(bool val)			{ setFlag(REGION_FLAGS_SUN_FIXED, 				val);	}
void LLEstateInfoModel::setIsExternallyVisible(bool val)	{ setFlag(REGION_FLAGS_EXTERNALLY_VISIBLE,		val);	}
void LLEstateInfoModel::setAllowDirectTeleport(bool val)	{ setFlag(REGION_FLAGS_ALLOW_DIRECT_TELEPORT,	val);	}
void LLEstateInfoModel::setDenyAnonymous(bool val)			{ setFlag(REGION_FLAGS_DENY_ANONYMOUS,			val);	}
void LLEstateInfoModel::setDenyAgeUnverified(bool val)		{ setFlag(REGION_FLAGS_DENY_AGEUNVERIFIED,		val);	}
void LLEstateInfoModel::setAllowVoiceChat(bool val)			{ setFlag(REGION_FLAGS_ALLOW_VOICE,				val);	}

void LLEstateInfoModel::update(const strings_t& strings)
{
	// NOTE: LLDispatcher extracts strings with an extra \0 at the
	// end.  If we pass the std::string direct to the UI/renderer
	// it draws with a weird character at the end of the string.
	mName		= strings[0].c_str();
	mOwnerID	= LLUUID(strings[1].c_str());
	mID			= strtoul(strings[2].c_str(), NULL, 10);
	mFlags		= strtoul(strings[3].c_str(), NULL, 10);
	mSunHour	= ((F32)(strtod(strings[4].c_str(), NULL)))/1024.0f;

	LL_DEBUGS("Windlight Sync") << "Received estate info: "
		<< "is_sun_fixed = " << getUseFixedSun()
		<< ", sun_hour = " << getSunHour() << LL_ENDL;
	lldebugs << getInfoDump() << llendl;

	// Update region owner.
	LLViewerRegion* regionp = gAgent.getRegion();
	regionp->setOwner(mOwnerID);

	// Let interested parties know that estate info has been updated.
	mUpdateSignal();
}

void LLEstateInfoModel::notifyCommit()
{
	mCommitSignal();
}

//== PRIVATE STUFF ============================================================

class LLEstateChangeInfoResponder : public LLHTTPClient::ResponderWithResult
{
public:

	// if we get a normal response, handle it here
	/*virtual*/ void result(const LLSD& content)
	{
		llinfos << "Committed estate info" << llendl;
		LLEstateInfoModel::instance().notifyCommit();
	}

	// if we get an error response
	/*virtual*/ void error(U32 status, const std::string& reason)
	{
		llwarns << "Failed to commit estate info (" << status << "): " << reason << llendl;
	}

	/*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return estateChangeInfoResponder_timeout; }
	/*virtual*/ char const* getName(void) const { return "LLEstateChangeInfoResponder"; }
};

// tries to send estate info using a cap; returns true if it succeeded
bool LLEstateInfoModel::commitEstateInfoCaps()
{
	std::string url = gAgent.getRegion()->getCapability("EstateChangeInfo");

	if (url.empty())
	{
		// whoops, couldn't find the cap, so bail out
		return false;
	}

	LLSD body;
	body["estate_name"          ] = getName();
	body["sun_hour"             ] = getSunHour();

	body["is_sun_fixed"         ] = getUseFixedSun();
	body["is_externally_visible"] = getIsExternallyVisible();
	body["allow_direct_teleport"] = getAllowDirectTeleport();
	body["deny_anonymous"       ] = getDenyAnonymous();
	body["deny_age_unverified"  ] = getDenyAgeUnverified();
	body["allow_voice_chat"     ] = getAllowVoiceChat();

	body["invoice"              ] = LLFloaterRegionInfo::getLastInvoice();

	LL_DEBUGS("Windlight Sync") << "Sending estate caps: "
		<< "is_sun_fixed = " << getUseFixedSun()
		<< ", sun_hour = " << getSunHour() << LL_ENDL;
	lldebugs << body << LL_ENDL;

	// we use a responder so that we can re-get the data after committing to the database
	LLHTTPClient::post(url, body, new LLEstateChangeInfoResponder);
    return true;
}

/* This is the old way of doing things, is deprecated, and should be
   deleted when the dataserver model can be removed */
// key = "estatechangeinfo"
// strings[0] = str(estate_id) (added by simulator before relay - not here)
// strings[1] = estate_name
// strings[2] = str(estate_flags)
// strings[3] = str((S32)(sun_hour * 1024.f))
void LLEstateInfoModel::commitEstateInfoDataserver()
{
	LL_DEBUGS("Windlight Sync") << "Sending estate info: "
		<< "is_sun_fixed = " << getUseFixedSun()
		<< ", sun_hour = " << getSunHour() << LL_ENDL;
	lldebugs << getInfoDump() << LL_ENDL;

	LLMessageSystem* msg = gMessageSystem;
	msg->newMessage("EstateOwnerMessage");
	msg->nextBlockFast(_PREHASH_AgentData);
	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
	msg->addUUIDFast(_PREHASH_TransactionID, LLUUID::null); //not used

	msg->nextBlock("MethodData");
	msg->addString("Method", "estatechangeinfo");
	msg->addUUID("Invoice", LLFloaterRegionInfo::getLastInvoice());

	msg->nextBlock("ParamList");
	msg->addString("Parameter", getName());

	msg->nextBlock("ParamList");
	msg->addString("Parameter", llformat("%u", getFlags()));

	msg->nextBlock("ParamList");
	msg->addString("Parameter", llformat("%d", (S32) (getSunHour() * 1024.0f)));

	gAgent.sendMessage();
}

void LLEstateInfoModel::setFlag(U32 flag, bool val)
{
	if (val)
	{
		mFlags |= flag;
	}
	else
	{
		mFlags &= ~flag;
	}
}

std::string LLEstateInfoModel::getInfoDump()
{
	LLSD dump;
	dump["estate_name"          ] = getName();
	dump["sun_hour"             ] = getSunHour();

	dump["is_sun_fixed"         ] = getUseFixedSun();
	dump["is_externally_visible"] = getIsExternallyVisible();
	dump["allow_direct_teleport"] = getAllowDirectTeleport();
	dump["deny_anonymous"       ] = getDenyAnonymous();
	dump["deny_age_unverified"  ] = getDenyAgeUnverified();
	dump["allow_voice_chat"     ] = getAllowVoiceChat();

	std::stringstream dump_str;
	dump_str << dump;
	return dump_str.str();
}
