/** 
 * @file llpanelvolume.cpp
 * @brief Object editing (position, scale, etc.) in the tools floater
 *
 * $LicenseInfo:firstyear=2001&license=viewergpl$
 * 
 * Copyright (c) 2001-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 "llviewerprecompiledheaders.h"

// file include
#include "llpanelvolume.h"

// linden library includes
#include "llclickaction.h"
#include "lleconomy.h"
#include "llerror.h"
#include "llfontgl.h"
#include "llflexibleobject.h"
#include "llmaterialtable.h"
#include "llpermissionsflags.h"
#include "llstring.h"
#include "llvolume.h"
#include "m3math.h"
#include "material_codes.h"

// project includes
#include "llbutton.h"
#include "llcheckboxctrl.h"
#include "llcolorswatch.h"
#include "lltexturectrl.h"
#include "llcombobox.h"
#include "llfirstuse.h"
#include "llfocusmgr.h"
#include "llmanipscale.h"
#include "llpreviewscript.h"
#include "llresmgr.h"
#include "llselectmgr.h"
#include "llspinctrl.h"
#include "lltextbox.h"
#include "lltool.h"
#include "lltoolcomp.h"
#include "lltoolmgr.h"
#include "llui.h"
#include "llviewerobject.h"
#include "llviewerregion.h"
#include "llviewerwindow.h"
#include "llvovolume.h"
#include "llworld.h"
#include "pipeline.h"
#include "llviewershadermgr.h"

#include "lldrawpool.h"
#include "lluictrlfactory.h"

// "Features" Tab

BOOL	LLPanelVolume::postBuild()
{
	// Flexible Objects Parameters
	{
		childSetCommitCallback("Flexible1D Checkbox Ctrl",onCommitIsFlexible,this);
		childSetCommitCallback("FlexNumSections",onCommitFlexible,this);
		childSetValidate("FlexNumSections",precommitValidate);
		childSetCommitCallback("FlexGravity",onCommitFlexible,this);
		childSetValidate("FlexGravity",precommitValidate);
		childSetCommitCallback("FlexFriction",onCommitFlexible,this);
		childSetValidate("FlexFriction",precommitValidate);
		childSetCommitCallback("FlexWind",onCommitFlexible,this);
		childSetValidate("FlexWind",precommitValidate);
		childSetCommitCallback("FlexTension",onCommitFlexible,this);
		childSetValidate("FlexTension",precommitValidate);
		childSetCommitCallback("FlexForceX",onCommitFlexible,this);
		childSetValidate("FlexForceX",precommitValidate);
		childSetCommitCallback("FlexForceY",onCommitFlexible,this);
		childSetValidate("FlexForceY",precommitValidate);
		childSetCommitCallback("FlexForceZ",onCommitFlexible,this);
		childSetValidate("FlexForceZ",precommitValidate);
	}

	// LIGHT Parameters
	{
		childSetCommitCallback("Light Checkbox Ctrl",onCommitIsLight,this);
		LLColorSwatchCtrl*	LightColorSwatch = getChild<LLColorSwatchCtrl>("colorswatch");
		if(LightColorSwatch){
			LightColorSwatch->setOnCancelCallback(boost::bind(&LLPanelVolume::onLightCancelColor, this, _2));
			LightColorSwatch->setOnSelectCallback(boost::bind(&LLPanelVolume::onLightSelectColor, this, _2));
			childSetCommitCallback("colorswatch",onCommitLight,this);
		}

		LLTextureCtrl* LightTexPicker = getChild<LLTextureCtrl>("light texture control");
		if (LightTexPicker)
		{
			LightTexPicker->setOnCancelCallback(boost::bind(&LLPanelVolume::onLightCancelTexture, this, _2));
			LightTexPicker->setOnSelectCallback(boost::bind(&LLPanelVolume::onLightSelectTexture, this, _2));
			childSetCommitCallback("light texture control", onCommitLight, this);
		}

		childSetCommitCallback("Light Intensity",onCommitLight,this);
		childSetValidate("Light Intensity",precommitValidate);
		childSetCommitCallback("Light Radius",onCommitLight,this);
		childSetValidate("Light Radius",precommitValidate);
		childSetCommitCallback("Light Falloff",onCommitLight,this);
		childSetValidate("Light Falloff",precommitValidate);
		
		childSetCommitCallback("Light FOV", onCommitLight, this);
		childSetValidate("Light FOV", precommitValidate);
		childSetCommitCallback("Light Focus", onCommitLight, this);
		childSetValidate("Light Focus", precommitValidate);
		childSetCommitCallback("Light Ambiance", onCommitLight, this);
		childSetValidate("Light Ambiance", precommitValidate);
	}
	
	// Start with everyone disabled
	clearCtrls();

	return TRUE;
}

LLPanelVolume::LLPanelVolume()
	: LLPanel()
{
	setMouseOpaque(FALSE);

}


LLPanelVolume::~LLPanelVolume()
{
	// Children all cleaned up by default view destructor.
}

void LLPanelVolume::getState( )
{
	LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstRootObject();
	LLViewerObject* root_objectp = objectp;
	if(!objectp)
	{
		objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
		// *FIX: shouldn't we just keep the child?
		if (objectp)
		{
			LLViewerObject* parentp = objectp->getRootEdit();

			if (parentp)
			{
				root_objectp = parentp;
			}
			else
			{
				root_objectp = objectp;
			}
		}
	}

	LLVOVolume *volobjp = NULL;
	if ( objectp && (objectp->getPCode() == LL_PCODE_VOLUME))
	{
		volobjp = (LLVOVolume *)objectp;
	}
	
	if( !objectp )
	{
		//forfeit focus
		if (gFocusMgr.childHasKeyboardFocus(this))
		{
			gFocusMgr.setKeyboardFocus(NULL);
		}

		// Disable all text input fields
		clearCtrls();

		return;
	}

	BOOL owners_identical;
	LLUUID owner_id;
	std::string owner_name;
	owners_identical = LLSelectMgr::getInstance()->selectGetOwner(owner_id, owner_name);

	// BUG? Check for all objects being editable?
	BOOL editable = root_objectp->permModify();
	BOOL single_volume = LLSelectMgr::getInstance()->selectionAllPCode( LL_PCODE_VOLUME )
		&& LLSelectMgr::getInstance()->getSelection()->getObjectCount() == 1;

	// Select Single Message
	if (single_volume)
	{
		childSetVisible("edit_object",true);
		childSetEnabled("edit_object",true);
		childSetVisible("select_single",false);
	}
	else
	{	
		childSetVisible("edit_object",false);
		childSetVisible("select_single",true);
		childSetEnabled("select_single",true);
	}
	
	// Light properties
	BOOL is_light = volobjp && volobjp->getIsLight();
	childSetValue("Light Checkbox Ctrl",is_light);
	childSetEnabled("Light Checkbox Ctrl",editable && single_volume && volobjp);
	
	if (is_light && editable && single_volume)
	{
		childSetEnabled("label color",true);
		//mLabelColor		 ->setEnabled( TRUE );
		LLColorSwatchCtrl* LightColorSwatch = getChild<LLColorSwatchCtrl>("colorswatch");
		if(LightColorSwatch)
		{
			LightColorSwatch->setEnabled( TRUE );
			LightColorSwatch->setValid( TRUE );
			LightColorSwatch->set(volobjp->getLightBaseColor());
		}

		LLTextureCtrl* LightTextureCtrl = getChild<LLTextureCtrl>("light texture control");
		if (LightTextureCtrl)
		{
			LightTextureCtrl->setEnabled(TRUE);
			LightTextureCtrl->setValid(TRUE);
			LightTextureCtrl->setImageAssetID(volobjp->getLightTextureID());
		}

		childSetEnabled("Light Intensity",true);
		childSetEnabled("Light Radius",true);
		childSetEnabled("Light Falloff",true);

		childSetEnabled("Light FOV", true);
		childSetEnabled("Light Focus", true);
		childSetEnabled("Light Ambiance", true);
		
		childSetValue("Light Intensity",volobjp->getLightIntensity());
		childSetValue("Light Radius",volobjp->getLightRadius());
		childSetValue("Light Falloff",volobjp->getLightFalloff());

		LLVector3 params = volobjp->getSpotLightParams();
		childSetValue("Light FOV", params.mV[0]);
		childSetValue("Light Focus", params.mV[1]);
		childSetValue("Light Ambiance", params.mV[2]);

		mLightSavedColor = volobjp->getLightColor();
	}
	else
	{
		getChild<LLSpinCtrl>("Light Intensity", true)->clear();
		getChild<LLSpinCtrl>("Light Radius", true)->clear();
		getChild<LLSpinCtrl>("Light Falloff", true)->clear();

		childSetEnabled("label color",false);	
		LLColorSwatchCtrl* LightColorSwatch = getChild<LLColorSwatchCtrl>("colorswatch");
		if(LightColorSwatch)
		{
			LightColorSwatch->setEnabled( FALSE );
			LightColorSwatch->setValid( FALSE );
		}
		LLTextureCtrl* LightTextureCtrl = getChild<LLTextureCtrl>("light texture control");
		if (LightTextureCtrl)
		{
			LightTextureCtrl->setEnabled(FALSE);
			LightTextureCtrl->setValid(FALSE);
		}

		childSetEnabled("Light Intensity",false);
		childSetEnabled("Light Radius",false);
		childSetEnabled("Light Falloff",false);

		childSetEnabled("Light FOV",false);
		childSetEnabled("Light Focus",false);
		childSetEnabled("Light Ambiance",false);
	}
	
	// Flexible properties
	BOOL is_flexible = volobjp && volobjp->isFlexible();
	childSetValue("Flexible1D Checkbox Ctrl",is_flexible);
	if (is_flexible || (volobjp && volobjp->canBeFlexible()))
	{
		childSetEnabled("Flexible1D Checkbox Ctrl", editable && single_volume && volobjp);
	}
	else
	{
		childSetEnabled("Flexible1D Checkbox Ctrl", false);
	}
	if (is_flexible && editable && single_volume)
	{
		childSetVisible("FlexNumSections",true);
		childSetVisible("FlexGravity",true);
		childSetVisible("FlexTension",true);
		childSetVisible("FlexFriction",true);
		childSetVisible("FlexWind",true);
		childSetVisible("FlexForceX",true);
		childSetVisible("FlexForceY",true);
		childSetVisible("FlexForceZ",true);

		childSetEnabled("FlexNumSections",true);
		childSetEnabled("FlexGravity",true);
		childSetEnabled("FlexTension",true);
		childSetEnabled("FlexFriction",true);
		childSetEnabled("FlexWind",true);
		childSetEnabled("FlexForceX",true);
		childSetEnabled("FlexForceY",true);
		childSetEnabled("FlexForceZ",true);

		LLFlexibleObjectData *attributes = (LLFlexibleObjectData *)objectp->getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE);
		
		childSetValue("FlexNumSections",(F32)attributes->getSimulateLOD());
		childSetValue("FlexGravity",attributes->getGravity());
		childSetValue("FlexTension",attributes->getTension());
		childSetValue("FlexFriction",attributes->getAirFriction());
		childSetValue("FlexWind",attributes->getWindSensitivity());
		childSetValue("FlexForceX",attributes->getUserForce().mV[VX]);
		childSetValue("FlexForceY",attributes->getUserForce().mV[VY]);
		childSetValue("FlexForceZ",attributes->getUserForce().mV[VZ]);
	}
	else
	{
		getChild<LLSpinCtrl>("FlexNumSections", true)->clear();
		getChild<LLSpinCtrl>("FlexGravity", true)->clear();
		getChild<LLSpinCtrl>("FlexTension", true)->clear();
		getChild<LLSpinCtrl>("FlexFriction", true)->clear();
		getChild<LLSpinCtrl>("FlexWind", true)->clear();
		getChild<LLSpinCtrl>("FlexForceX", true)->clear();
		getChild<LLSpinCtrl>("FlexForceY", true)->clear();
		getChild<LLSpinCtrl>("FlexForceZ", true)->clear();

		childSetEnabled("FlexNumSections",false);
		childSetEnabled("FlexGravity",false);
		childSetEnabled("FlexTension",false);
		childSetEnabled("FlexFriction",false);
		childSetEnabled("FlexWind",false);
		childSetEnabled("FlexForceX",false);
		childSetEnabled("FlexForceY",false);
		childSetEnabled("FlexForceZ",false);
	}
	
	mObject = objectp;
	mRootObject = root_objectp;
}

// static
bool LLPanelVolume::precommitValidate( const LLSD& data )
{
	// TODO: Richard will fill this in later.  
	return TRUE; // FALSE means that validation failed and new value should not be commited.
}


void LLPanelVolume::refresh()
{
	getState();
	if (mObject.notNull() && mObject->isDead())
	{
		mObject = NULL;
	}

	if (mRootObject.notNull() && mRootObject->isDead())
	{
		mRootObject = NULL;
	}

	BOOL visible = LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED) > 0 ? TRUE : FALSE;

	childSetVisible("label texture", visible);
	childSetVisible("Light FOV", visible);
	childSetVisible("Light Focus", visible);
	childSetVisible("Light Ambiance", visible);
	childSetVisible("light texture control", visible);

}


void LLPanelVolume::draw()
{
	LLPanel::draw();
}

// virtual
void LLPanelVolume::clearCtrls()
{
	LLPanel::clearCtrls();

	childSetEnabled("select_single",false);
	childSetVisible("select_single",true);
	childSetEnabled("edit_object",false);
	childSetVisible("edit_object",false);
	childSetEnabled("Light Checkbox Ctrl",false);
	childSetEnabled("label color",false);
	childSetEnabled("label color",false);
	LLColorSwatchCtrl* LightColorSwatch = getChild<LLColorSwatchCtrl>("colorswatch");
	if(LightColorSwatch)
	{
		LightColorSwatch->setEnabled( FALSE );
		LightColorSwatch->setValid( FALSE );
	}
	LLTextureCtrl* LightTextureCtrl = getChild<LLTextureCtrl>("light texture control");
	if(LightTextureCtrl)
	{
		LightTextureCtrl->setEnabled( FALSE );
		LightTextureCtrl->setValid( FALSE );
	}

	childSetEnabled("Light Intensity",false);
	childSetEnabled("Light Radius",false);
	childSetEnabled("Light Falloff",false);

	childSetEnabled("Flexible1D Checkbox Ctrl",false);
	childSetEnabled("FlexNumSections",false);
	childSetEnabled("FlexGravity",false);
	childSetEnabled("FlexTension",false);
	childSetEnabled("FlexFriction",false);
	childSetEnabled("FlexWind",false);
	childSetEnabled("FlexForceX",false);
	childSetEnabled("FlexForceY",false);
	childSetEnabled("FlexForceZ",false);
}

//
// Static functions
//

void LLPanelVolume::sendIsLight()
{
	LLViewerObject* objectp = mObject;
	if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME))
	{
		return;
	}	
	LLVOVolume *volobjp = (LLVOVolume *)objectp;
	
	BOOL value = childGetValue("Light Checkbox Ctrl");
	volobjp->setIsLight(value);
	llinfos << "update light sent" << llendl;
}

void LLPanelVolume::sendIsFlexible()
{
	LLViewerObject* objectp = mObject;
	if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME))
	{
		return;
	}	
	LLVOVolume *volobjp = (LLVOVolume *)objectp;
	
	BOOL is_flexible = childGetValue("Flexible1D Checkbox Ctrl");
	//BOOL is_flexible = mCheckFlexible1D->get();

	if (is_flexible)
	{
		LLFirstUse::useFlexible();

		if (objectp->getClickAction() == CLICK_ACTION_SIT)
		{
			LLSelectMgr::getInstance()->selectionSetClickAction(CLICK_ACTION_NONE);
		}

	}

	if (volobjp->setIsFlexible(is_flexible))
	{
		mObject->sendShapeUpdate();
		LLSelectMgr::getInstance()->selectionUpdatePhantom(volobjp->flagPhantom());
	}

	llinfos << "update flexible sent" << llendl;
}

void LLPanelVolume::onLightCancelColor(const LLSD& data)
{
	LLColorSwatchCtrl*	LightColorSwatch = getChild<LLColorSwatchCtrl>("colorswatch");
	if(LightColorSwatch)
	{
		LightColorSwatch->setColor(mLightSavedColor);
	}
	onLightSelectColor(data);
}

void LLPanelVolume::onLightCancelTexture(const LLSD& data)
{
	LLTextureCtrl* LightTextureCtrl = getChild<LLTextureCtrl>("light texture control");
	if (LightTextureCtrl)
	{
		LightTextureCtrl->setImageAssetID(mLightSavedTexture);
	}
}

void LLPanelVolume::onLightSelectColor(const LLSD& data)
{
	LLViewerObject* objectp = mObject;
	if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME))
	{
		return;
	}	
	LLVOVolume *volobjp = (LLVOVolume *)objectp;


	LLColorSwatchCtrl*	LightColorSwatch = getChild<LLColorSwatchCtrl>("colorswatch");
	if(LightColorSwatch)
	{
		LLColor4	clr = LightColorSwatch->get();
		LLColor3	clr3( clr );
		volobjp->setLightColor(clr3);
		mLightSavedColor = clr;
	}
}

void LLPanelVolume::onLightSelectTexture(const LLSD& data)
{
	if (mObject.isNull() || (mObject->getPCode() != LL_PCODE_VOLUME))
	{
		return;
	}	
	LLVOVolume *volobjp = (LLVOVolume *) mObject.get();


	LLTextureCtrl*	LightTextureCtrl = getChild<LLTextureCtrl>("light texture control");
	if(LightTextureCtrl)
	{
		LLUUID id = LightTextureCtrl->getImageAssetID();
		volobjp->setLightTextureID(id);
		mLightSavedTexture = id;
	}
}

// static
void LLPanelVolume::onCommitLight( LLUICtrl* ctrl, void* userdata )
{
	LLPanelVolume* self = (LLPanelVolume*) userdata;
	LLViewerObject* objectp = self->mObject;
	if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME))
	{
		return;
	}	
	LLVOVolume *volobjp = (LLVOVolume *)objectp;
	
	
	volobjp->setLightIntensity((F32)self->childGetValue("Light Intensity").asReal());
	volobjp->setLightRadius((F32)self->childGetValue("Light Radius").asReal());
	volobjp->setLightFalloff((F32)self->childGetValue("Light Falloff").asReal());

	LLColorSwatchCtrl*	LightColorSwatch = self->getChild<LLColorSwatchCtrl>("colorswatch");
	if(LightColorSwatch)
	{
		LLColor4	clr = LightColorSwatch->get();
		volobjp->setLightColor(LLColor3(clr));
	}

	LLTextureCtrl*	LightTextureCtrl = self->getChild<LLTextureCtrl>("light texture control");
	if(LightTextureCtrl)
	{
		LLUUID id = LightTextureCtrl->getImageAssetID();
		if (id.notNull())
		{
			if (volobjp->getLightTextureID().isNull())
			{ //this commit is making this a spot light, set UI to default params
				volobjp->setLightTextureID(id);
				LLVector3 spot_params = volobjp->getSpotLightParams();
				self->childSetValue("Light FOV", spot_params.mV[0]);
				self->childSetValue("Light Focus", spot_params.mV[1]);
				self->childSetValue("Light Ambiance", spot_params.mV[2]);
			}
			else
			{ //modifying existing params
				LLVector3 spot_params;
				spot_params.mV[0] = (F32) self->childGetValue("Light FOV").asReal();
				spot_params.mV[1] = (F32) self->childGetValue("Light Focus").asReal();
				spot_params.mV[2] = (F32) self->childGetValue("Light Ambiance").asReal();
				volobjp->setSpotLightParams(spot_params);
			}
		}
		else if (volobjp->getLightTextureID().notNull())
		{ //no longer a spot light
			volobjp->setLightTextureID(id);
			//self->childDisable("Light FOV");
			//self->childDisable("Light Focus");
			//self->childDisable("Light Ambiance");
		}
	}

	
}

// static
void LLPanelVolume::onCommitIsLight( LLUICtrl* ctrl, void* userdata )
{
	LLPanelVolume* self = (LLPanelVolume*) userdata;
	self->sendIsLight();
}

//----------------------------------------------------------------------------

// static
void LLPanelVolume::onCommitFlexible( LLUICtrl* ctrl, void* userdata )
{
	LLPanelVolume* self = (LLPanelVolume*) userdata;
	LLViewerObject* objectp = self->mObject;
	if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME))
	{
		return;
	}
	
	LLFlexibleObjectData *attributes = (LLFlexibleObjectData *)objectp->getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE);
	if (attributes)
	{
		LLFlexibleObjectData new_attributes;
		new_attributes = *attributes;


		new_attributes.setSimulateLOD(self->childGetValue("FlexNumSections").asInteger());//(S32)self->mSpinSections->get());
		new_attributes.setGravity((F32)self->childGetValue("FlexGravity").asReal());
		new_attributes.setTension((F32)self->childGetValue("FlexTension").asReal());
		new_attributes.setAirFriction((F32)self->childGetValue("FlexFriction").asReal());
		new_attributes.setWindSensitivity((F32)self->childGetValue("FlexWind").asReal());
		F32 fx = (F32)self->childGetValue("FlexForceX").asReal();
		F32 fy = (F32)self->childGetValue("FlexForceY").asReal();
		F32 fz = (F32)self->childGetValue("FlexForceZ").asReal();
		LLVector3 force(fx,fy,fz);

		new_attributes.setUserForce(force);
		objectp->setParameterEntry(LLNetworkData::PARAMS_FLEXIBLE, new_attributes, true);
	}

	// Values may fail validation
	self->refresh();
}

// static
void LLPanelVolume::onCommitIsFlexible( LLUICtrl* ctrl, void* userdata )
{
	LLPanelVolume* self = (LLPanelVolume*) userdata;
	self->sendIsFlexible();
}