/** 
 * @file llfloaterreporter.cpp
 * @brief Bug and abuse reports.
 *
 * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
 * $License$
 */

#include "llviewerprecompiledheaders.h"

#include <sstream>

// self include
#include "llfloaterreporter.h"

// linden library includes
#include "llassetstorage.h"
#include "llcachename.h"
#include "llfontgl.h"
#include "llgl.h"			// for renderer
#include "llinventory.h"
#include "llstring.h"
#include "llsys.h"
#include "llversion.h"
#include "message.h"
#include "v3math.h"

// viewer project includes
#include "llagent.h"
#include "llbutton.h"
#include "llcheckboxctrl.h"
#include "llinventoryview.h"
#include "lllineeditor.h"
#include "lltexturectrl.h"
#include "llscrolllistctrl.h"
#include "llimview.h"
#include "lltextbox.h"
#include "llviewertexteditor.h"
#include "llviewerobject.h"
#include "llviewerregion.h"
#include "llcombobox.h"
#include "llfloaterrate.h"
#include "lltooldraganddrop.h"
#include "llfloatermap.h"
#include "lluiconstants.h"
#include "llcallingcard.h"
#include "llviewerobjectlist.h"
#include "llagent.h"
#include "lltoolobjpicker.h"
#include "lltoolmgr.h"
#include "llviewermenu.h"		// for LLResourceData
#include "llviewerwindow.h"
#include "llviewerimagelist.h"
#include "llfilepicker.h"
#include "llfloateravatarpicker.h"
#include "lldir.h"
#include "llselectmgr.h"
#include "llviewerbuild.h"
#include "llvieweruictrlfactory.h"
#include "viewer.h"


const U32 INCLUDE_SCREENSHOT  = 0x01 << 0;

//-----------------------------------------------------------------------------
// Globals
//-----------------------------------------------------------------------------

// this map keeps track of current reporter instances
// there can only be one instance of each reporter type
LLMap< EReportType, LLFloaterReporter* > gReporterInstances;

//-----------------------------------------------------------------------------
// Member functions
//-----------------------------------------------------------------------------
LLFloaterReporter::LLFloaterReporter(
	const std::string& name,
	const LLRect& rect, 
	const std::string& title, 
	EReportType report_type)
	:	
	LLFloater(name, rect, title),
	mReportType(report_type),
	mObjectID( ),
	mScreenID(),
	mDeselectOnClose( FALSE ),
	mPicking( FALSE), 
	mPosition(),
	mCopyrightWarningSeen( FALSE )
{
	if (report_type == BUG_REPORT)
	{
		gUICtrlFactory->buildFloater(this, "floater_report_bug.xml");
	}
	else
	{
		gUICtrlFactory->buildFloater(this, "floater_report_abuse.xml");
	}

	LLViewerRegion *regionp = gAgent.getRegion();
	if (regionp)
	{
		childSetText("sim_field", regionp->getName() );
	}
	LLButton* pick_btn = LLUICtrlFactory::getButtonByName(this, "pick_btn");
	if (pick_btn)
	{
		// XUI: Why aren't these in viewerart.ini?
		pick_btn->setImages( "UIImgFaceUUID",
							"UIImgFaceSelectedUUID" );
		childSetAction("pick_btn", onClickObjPicker, this);
	}

	if (report_type != BUG_REPORT)
	{
		LLLineEditor* le = (LLLineEditor*)getCtrlByNameAndType("abuser_name_edit", WIDGET_TYPE_LINE_EDITOR);
		le->setEnabled( FALSE );
	}

	childSetAction("select_abuser", onClickSelectAbuser, this);

	childSetAction("send_btn", onClickSend, this);
	childSetAction("cancel_btn", onClickCancel, this);

	enableControls(TRUE);

	// convert the position to a string
	LLVector3d pos = gAgent.getPositionGlobal();
	if (regionp)
	{
		pos -= regionp->getOriginGlobal();
	}
	setPosBox(pos);

	gReporterInstances.addData(report_type, this);

	// Upload a screenshot, but don't draw this floater.
	setVisible(FALSE);
	uploadScreenshot();
	setVisible(TRUE);

	// Default text to be blank
	childSetText("object_name", "");
	childSetText("owner_name", "");

	childSetFocus("summary_edit");

	mDefaultSummary = childGetText("details_edit");
}


// virtual
LLFloaterReporter::~LLFloaterReporter()
{
	gReporterInstances.removeData(mReportType);
	// child views automatically deleted
	mObjectID 		= LLUUID::null;

	if (mPicking)
	{
		closePickTool(this);
	}

	mPosition.setVec(0.0f, 0.0f, 0.0f);

	std::for_each(mMCDList.begin(), mMCDList.end(), DeletePointer() );
	mMCDList.clear();

	if (gSelectMgr)
	{
		gSelectMgr->deselectTransient();
	}
}


void LLFloaterReporter::enableControls(BOOL enable)
{
	childSetEnabled("category_combo", enable);
	// bug reports never include the chat history
	if (mReportType != BUG_REPORT)
	{
		childSetEnabled("chat_check", enable);
	}
	childSetEnabled("screen_check",	enable);
	childDisable("screenshot");
	childSetEnabled("pick_btn",		enable);
	childSetEnabled("summary_edit",	enable);
	childSetEnabled("details_edit",	enable);
	childSetEnabled("send_btn",		enable);
	childSetEnabled("cancel_btn",	enable);
}


void LLFloaterReporter::getObjectInfo(const LLUUID& object_id)
{
	// TODO -- 
	// 1 need to send to correct simulator if object is not 
	//   in same simulator as agent
	// 2 display info in widget window that gives feedback that
	//   we have recorded the object info
	// 3 can pick avatar ==> might want to indicate when a picked 
	//   object is an avatar, attachment, or other category

	mObjectID = object_id;

	if (LLUUID::null != mObjectID)
	{
		// get object info for the user's benefit
		LLViewerObject* objectp = NULL;
		objectp = gObjectList.findObject( mObjectID );
		if (objectp)
		{
			if ( objectp->isAttachment() )
			{
				objectp = (LLViewerObject*)objectp->getRoot();
			}

			// correct the region and position information
			LLViewerRegion *regionp = objectp->getRegion();
			if (regionp)
			{
				childSetText("sim_field", regionp->getName());
				LLVector3d global_pos;
				global_pos.setVec(objectp->getPositionRegion());
				setPosBox(global_pos);
			}
	
			if (objectp->isAvatar())
			{
				// we have the information we need
				LLString object_owner;

				LLNameValue* firstname = objectp->getNVPair("FirstName");
				LLNameValue* lastname =  objectp->getNVPair("LastName");
				if (firstname && lastname)
				{
					object_owner.append(firstname->getString());
					object_owner.append(1, ' ');
					object_owner.append(lastname->getString());
				}
				else
				{
					object_owner.append("Unknown");
				}
				childSetText("object_name", object_owner);
				childSetText("owner_name", object_owner);
			}
			else
			{
				// we have to query the simulator for information 
				// about this object
				LLMessageSystem* msg = gMessageSystem;
				U32 request_flags = (mReportType == BUG_REPORT) ? BUG_REPORT_REQUEST : COMPLAINT_REPORT_REQUEST;
				msg->newMessageFast(_PREHASH_RequestObjectPropertiesFamily);
				msg->nextBlockFast(_PREHASH_AgentData);
				msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
				msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
				msg->nextBlockFast(_PREHASH_ObjectData);
				msg->addU32Fast(_PREHASH_RequestFlags, request_flags );
				msg->addUUIDFast(_PREHASH_ObjectID, 	mObjectID);
				LLViewerRegion* regionp = objectp->getRegion();
				msg->sendReliable( regionp->getHost() );
			}
		}
	}
}


// static
void LLFloaterReporter::onClickSelectAbuser(void *userdata)
{
	LLFloaterReporter *self = (LLFloaterReporter *)userdata;

	gFloaterView->getParentFloater(self)->addDependentFloater(LLFloaterAvatarPicker::show(callbackAvatarID, userdata, FALSE, TRUE ));
}

void LLFloaterReporter::callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data)
{
	LLFloaterReporter* self = (LLFloaterReporter*) data;

	if (ids.empty() || names.empty()) return;

	// this should never be called in a bug report but here for safety.
	if ( self->mReportType != BUG_REPORT )
	{
		self->childSetText("abuser_name_edit", names[0] );

		self->refresh();
	};
}

// static
void LLFloaterReporter::onClickSend(void *userdata)
{
	LLFloaterReporter *self = (LLFloaterReporter *)userdata;

	// only do this for abuse reports
	if ( self->mReportType != BUG_REPORT )
	{
		if ( ! self->mCopyrightWarningSeen )
		{
			LLString details_lc = self->childGetText("details_edit");
			LLString::toLower( details_lc );
			LLString summary_lc = self->childGetText("summary_edit");
			LLString::toLower( summary_lc );
			if ( details_lc.find( "copyright" ) != std::string::npos ||
				summary_lc.find( "copyright" ) != std::string::npos )
			{
				gViewerWindow->alertXml("HelpReportAbuseContainsCopyright");
				self->mCopyrightWarningSeen = TRUE;
				return;
			};
		};
	};

	if (self->mPicking)
	{
		closePickTool(self);
	}
	self->sendReport();
}


// static
void LLFloaterReporter::onClickCancel(void *userdata)
{
	LLFloaterReporter *self = (LLFloaterReporter *)userdata;

	// reset flag in case the next report also contains this text
	self->mCopyrightWarningSeen = FALSE;

	if (self->mPicking)
	{
		closePickTool(self);
	}
	self->close();
}


// static 
void LLFloaterReporter::onClickObjPicker(void *userdata)
{
	LLFloaterReporter *self = (LLFloaterReporter *)userdata;
	gToolObjPicker->setExitCallback(LLFloaterReporter::closePickTool, self);
	gToolMgr->setTransientTool(gToolObjPicker);
	self->mPicking = TRUE;
	self->childSetText("object_name", "");
	self->childSetText("owner_name", "");
	LLButton* pick_btn = LLUICtrlFactory::getButtonByName(self, "pick_btn");
	if (pick_btn) pick_btn->setToggleState(TRUE);
}


// static
void LLFloaterReporter::closePickTool(void *userdata)
{
	LLFloaterReporter *self = (LLFloaterReporter *)userdata;

	LLUUID object_id = gToolObjPicker->getObjectID();
	self->getObjectInfo(object_id);

	gToolMgr->clearTransientTool();
	self->mPicking = FALSE;
	LLButton* pick_btn = LLUICtrlFactory::getButtonByName(self, "pick_btn");
	if (pick_btn) pick_btn->setToggleState(FALSE);
}


// static
void LLFloaterReporter::showFromMenu(EReportType report_type)
{
	if (gReporterInstances.checkData(report_type))
	{
		// ...bring that window to front
		LLFloaterReporter *f = gReporterInstances.getData(report_type);
		f->open();
	}
	else
	{
		LLFloaterReporter *f;
		if (BUG_REPORT == report_type)
		{
			f = LLFloaterReporter::createNewBugReporter();
		}
		else if (COMPLAINT_REPORT == report_type)
		{
			f = LLFloaterReporter::createNewAbuseReporter();
		}
		else
		{
			llwarns << "Unknown LLViewerReporter type : " << report_type << llendl;
			return;
		}

		f->center();

		if (report_type == BUG_REPORT)
		{
 			gViewerWindow->alertXml("HelpReportBug");
		}
		else
		{
			gViewerWindow->alertXml("HelpReportAbuse");
		}

		// grab the user's name
		LLString fullname;
		gAgent.buildFullname(fullname);
		f->childSetText("reporter_field", fullname);
	}
}


// static
void LLFloaterReporter::showFromObject(const LLUUID& object_id)
{
	LLFloaterReporter* f = createNewAbuseReporter();
	f->center();
	f->setFocus(TRUE);

	// grab the user's name
	LLString fullname;
	gAgent.buildFullname(fullname);
	f->childSetText("reporter_field", fullname);

	// Request info for this object
	f->getObjectInfo(object_id);

	// Need to deselect on close
	f->mDeselectOnClose = TRUE;

	f->open();
}


// static 
LLFloaterReporter* LLFloaterReporter::getReporter(EReportType report_type)
{
	LLFloaterReporter *self = NULL;
	if (gReporterInstances.checkData(report_type))
	{
		// ...bring that window to front
		self = gReporterInstances.getData(report_type);
	}
	return self;
}

LLFloaterReporter* LLFloaterReporter::createNewAbuseReporter()
{
	return new LLFloaterReporter("complaint_reporter",
						         LLRect(),
								 "Report Abuse",
								 COMPLAINT_REPORT);
}

//static
LLFloaterReporter* LLFloaterReporter::createNewBugReporter()
{
	return new LLFloaterReporter("bug_reporter",
				                 LLRect(),
 					             "Report Bug",
                     			 BUG_REPORT);
}
	


void LLFloaterReporter::setPickedObjectProperties(const char *object_name, const char *owner_name)
{
	childSetText("object_name", object_name);
	childSetText("owner_name", owner_name);
}

void LLFloaterReporter::sendReport()
{
	LLViewerRegion *regionp = gAgent.getRegion();
	if (!regionp) return;
	// Ensure user selected a category from the list
	LLSD category_sd = childGetValue("category_combo");
	U8 category = (U8)category_sd.asInteger();
	if (category == 0)
	{
		if ( mReportType != BUG_REPORT )
		{
			gViewerWindow->alertXml("HelpReportAbuseSelectCategory");
		}
		else
		{
			gViewerWindow->alertXml("HelpReportBugSelectCategory");
		}
		return;
	}

	if ( mReportType != BUG_REPORT )
	{
	  if ( childGetText("abuser_name_edit").empty() )
	  {
		  gViewerWindow->alertXml("HelpReportAbuseAbuserNameEmpty");
		  return;
	  };
  
	  if ( childGetText("abuse_location_edit").empty() )
	  {
		  gViewerWindow->alertXml("HelpReportAbuseAbuserLocationEmpty");
		  return;
	  };
	};

	if ( childGetText("summary_edit").empty() )
	{
		if ( mReportType != BUG_REPORT )
		{
			gViewerWindow->alertXml("HelpReportAbuseSummaryEmpty");
		}
		else
		{
			gViewerWindow->alertXml("HelpReportBugSummaryEmpty");
		}
		return;
	};

	if ( childGetText("details_edit") == mDefaultSummary )
	{
		if ( mReportType != BUG_REPORT )
		{
			gViewerWindow->alertXml("HelpReportAbuseDetailsEmpty");
		}
		else
		{
			gViewerWindow->alertXml("HelpReportBugDetailsEmpty");
		}
		return;
	};

	// reset flag in case the next report also contains this text
	mCopyrightWarningSeen = FALSE;

	U32 check_flags = 0;
	if (childGetValue("screen_check"))
	{
		check_flags |= INCLUDE_SCREENSHOT;
	}

	LLMessageSystem *msg = gMessageSystem;
	msg->newMessageFast(_PREHASH_UserReport);
	msg->nextBlockFast(_PREHASH_AgentData);
	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
	msg->nextBlockFast(_PREHASH_ReportData);
	msg->addU8Fast(_PREHASH_ReportType, 	(U8) mReportType);
	msg->addU8(_PREHASH_Category, category);
	msg->addVector3Fast(_PREHASH_Position, 	mPosition);
	msg->addU8Fast(_PREHASH_CheckFlags, 	(U8) check_flags);
	LLSD screenshot_id = childGetValue("screenshot");
	msg->addUUIDFast(_PREHASH_ScreenshotID, screenshot_id);
	msg->addUUIDFast(_PREHASH_ObjectID, 	mObjectID);
	
	std::ostringstream summary;
	if (!gInProductionGrid)
	{
		summary << "Preview ";
	}

	LLString category_name;
	LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(this, "category_combo");
	if (combo)
	{
		category_name = combo->getSimpleSelectedItem(); // want label, not value
	}

#if LL_WINDOWS
	const char* platform = "Win";
	const char* short_platform = "O:W";
#elif LL_DARWIN
	const char* platform = "Mac";
	const char* short_platform = "O:M";
#elif LL_LINUX
	const char* platform = "Lnx";
	const char* short_platform = "O:L";
#else
	const char* platform = "???";
	const char* short_platform = "O:?";
#endif


	if ( mReportType == BUG_REPORT)
	{
		summary << short_platform << " V" << LL_VERSION_MAJOR << "."
			<< LL_VERSION_MINOR << "."
			<< LL_VERSION_PATCH << "."
			<< LL_VIEWER_BUILD
			<< " (" << regionp->getName() << ")"
			<< "[" << category_name << "] "
			<< "\"" << childGetValue("summary_edit").asString() << "\"";
	}
	else
	{
		summary << ""
			<< " |" << regionp->getName() << "|"								// region reporter is currently in.
			<< " (" << childGetText("abuse_location_edit") << ")"				// region abuse occured in (freeform text - no LLRegionPicker tool)
			<< " [" << category_name << "] "									// updated category
			<< " {" << childGetText("abuser_name_edit") << "} "					// name of abuse entered in report (chosen using LLAvatarPicker)
			<< " \"" << childGetValue("summary_edit").asString() << "\"";		// summary as entered
	};
	msg->addStringFast(_PREHASH_Summary, summary.str().c_str());

	std::ostringstream details;
	if (mReportType != BUG_REPORT)
	{
		details << "V" << LL_VERSION_MAJOR << "."								// client version moved to body of email for abuse reports
			<< LL_VERSION_MINOR << "."
			<< LL_VERSION_PATCH << "."
			<< LL_VIEWER_BUILD << std::endl << std::endl;
	}
	LLString object_name = childGetText("object_name");
	LLString owner_name = childGetText("owner_name");
	if (!object_name.empty() && !owner_name.empty())
	{
		details << "Object: " << object_name << "\n";
		details << "Owner: " << owner_name << "\n";
	}

	if ( mReportType != BUG_REPORT )
	{
		details << "Abuser name: " << childGetText("abuser_name_edit") << " \n";
		details << " Abuser location: " << childGetText("abuse_location_edit") << " \n";
	};

	details << childGetValue("details_edit").asString();
	msg->addStringFast(_PREHASH_Details, details.str() );

	char version_string[MAX_STRING];
	sprintf(version_string,
			"%d.%d.%d %s %s %s %s",
			LL_VERSION_MAJOR,
			LL_VERSION_MINOR,
			LL_VERSION_PATCH,
			platform,
			gSysCPU.getFamily().c_str(),
			gGLManager.mGLRenderer.c_str(),
			gGLManager.mDriverVersionVendorString.c_str());
	msg->addString("VersionString", version_string);

	std::list<LLMeanCollisionData*>::iterator it;
	for (it = mMCDList.begin(); it != mMCDList.end(); ++it)
	{
		LLMeanCollisionData *mcd = *it;
		msg->nextBlockFast(_PREHASH_MeanCollision);
		msg->addUUIDFast(_PREHASH_Perp, mcd->mPerp);
		msg->addU32Fast(_PREHASH_Time, mcd->mTime);
		msg->addF32Fast(_PREHASH_Mag, mcd->mMag);
		msg->addU8Fast(_PREHASH_Type, mcd->mType);
	}
	msg->sendReliable(regionp->getHost());

	close();
}


void LLFloaterReporter::uploadScreenshot()
{
	const S32 IMAGE_WIDTH = 1024;
	const S32 IMAGE_HEIGHT = 768;
	LLString filename("report_screenshot.bmp");

	if( !gViewerWindow->saveSnapshot( filename, IMAGE_WIDTH, IMAGE_HEIGHT, TRUE, FALSE ) )
	{
		return;
	}

	// Generate the temporary filename
	std::string temp_filename = gDirUtilp->getTempFilename();

	// try to create the upload file
	if (!LLViewerImageList::createUploadFile(filename,
										 	temp_filename,
										 	IMG_CODEC_BMP ))
	{
		llwarns << "Unable to upload report screenshot " << filename << ":\n\n" << LLImageBase::getLastError() << "\n" << llendl;
		if(LLFile::remove(temp_filename.c_str()) == -1)
		{
			lldebugs << "unable to remove temp file" << llendl;
		}
		LLFilePicker::instance().reset();
	}
	else
	{
		// create a resource data
		LLResourceData* data = NULL;
		data = new LLResourceData;
		data->mInventoryType = LLInventoryType::IT_NONE;
		data->mAssetInfo.mTransactionID.generate();
		data->mAssetInfo.mUuid = data->mAssetInfo.mTransactionID.makeAssetID(gAgent.getSecureSessionID());
		if (BUG_REPORT == mReportType)
		{
			//data->mAssetInfo.mType = LLAssetType::AT_BUG_REPORT_SCREENSHOT;
			data->mAssetInfo.mType = LLAssetType::AT_TEXTURE;
			data->mPreferredLocation = LLAssetType::EType(-1);
		}
		else if (COMPLAINT_REPORT == mReportType)
		{
			//data->mAssetInfo.mType = LLAssetType::AT_COMPLAINT_REPORT_SCREENSHOT;
			data->mAssetInfo.mType = LLAssetType::AT_TEXTURE;
			data->mPreferredLocation = LLAssetType::EType(-2);
		}
		else
		{
			llwarns << "Unknown LLFloaterReporter type" << llendl;
		}
		data->mAssetInfo.mCreatorID = gAgentID;
		data->mAssetInfo.setName("screenshot_name");
		data->mAssetInfo.setDescription("screenshot_descr");

		llinfos << "*** Uploading: " << llendl;
		llinfos << "Type: " << LLAssetType::lookup(data->mAssetInfo.mType) << llendl;
		llinfos << "File: " << filename << llendl;
		llinfos << "Dest: " << temp_filename << llendl;
		llinfos << "Name: " << data->mAssetInfo.getName() << llendl;
		llinfos << "Desc: " << data->mAssetInfo.getDescription() << llendl;

		gAssetStorage->storeAssetData(temp_filename.c_str(),
										data->mAssetInfo.mTransactionID,
										data->mAssetInfo.mType,
										LLFloaterReporter::uploadDoneCallback,
										(void*)data, TRUE);
	}
}


// static
void LLFloaterReporter::uploadDoneCallback(const LLUUID &uuid, void *user_data, S32 result) // StoreAssetData callback (fixed)
{
	LLResourceData* data = (LLResourceData*)user_data;

	if(result >= 0)
	{
		EReportType report_type = UNKNOWN_REPORT;
		if (data->mPreferredLocation == -1)
		{
			report_type = BUG_REPORT;
		}
		else if (data->mPreferredLocation == -2)
		{
			report_type = COMPLAINT_REPORT;
		}
		else 
		{
			llwarns << "Unknown report type : " << data->mPreferredLocation << llendl;
		}

		LLFloaterReporter *self = getReporter(report_type);
		if (self)
		{
			LLTexturePicker* texture = LLUICtrlFactory::getTexturePickerByName(self, "screenshot");
			if (texture)
			{
				texture->setImageAssetID(uuid);
				texture->setDefaultImageAssetID(uuid);
				texture->setCaption("Screenshot");
			}
			self->mScreenID = uuid;
			llinfos << "Got screen shot " << uuid << llendl;
		}
	}
	else // 	if(result >= 0)
	{
		LLStringBase<char>::format_map_t args;
		args["[REASON]"] = std::string(LLAssetStorage::getErrorString(result));
		gViewerWindow->alertXml("ErrorUploadingReportScreenshot", args);

		std::string err_msg("There was a problem uploading a report screenshot");
		err_msg += " due to the following reason: " + args["[REASON]"];
		llwarns << err_msg << llendl;
	}
	delete data;
}


void LLFloaterReporter::setPosBox(const LLVector3d &pos)
{
	mPosition.setVec(pos);
	LLString pos_string = llformat("{%.1f, %.1f, %.1f}",
		mPosition.mV[VX],
		mPosition.mV[VY],
		mPosition.mV[VZ]);
	childSetText("pos_field", pos_string);
}

void LLFloaterReporter::setDescription(const LLString& description, LLMeanCollisionData *mcd)
{
	LLFloaterReporter *self = gReporterInstances[COMPLAINT_REPORT];
	if (self)
	{
		self->childSetText("details_edit", description);

		for_each(self->mMCDList.begin(), self->mMCDList.end(), DeletePointer());
		self->mMCDList.clear();
		if (mcd)
		{
			self->mMCDList.push_back(new LLMeanCollisionData(mcd));
		}
	}
}

void LLFloaterReporter::addDescription(const LLString& description, LLMeanCollisionData *mcd)
{
	LLFloaterReporter *self = gReporterInstances[COMPLAINT_REPORT];
	if (self)
	{
		LLTextEditor* text = LLUICtrlFactory::getTextEditorByName(self, "details_edit");
		if (text)
		{	
			text->insertText(description);
		}
		if (mcd)
		{
			self->mMCDList.push_back(new LLMeanCollisionData(mcd));
		}
	}
}
