/*
 * @file llpanelenvironmenttools.cpp
 * @brief Environment tools
 *
 * Copyright (c) 2016, Cinder Roxley <cinder@sdf.org>
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

#include "llviewerprecompiledheaders.h"
#include "llpanelenvironmenttools.h"

#include "llcolorswatch.h"
#include "llcombobox.h"
#include "llmultisliderctrl.h"
#include "llsliderctrl.h"
#include "lltexturectrl.h"

#include "llcommandhandler.h"
#include "lldaycyclemanager.h"
#include "llenvmanager.h"
#include "llviewercontrol.h"
#include "llwaterparammanager.h"
#include "llwlparammanager.h"

/*
 * TODO: all these findChild calls in the draw loop are somewhat slow, but not noticably so,
 * not enough for me to be arsed to cache them right now.
 */

void set_swatch(LLColorSwatchCtrl* swatch, const WLColorControl& from_ctrl, F32 k);
void update_color(LLColorSwatchCtrl* swatch, WLColorControl& color_ctrl);
void change_shadow_softness(const LLSD& userdata);
void change_ssao_softness(const LLSD& userdata);
void change_shadow_clarity(const LLSD& userdata);
void darken_ssao(const LLSD& userdata);

const F64 sHoursPerDay = 24.0;
static LLPanelInjector<LLPanelEnvironmentTools> t_panel_environment_tools("environment_tools");

LLPanelEnvironmentTools::LLPanelEnvironmentTools() : LLPanel()
,	mDayCyclesCombo(nullptr)
,	mSkyPresetsCombo(nullptr)
,	mWaterPresetsCombo(nullptr)
,	mSunPos(nullptr)
,	mSunlightSwatch(nullptr)
,	mAmbientSwatch(nullptr)
,	mBlueDensitySwatch(nullptr)
,	mBlueHorizonSwatch(nullptr)
,	mCloudColorSwatch(nullptr)
,	mWaterFogSwatch(nullptr)
,	mWaterNormalCtrl(nullptr)
,	mSunSizeCtrl(nullptr)
,	mSunFocusCtrl(nullptr)
,	mStarBrightnessCtrl(nullptr)
,	mGammaCtrl(nullptr)
{
	mCommitCallbackRegistrar.add("ET.ShadowSoften", boost::bind(&change_shadow_softness, _2));
	mCommitCallbackRegistrar.add("ET.ShadowClarity", boost::bind(&change_shadow_clarity, _2));
	mCommitCallbackRegistrar.add("ET.SSAOSoften", boost::bind(&change_ssao_softness, _2));
	mCommitCallbackRegistrar.add("ET.SSAODarken", boost::bind(&darken_ssao, _2));
	
	mCommitCallbackRegistrar.add("ET.SetDayCycle", boost::bind(&LLPanelEnvironmentTools::onDayCycleSelected, this, _2));
	mCommitCallbackRegistrar.add("ET.SetSkyPreset", boost::bind(&LLPanelEnvironmentTools::onSkyPresetSelected, this, _2));
	mCommitCallbackRegistrar.add("ET.SetWaterPreset", boost::bind(&LLPanelEnvironmentTools::onWaterPresetSelected, this, _2));
}

BOOL LLPanelEnvironmentTools::postBuild()
{
	mDayCyclesCombo = getChild<LLComboBox>("DayPresetCombo");
	mSkyPresetsCombo = getChild<LLComboBox>("SkyPresetCombo");
	mWaterPresetsCombo = getChild<LLComboBox>("WaterPresetCombo");
	mSunPos = getChild<LLMultiSliderCtrl>("WLSunPos");
	mSunlightSwatch = getChild<LLColorSwatchCtrl>("WLSunlight");
	mAmbientSwatch = getChild<LLColorSwatchCtrl>("WLAmbient");
	mBlueDensitySwatch = getChild<LLColorSwatchCtrl>("WLBlueDensity");
	mBlueHorizonSwatch = getChild<LLColorSwatchCtrl>("WLBlueHorizon");
	mCloudColorSwatch = getChild<LLColorSwatchCtrl>("WLCloudColor");
	mWaterFogSwatch = getChild<LLColorSwatchCtrl>("WaterFogColor");
	mWaterNormalCtrl = getChild<LLTextureCtrl>("WaterNormalMap");
	mSunSizeCtrl = getChild<LLSliderCtrl>("WLGlowR");
	mSunFocusCtrl = getChild<LLSliderCtrl>("WLGlowB");
	mStarBrightnessCtrl = getChild<LLSliderCtrl>("WLStarAlpha");
	mGammaCtrl = getChild<LLSliderCtrl>("WLGamma");
	
	initSkyCallbacks();
	initWaterCallbacks();
	
	refreshDayCycleList();
	refreshSkyPresetList();
	refreshWaterPresetList();
	
	LLDayCycleManager::instance().setModifyCallback(boost::bind(&LLPanelEnvironmentTools::refreshDayCycleList, this));
	LLWLParamManager::instance().setPresetListChangeCallback(boost::bind(&LLPanelEnvironmentTools::refreshSkyPresetList, this));
	LLWaterParamManager::instance().setPresetListChangeCallback(boost::bind(&LLPanelEnvironmentTools::refreshWaterPresetList, this));
	return TRUE;
}

void LLPanelEnvironmentTools::initSkyCallbacks()
{
	mSunlightSwatch->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onSetSunlightColor, this));
	mAmbientSwatch->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onSetAmbientColor, this));
	mCloudColorSwatch->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onSetCloudColor, this));
	mBlueDensitySwatch->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onSetBlueDensityColor, this));
	mBlueHorizonSwatch->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onSetBlueHorizonColor, this));
	mSunPos->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onSunMoved, this));
	mSunPos->addSlider();
	
	LLWLParamManager& param_mgr = LLWLParamManager::instance();
	
	getChild<LLUICtrl>("WLHazeDensity")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onFloatControlMoved, this, _1, &param_mgr.mHazeDensity));
	getChild<LLUICtrl>("WLHazeHorizon")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onFloatControlMoved, this, _1, &param_mgr.mHazeHorizon));
	getChild<LLUICtrl>("WLDensityMult")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onFloatControlMoved, this, _1, &param_mgr.mDensityMult));
	getChild<LLUICtrl>("WLMaxAltitude")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onFloatControlMoved, this, _1, &param_mgr.mMaxAlt));
	
	getChild<LLUICtrl>("WLCloudX")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onColorControlRMoved, this, _1, &param_mgr.mCloudMain));
	getChild<LLUICtrl>("WLCloudY")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onColorControlGMoved, this, _1, &param_mgr.mCloudMain));
	getChild<LLUICtrl>("WLCloudDensity")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onColorControlBMoved, this, _1, &param_mgr.mCloudMain));
	getChild<LLUICtrl>("WLCloudDetailX")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onColorControlRMoved, this, _1, &param_mgr.mCloudDetail));
	getChild<LLUICtrl>("WLCloudDetailY")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onColorControlGMoved, this, _1, &param_mgr.mCloudDetail));
	getChild<LLUICtrl>("WLCloudDetailDensity")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onColorControlBMoved, this, _1, &param_mgr.mCloudDetail));
	getChild<LLUICtrl>("WLCloudLockX")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onCloudScrollXToggled, this, _1));
	getChild<LLUICtrl>("WLCloudLockY")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onCloudScrollYToggled, this, _1));
	getChild<LLUICtrl>("WLCloudScrollX")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onCloudScrollXMoved, this, _1));
	getChild<LLUICtrl>("WLCloudScrollY")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onCloudScrollYMoved, this, _1));
	
	getChild<LLUICtrl>("WLCloudCoverage")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onFloatControlMoved, this, _1, &param_mgr.mCloudCoverage));
	getChild<LLUICtrl>("WLCloudScale")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onFloatControlMoved, this, _1, &param_mgr.mCloudScale));
	getChild<LLUICtrl>("WLDistanceMult")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onFloatControlMoved, this, _1, &param_mgr.mDistanceMult));
	
	getChild<LLUICtrl>("WLGlowR")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onGlowMoved, this, _1, &param_mgr.mGlow));
	getChild<LLUICtrl>("WLGlowB")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onGlowMoved, this, _1, &param_mgr.mGlow));
	
	getChild<LLUICtrl>("WLGamma")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onFloatControlMoved, this, _1, &param_mgr.mWLGamma));
	getChild<LLUICtrl>("WLStarAlpha")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onStarAlphaMoved, this, _1));
}

void LLPanelEnvironmentTools::initWaterCallbacks()
{
	mWaterFogSwatch->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onSetWaterFogColor, this));
	mWaterNormalCtrl->setDefaultImageAssetID(DEFAULT_WATER_NORMAL);
	mWaterNormalCtrl->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onWaterNormalMapPicked, this));
	
	LLWaterParamManager& water_mgr = LLWaterParamManager::instance();
	
	// fog density
	getChild<LLUICtrl>("WaterFogDensity")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onExpFloatControlMoved, this, _1, &water_mgr.mFogDensity));
	getChild<LLUICtrl>("WaterUnderWaterFogMod")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onWaterFloatControlMoved, this, _1, &water_mgr.mUnderWaterFogMod));
	
	// blue density
	getChild<LLUICtrl>("WaterNormalScaleX")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onVector3ControlXMoved, this, _1, &water_mgr.mNormalScale));
	getChild<LLUICtrl>("WaterNormalScaleY")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onVector3ControlYMoved, this, _1, &water_mgr.mNormalScale));
	getChild<LLUICtrl>("WaterNormalScaleZ")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onVector3ControlZMoved, this, _1, &water_mgr.mNormalScale));
	
	// fresnel
	getChild<LLUICtrl>("WaterFresnelScale")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onWaterFloatControlMoved, this, _1, &water_mgr.mFresnelScale));
	getChild<LLUICtrl>("WaterFresnelOffset")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onWaterFloatControlMoved, this, _1, &water_mgr.mFresnelOffset));
	
	// scale above/below
	getChild<LLUICtrl>("WaterScaleAbove")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onWaterFloatControlMoved, this, _1, &water_mgr.mScaleAbove));
	getChild<LLUICtrl>("WaterScaleBelow")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onWaterFloatControlMoved, this, _1, &water_mgr.mScaleBelow));
	
	// blur mult
	getChild<LLUICtrl>("WaterBlurMult")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onWaterFloatControlMoved, this, _1, &water_mgr.mBlurMultiplier));
	
	// wave direction
	getChild<LLUICtrl>("WaterWave1DirX")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onVector2ControlXMoved, this, _1, &water_mgr.mWave1Dir));
	getChild<LLUICtrl>("WaterWave1DirY")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onVector2ControlYMoved, this, _1, &water_mgr.mWave1Dir));
	getChild<LLUICtrl>("WaterWave2DirX")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onVector2ControlXMoved, this, _1, &water_mgr.mWave2Dir));
	getChild<LLUICtrl>("WaterWave2DirY")->setCommitCallback(boost::bind(&LLPanelEnvironmentTools::onVector2ControlYMoved, this, _1, &water_mgr.mWave2Dir));
}

void LLPanelEnvironmentTools::draw()
{
	LLWLParamManager& param_mgr = LLWLParamManager::instance();
	syncTimeSlider(param_mgr);
	syncSkyProperties(param_mgr);
	syncAtmosphericProperties(param_mgr);
	syncCloudProperties(param_mgr);
	syncWaterProperties();
	LLPanel::draw();
}

void LLPanelEnvironmentTools::syncTimeSlider(LLWLParamManager& param_mgr)
{
	F32 val;
	if (LLEnvManagerNew::instance().getUseRegionSettings() || LLEnvManagerNew::instance().getUseDayCycle())
	{
		val = param_mgr.mAnimator.getDayTime() * sHoursPerDay;
	}
	else
	{
		auto sun_pos = [](F64 pos) { return fmod((pos / F_TWO_PI) * 24.0 + 6.0, 24.0); };
		val = sun_pos(LLWLParamManager::instance().mCurParams.getSunAngle());
	}
	mSunPos->setCurSliderValue(val);
}

void LLPanelEnvironmentTools::syncSkyProperties(LLWLParamManager& param_mgr)
{
	bool err;
	LLWLParamSet& cur_params = param_mgr.mCurParams;
	
	// sunlight
	param_mgr.mSunlight = cur_params.getVector(param_mgr.mSunlight.mName, err);
	set_swatch(mSunlightSwatch, param_mgr.mSunlight, 3.f);
	
	// glow
	param_mgr.mGlow = cur_params.getVector(param_mgr.mGlow.mName, err);
	mSunSizeCtrl->setValue(2 - param_mgr.mGlow.r / 20.0f);
	mSunFocusCtrl->setValue(-param_mgr.mGlow.b / 5.0f);
	
	// ambient
	param_mgr.mAmbient = cur_params.getVector(param_mgr.mAmbient.mName, err);
	set_swatch(mAmbientSwatch, param_mgr.mAmbient, 3.f);
	
	// stars
	mStarBrightnessCtrl->setValue(param_mgr.mCurParams.getStarBrightness());
	
	// gamma
	param_mgr.mWLGamma = cur_params.getFloat(param_mgr.mWLGamma.mName, err);
	mGammaCtrl->setValue((F32)param_mgr.mWLGamma);
}

void LLPanelEnvironmentTools::syncAtmosphericProperties(LLWLParamManager& param_mgr)
{
	bool err;
	LLWLParamSet& cur_params = param_mgr.mCurParams;
	
	// blue horizon
	param_mgr.mBlueHorizon = cur_params.getVector(param_mgr.mBlueHorizon.mName, err);
	set_swatch(mBlueHorizonSwatch, param_mgr.mBlueHorizon, 2.f);
	
	// blue density
	param_mgr.mBlueDensity = cur_params.getVector(param_mgr.mBlueDensity.mName, err);
	set_swatch(mBlueDensitySwatch, param_mgr.mBlueDensity, 2.f);
	
	// haze density, horizon, mult, and altitude
	param_mgr.mHazeDensity = cur_params.getFloat(param_mgr.mHazeDensity.mName, err);
	childSetValue("WLHazeDensity", (F32) param_mgr.mHazeDensity);
	param_mgr.mHazeHorizon = cur_params.getFloat(param_mgr.mHazeHorizon.mName, err);
	childSetValue("WLHazeHorizon", (F32) param_mgr.mHazeHorizon);
	param_mgr.mDensityMult = cur_params.getFloat(param_mgr.mDensityMult.mName, err);
	childSetValue("WLDensityMult", ((F32) param_mgr.mDensityMult) * param_mgr.mDensityMult.mult);
	param_mgr.mMaxAlt = cur_params.getFloat(param_mgr.mMaxAlt.mName, err);
	childSetValue("WLMaxAltitude", (F32) param_mgr.mMaxAlt);
}

void LLPanelEnvironmentTools::syncCloudProperties(LLWLParamManager& param_mgr)
{
	bool err;
	LLWLParamSet& cur_params = param_mgr.mCurParams;
	
	// Cloud Color
	param_mgr.mCloudColor = cur_params.getVector(param_mgr.mCloudColor.mName, err);
	set_swatch(mCloudColorSwatch, param_mgr.mCloudColor, 1.f);
	
	// Cloud
	param_mgr.mCloudMain = cur_params.getVector(param_mgr.mCloudMain.mName, err);
	childSetValue("WLCloudX", param_mgr.mCloudMain.r);
	childSetValue("WLCloudY", param_mgr.mCloudMain.g);
	childSetValue("WLCloudDensity", param_mgr.mCloudMain.b);
	
	// Cloud Detail
	param_mgr.mCloudDetail = cur_params.getVector(param_mgr.mCloudDetail.mName, err);
	childSetValue("WLCloudDetailX", param_mgr.mCloudDetail.r);
	childSetValue("WLCloudDetailY", param_mgr.mCloudDetail.g);
	childSetValue("WLCloudDetailDensity", param_mgr.mCloudDetail.b);
	
	// Cloud extras
	param_mgr.mCloudCoverage = cur_params.getFloat(param_mgr.mCloudCoverage.mName, err);
	param_mgr.mCloudScale = cur_params.getFloat(param_mgr.mCloudScale.mName, err);
	childSetValue("WLCloudCoverage", (F32) param_mgr.mCloudCoverage);
	childSetValue("WLCloudScale", (F32) param_mgr.mCloudScale);
	
	// cloud scrolling
	bool lockX = !param_mgr.mCurParams.getEnableCloudScrollX();
	bool lockY = !param_mgr.mCurParams.getEnableCloudScrollY();
	childSetValue("WLCloudLockX", lockX);
	childSetValue("WLCloudLockY", lockY);
	
	// disable if locked, enable if not
	if (lockX)
	{
		childDisable("WLCloudScrollX");
	}
	else
	{
		childEnable("WLCloudScrollX");
	}
	if (lockY)
	{
		childDisable("WLCloudScrollY");
	}
	else
	{
		childEnable("WLCloudScrollY");
	}
	
	// *HACK cloud scrolling is off my an additive of 10
	childSetValue("WLCloudScrollX", param_mgr.mCurParams.getCloudScrollX() - 10.0f);
	childSetValue("WLCloudScrollY", param_mgr.mCurParams.getCloudScrollY() - 10.0f);
}

void LLPanelEnvironmentTools::syncWaterProperties()
{
	bool err;
	LLWaterParamManager& water_mgr = LLWaterParamManager::instance();
	LLWaterParamSet& current_params = water_mgr.mCurParams;
	
	water_mgr.mFogColor = current_params.getVector4(water_mgr.mFogColor.mName, err);
	LLColor4 col = water_mgr.getFogColor();
	col.mV[3] = 1.0f;
	mWaterFogSwatch->set(col);
	
	// fog and wavelets
	water_mgr.mFogDensity.mExp =
		log(current_params.getFloat(water_mgr.mFogDensity.mName, err)) / log(water_mgr.mFogDensity.mBase);
	water_mgr.setDensitySliderValue(water_mgr.mFogDensity.mExp);
	childSetValue("WaterFogDensity", water_mgr.mFogDensity.mExp);
	
	water_mgr.mUnderWaterFogMod.mX = current_params.getFloat(water_mgr.mUnderWaterFogMod.mName, err);
	childSetValue("WaterUnderWaterFogMod", water_mgr.mUnderWaterFogMod.mX);
	
	water_mgr.mNormalScale = current_params.getVector3(water_mgr.mNormalScale.mName, err);
	childSetValue("WaterNormalScaleX", water_mgr.mNormalScale.mX);
	childSetValue("WaterNormalScaleY", water_mgr.mNormalScale.mY);
	childSetValue("WaterNormalScaleZ", water_mgr.mNormalScale.mZ);
	
	// Fresnel
	water_mgr.mFresnelScale.mX = current_params.getFloat(water_mgr.mFresnelScale.mName, err);
	childSetValue("WaterFresnelScale", water_mgr.mFresnelScale.mX);
	water_mgr.mFresnelOffset.mX = current_params.getFloat(water_mgr.mFresnelOffset.mName, err);
	childSetValue("WaterFresnelOffset", water_mgr.mFresnelOffset.mX);
	
	// Scale Above/Below
	water_mgr.mScaleAbove.mX = current_params.getFloat(water_mgr.mScaleAbove.mName, err);
	childSetValue("WaterScaleAbove", water_mgr.mScaleAbove.mX);
	water_mgr.mScaleBelow.mX = current_params.getFloat(water_mgr.mScaleBelow.mName, err);
	childSetValue("WaterScaleBelow", water_mgr.mScaleBelow.mX);
	
	// blur mult
	water_mgr.mBlurMultiplier.mX = current_params.getFloat(water_mgr.mBlurMultiplier.mName, err);
	childSetValue("WaterBlurMult", water_mgr.mBlurMultiplier.mX);
	
	// wave directions
	water_mgr.mWave1Dir = current_params.getVector2(water_mgr.mWave1Dir.mName, err);
	childSetValue("WaterWave1DirX", water_mgr.mWave1Dir.mX);
	childSetValue("WaterWave1DirY", water_mgr.mWave1Dir.mY);
	
	water_mgr.mWave2Dir = current_params.getVector2(water_mgr.mWave2Dir.mName, err);
	childSetValue("WaterWave2DirX", water_mgr.mWave2Dir.mX);
	childSetValue("WaterWave2DirY", water_mgr.mWave2Dir.mY);
	
	mWaterNormalCtrl->setImageAssetID(water_mgr.getNormalMapID());
}

void LLPanelEnvironmentTools::refreshDayCycleList()
{
	mDayCyclesCombo->removeall();
	LLDayCycleManager::preset_name_list_t user_days, sys_days;
	LLDayCycleManager::instance().getPresetNames(user_days, sys_days);
	
	// Add user days.
	for (const auto& preset_name : user_days)
	{
		mDayCyclesCombo->add(preset_name, LLWLParamKey(preset_name, LLEnvKey::SCOPE_LOCAL).toLLSD());
	}
	
	if (!user_days.empty())
	{
		mDayCyclesCombo->addSeparator();
	}
	
	// Add system days.
	for (const auto& preset_name : sys_days)
	{
		mDayCyclesCombo->add(preset_name, LLWLParamKey(preset_name, LLEnvKey::SCOPE_LOCAL).toLLSD());
	}
}

void LLPanelEnvironmentTools::refreshSkyPresetList()
{
	mSkyPresetsCombo->removeall();
	LLWLParamManager::preset_name_list_t region_presets, user_presets, sys_presets;
	LLWLParamManager::instance().getPresetNames(region_presets, user_presets, sys_presets);
	
	// Add region presets.
	for (const auto& preset_name : region_presets)
	{
		mSkyPresetsCombo->add(preset_name, LLWLParamKey(preset_name, LLEnvKey::SCOPE_REGION).toLLSD());
	}
	
	if (!region_presets.empty())
	{
		mSkyPresetsCombo->addSeparator();
	}
	
	// Add user presets.
	for (const auto& preset_name : user_presets)
	{
		mSkyPresetsCombo->add(preset_name, LLWLParamKey(preset_name, LLEnvKey::SCOPE_LOCAL).toLLSD());
	}
	
	if (!user_presets.empty())
	{
		mSkyPresetsCombo->addSeparator();
	}
	
	// Add system presets.
	for (const auto& preset_name : sys_presets)
	{
		mSkyPresetsCombo->add(preset_name, LLWLParamKey(preset_name, LLEnvKey::SCOPE_LOCAL).toLLSD());
	}
}

void LLPanelEnvironmentTools::refreshWaterPresetList()
{
	mWaterPresetsCombo->removeall();
	std::list<std::string> user_presets, sys_presets;
	LLWaterParamManager::instance().getPresetNames(user_presets, sys_presets);
	
	// Add local user presets first.
	for (const auto& preset_name : user_presets)
	{
		mWaterPresetsCombo->add(preset_name, LLSD().with(0, preset_name)
								.with(1, LLEnvKey::SCOPE_LOCAL));
	}
	
	if (!user_presets.empty())
	{
		mWaterPresetsCombo->addSeparator();
	}
	
	// Add local system presets.
	for (const auto& preset_name : sys_presets)
	{
		mWaterPresetsCombo->add(preset_name, LLSD().with(0, preset_name)
								.with(1, LLEnvKey::SCOPE_LOCAL));
	}
}

LLWLParamKey LLPanelEnvironmentTools::getSelectedPreset(const LLSD& userdata)
{
	LLWLParamKey key;
		
	if (!userdata.isArray()) // manually typed text
	{
		key.name = userdata.asString();
		key.scope = LLEnvKey::SCOPE_LOCAL;
	}
	else
	{
		key.fromLLSD(userdata);
	}
	
	return key;
}

void LLPanelEnvironmentTools::onSkyPresetSelected(const LLSD& userdata)
{
	LLWLParamKey key = getSelectedPreset(userdata);
	LLWLParamSet sky_params;
	
	if (!LLWLParamManager::instance().getParamSet(key, sky_params))
	{
		// Manually entered string?
		LL_WARNS("Windlight") << "No sky preset named " << key.toString() << LL_ENDL;
		return;
	}
	
	LLEnvManagerNew::instance().useSkyParams(sky_params.getAll());
}

void LLPanelEnvironmentTools::onDayCycleSelected(const LLSD& userdata)
{
	LLSD day_data;
	LLWLParamKey dc_key = getSelectedPreset(userdata);
	
	if (dc_key.scope == LLEnvKey::SCOPE_LOCAL)
	{
		if (!LLDayCycleManager::instance().getPreset(dc_key.name, day_data))
		{
			LL_WARNS() << "No day cycle named " << dc_key.name << LL_ENDL;
		}
	}
	else
	{
		day_data = LLEnvManagerNew::instance().getRegionSettings().getWLDayCycle();
		if (!day_data.size())
		{
			LL_WARNS() << "Empty region day cycle" << LL_ENDL;
			llassert(!day_data.empty());
		}
	}
	
	refreshSkyPresetList();
	F32 time = mSunPos->getCurSliderValue() / sHoursPerDay;
	LLWLParamManager::instance().applyDayCycleParams(day_data, dc_key.scope, time);
}

void LLPanelEnvironmentTools::onWaterPresetSelected(const LLSD& userdata)
{
	LLWaterParamSet water_params;
	std::string name;
	LLEnvKey::EScope scope;
	
	if (!userdata.isArray())
	{
		name = userdata.asString();
		scope = LLEnvKey::SCOPE_LOCAL;
	}
	else
	{
		name = userdata[0].asString();
		scope = (LLEnvKey::EScope) userdata[1].asInteger();
	}
	
	// Display selected preset.
	if (scope == LLEnvKey::SCOPE_REGION)
	{
		water_params.setAll(LLEnvManagerNew::instance().getRegionSettings().getWaterParams());
	}
	else // local preset selected
	{
		if (!LLWaterParamManager::instance().getParamSet(name, water_params))
		{
			// Manually entered string?
			LL_WARNS("Windlight") << "No water preset named " << name << LL_ENDL;
			return;
		}
	}
	
	LLEnvManagerNew::instance().useWaterParams(water_params.getAll());
}

void LLPanelEnvironmentTools::onSunMoved()
{
	LLWLParamManager& mgr = LLWLParamManager::instance();
	mgr.mAnimator.deactivate();
	if (LLEnvManagerNew::instance().getUseRegionSettings() || LLEnvManagerNew::instance().getUseDayCycle())
	{
		F64 pos = ((F64)mSunPos->getCurSliderValue()) / sHoursPerDay;
		mgr.mAnimator.setDayTime(pos);
		mgr.mAnimator.update(mgr.mCurParams);
	}
	else
	{
		auto sun_pos = [](F64 pos) { return (fmod(pos * 24.0 + 6.0, 24.0)) * F_TWO_PI; };
		F64 pos = sun_pos((F64)mSunPos->getCurSliderValue());
		mgr.mCurParams.setSunAngle(pos);
	}
}

void LLPanelEnvironmentTools::onSetSunlightColor()
{
	WLColorControl& color_ctrl = LLWLParamManager::instance().mSunlight;
	update_color(mSunlightSwatch, color_ctrl);
}

void LLPanelEnvironmentTools::onSetAmbientColor()
{
	WLColorControl& color_ctrl = LLWLParamManager::instance().mAmbient;
	update_color(mAmbientSwatch, color_ctrl);
}

void LLPanelEnvironmentTools::onSetCloudColor()
{
	WLColorControl& color_ctrl = LLWLParamManager::instance().mCloudColor;
	update_color(mCloudColorSwatch, color_ctrl);
}

void LLPanelEnvironmentTools::onSetBlueHorizonColor()
{
	WLColorControl& color_ctrl = LLWLParamManager::instance().mBlueHorizon;
	update_color(mBlueHorizonSwatch, color_ctrl);
}

void LLPanelEnvironmentTools::onSetBlueDensityColor()
{
	WLColorControl& color_ctrl = LLWLParamManager::instance().mBlueDensity;
	update_color(mBlueDensitySwatch, color_ctrl);
}

void LLPanelEnvironmentTools::onSetWaterFogColor()
{
	WaterColorControl& color_ctrl = LLWaterParamManager::instance().mFogColor;
	color_ctrl = mWaterFogSwatch->get();
	
	color_ctrl.update(LLWaterParamManager::getInstance()->mCurParams);
	LLWaterParamManager::getInstance()->propagateParameters();
}

void LLPanelEnvironmentTools::onWaterNormalMapPicked()
{
	LLUUID textID = mWaterNormalCtrl->getImageAssetID();
	LLWaterParamManager::getInstance()->setNormalMapID(textID);
}

void LLPanelEnvironmentTools::onFloatControlMoved(LLUICtrl* ctrl, void* userdata)
{
	LLWLParamManager::getInstance()->mAnimator.deactivate();
	
	LLSliderCtrl* sldr_ctrl = static_cast<LLSliderCtrl*>(ctrl);
	WLFloatControl* float_ctrl = static_cast<WLFloatControl*>(userdata);
	
	float_ctrl->x = sldr_ctrl->getValueF32() / float_ctrl->mult;
	
	float_ctrl->update(LLWLParamManager::getInstance()->mCurParams);
	LLWLParamManager::getInstance()->propagateParameters();
}

void LLPanelEnvironmentTools::onGlowMoved(LLUICtrl* ctrl, void* userdata)
{
	LLWLParamManager::getInstance()->mAnimator.deactivate();
	
	LLSliderCtrl* sldr_ctrl = static_cast<LLSliderCtrl*>(ctrl);
	WLColorControl* color_ctrl = static_cast<WLColorControl *>(userdata);
	
	
	if (sldr_ctrl->getName() == LLStringExplicit("WLGlowR"))
		color_ctrl->r = (2 - sldr_ctrl->getValueF32()) * 20; // scaled by 20
	else if (sldr_ctrl->getName() == LLStringExplicit("WLGlowB"))
		color_ctrl->b = -sldr_ctrl->getValueF32() * 5; // NEGATIVE (-) B and scaled by 5
	else
		llassert(slider);
	
	color_ctrl->update(LLWLParamManager::getInstance()->mCurParams);
	LLWLParamManager::getInstance()->propagateParameters();
}

void LLPanelEnvironmentTools::onColorControlRMoved(LLUICtrl* ctrl, void* userdata)
{
	LLWLParamManager::getInstance()->mAnimator.deactivate();
	
	LLSliderCtrl* sldr_ctrl = static_cast<LLSliderCtrl*>(ctrl);
	WLColorControl* color_ctrl = static_cast<WLColorControl *>(userdata);
	
	color_ctrl->r = sldr_ctrl->getValueF32();
	if (color_ctrl->isSunOrAmbientColor)
	{
		color_ctrl->r *= 3.f;
	}
	if (color_ctrl->isBlueHorizonOrDensity)
	{
		color_ctrl->r *= 2.f;
	}
	
	// move i if it's the max
	if (color_ctrl->r >= color_ctrl->g && color_ctrl->r >= color_ctrl->b && color_ctrl->hasSliderName)
	{
		color_ctrl->i = color_ctrl->r;
		std::string name = color_ctrl->mSliderName;
		name.append("I");
		
		if (color_ctrl->isSunOrAmbientColor)
		{
			childSetValue(name, color_ctrl->r / 3.f);
		}
		else if	(color_ctrl->isBlueHorizonOrDensity)
		{
			childSetValue(name, color_ctrl->r / 2.f);
		}
		else
		{
			childSetValue(name, color_ctrl->r);
		}
	}
	
	color_ctrl->update(LLWLParamManager::getInstance()->mCurParams);
	
	LLWLParamManager::getInstance()->propagateParameters();
}

void LLPanelEnvironmentTools::onColorControlGMoved(LLUICtrl* ctrl, void* userdata)
{
	LLWLParamManager::getInstance()->mAnimator.deactivate();
	
	LLSliderCtrl* sldr_ctrl = static_cast<LLSliderCtrl*>(ctrl);
	WLColorControl* color_ctrl = static_cast<WLColorControl *>(userdata);
	
	color_ctrl->g = sldr_ctrl->getValueF32();
	if (color_ctrl->isSunOrAmbientColor)
	{
		color_ctrl->g *= 3.f;
	}
	if (color_ctrl->isBlueHorizonOrDensity)
	{
		color_ctrl->g *= 2.f;
	}
	
	// move i if it's the max
	if (color_ctrl->g >= color_ctrl->r && color_ctrl->g >= color_ctrl->b && color_ctrl->hasSliderName)
	{
		color_ctrl->i = color_ctrl->g;
		std::string name = color_ctrl->mSliderName;
		name.append("I");
		
		if (color_ctrl->isSunOrAmbientColor)
		{
			childSetValue(name, color_ctrl->g / 3.f);
		}
		else if (color_ctrl->isBlueHorizonOrDensity)
		{
			childSetValue(name, color_ctrl->g / 2.f);
		}
		else
		{
			childSetValue(name, color_ctrl->g);
		}
	}
	
	color_ctrl->update(LLWLParamManager::getInstance()->mCurParams);
	
	LLWLParamManager::getInstance()->propagateParameters();
}

void LLPanelEnvironmentTools::onColorControlBMoved(LLUICtrl* ctrl, void* userdata)
{
	LLWLParamManager::getInstance()->mAnimator.deactivate();
	
	LLSliderCtrl* sldr_ctrl = static_cast<LLSliderCtrl*>(ctrl);
	WLColorControl* color_ctrl = static_cast<WLColorControl *>(userdata);
	
	color_ctrl->b = sldr_ctrl->getValueF32();
	if (color_ctrl->isSunOrAmbientColor)
	{
		color_ctrl->b *= 3.f;
	}
	if (color_ctrl->isBlueHorizonOrDensity)
	{
		color_ctrl->b *= 2.f;
	}
	
	// move i if it's the max
	if (color_ctrl->b >= color_ctrl->r && color_ctrl->b >= color_ctrl->g && color_ctrl->hasSliderName)
	{
		color_ctrl->i = color_ctrl->b;
		std::string name = color_ctrl->mSliderName;
		name.append("I");
		
		if (color_ctrl->isSunOrAmbientColor)
		{
			childSetValue(name, color_ctrl->b / 3.f);
		}
		else if (color_ctrl->isBlueHorizonOrDensity)
		{
			childSetValue(name, color_ctrl->b / 2.f);
		}
		else
		{
			childSetValue(name, color_ctrl->b);
		}
	}
	
	color_ctrl->update(LLWLParamManager::getInstance()->mCurParams);
	
	LLWLParamManager::getInstance()->propagateParameters();
}

void LLPanelEnvironmentTools::onStarAlphaMoved(LLUICtrl* ctrl)
{
	LLWLParamManager::getInstance()->mAnimator.deactivate();
	
	LLWLParamManager::getInstance()->mCurParams.setStarBrightness(ctrl->getValue().asReal());
}

void LLPanelEnvironmentTools::onCloudScrollXMoved(LLUICtrl* ctrl)
{
	LLWLParamManager::getInstance()->mAnimator.deactivate();
	// *HACK  all cloud scrolling is off by an additive of 10.
	LLWLParamManager::getInstance()->mCurParams.setCloudScrollX(ctrl->getValue().asReal() + 10.0f);
}

void LLPanelEnvironmentTools::onCloudScrollYMoved(LLUICtrl* ctrl)
{
	LLWLParamManager::getInstance()->mAnimator.deactivate();
	
	// *HACK  all cloud scrolling is off by an additive of 10.
	LLWLParamManager::getInstance()->mCurParams.setCloudScrollY(ctrl->getValue().asReal() + 10.0f);
}

void LLPanelEnvironmentTools::onCloudScrollXToggled(LLUICtrl* ctrl)
{
	LLWLParamManager::getInstance()->mAnimator.deactivate();
	
	bool lock = ctrl->getValue().asBoolean();
	LLWLParamManager::getInstance()->mCurParams.setEnableCloudScrollX(!lock);
	
	LLSliderCtrl* sldr = getChild<LLSliderCtrl>("WLCloudScrollX");
	
	sldr->setEnabled(lock ? false : true);
}

void LLPanelEnvironmentTools::onCloudScrollYToggled(LLUICtrl* ctrl)
{
	LLWLParamManager::getInstance()->mAnimator.deactivate();
	
	bool lock = ctrl->getValue().asBoolean();
	LLWLParamManager::getInstance()->mCurParams.setEnableCloudScrollY(!lock);
	
	LLSliderCtrl* sldr = getChild<LLSliderCtrl>("WLCloudScrollY");
	
	sldr->setEnabled(lock ? false : true);
}

void LLPanelEnvironmentTools::onVector3ControlXMoved(LLUICtrl* ctrl, WaterVector3Control* vector_ctrl)
{
	LLSliderCtrl* sldr_ctrl = static_cast<LLSliderCtrl*>(ctrl);
	
	vector_ctrl->mX = sldr_ctrl->getValueF32();
	
	vector_ctrl->update(LLWaterParamManager::getInstance()->mCurParams);
	
	LLWaterParamManager::getInstance()->propagateParameters();
}

void LLPanelEnvironmentTools::onVector3ControlYMoved(LLUICtrl* ctrl, WaterVector3Control* vector_ctrl)
{
	LLSliderCtrl* sldr_ctrl = static_cast<LLSliderCtrl*>(ctrl);
	
	vector_ctrl->mY = sldr_ctrl->getValueF32();
	
	vector_ctrl->update(LLWaterParamManager::getInstance()->mCurParams);
	
	LLWaterParamManager::getInstance()->propagateParameters();
}

void LLPanelEnvironmentTools::onVector3ControlZMoved(LLUICtrl* ctrl, WaterVector3Control* vector_ctrl)
{
	LLSliderCtrl* sldr_ctrl = static_cast<LLSliderCtrl*>(ctrl);
	
	vector_ctrl->mZ = sldr_ctrl->getValueF32();
	
	vector_ctrl->update(LLWaterParamManager::getInstance()->mCurParams);
	
	LLWaterParamManager::getInstance()->propagateParameters();
}


void LLPanelEnvironmentTools::onVector2ControlXMoved(LLUICtrl* ctrl, WaterVector2Control* vector_ctrl)
{
	LLSliderCtrl* sldr_ctrl = static_cast<LLSliderCtrl*>(ctrl);
	
	vector_ctrl->mX = sldr_ctrl->getValueF32();
	
	vector_ctrl->update(LLWaterParamManager::getInstance()->mCurParams);
	
	LLWaterParamManager::getInstance()->propagateParameters();
}

void LLPanelEnvironmentTools::onVector2ControlYMoved(LLUICtrl* ctrl, WaterVector2Control* vector_ctrl)
{
	LLSliderCtrl* sldr_ctrl = static_cast<LLSliderCtrl*>(ctrl);
	
	vector_ctrl->mY = sldr_ctrl->getValueF32();
	
	vector_ctrl->update(LLWaterParamManager::getInstance()->mCurParams);
	
	LLWaterParamManager::getInstance()->propagateParameters();
}

void LLPanelEnvironmentTools::onWaterFloatControlMoved(LLUICtrl* ctrl, WaterFloatControl* floatControl)
{
	LLSliderCtrl* sldr_ctrl = static_cast<LLSliderCtrl*>(ctrl);
	
	floatControl->mX = sldr_ctrl->getValueF32() / floatControl->mMult;
	
	floatControl->update(LLWaterParamManager::getInstance()->mCurParams);
	LLWaterParamManager::getInstance()->propagateParameters();
}

void LLPanelEnvironmentTools::onExpFloatControlMoved(LLUICtrl* ctrl, WaterExpFloatControl* expFloatControl)
{
	LLSliderCtrl* sldr_ctrl = static_cast<LLSliderCtrl*>(ctrl);
	
	F32 val = sldr_ctrl->getValueF32();
	expFloatControl->mExp = val;
	LLWaterParamManager::getInstance()->setDensitySliderValue(val);
	
	expFloatControl->update(LLWaterParamManager::getInstance()->mCurParams);
	LLWaterParamManager::getInstance()->propagateParameters();
}

// local functions

void set_swatch(LLColorSwatchCtrl* swatch, const WLColorControl& from_ctrl, F32 k)
{
	LLVector4 color_vec = from_ctrl;
	swatch->set(LLColor4(color_vec / k));
}

void update_color(LLColorSwatchCtrl* swatch, WLColorControl& color_ctrl)
{
	LLWLParamManager::getInstance()->mAnimator.deactivate();
	
	LLVector4 color_vec(swatch->get().mV);
	
	// Set intensity to maximum of the RGB values.
	color_vec.mV[3] = llmax(color_vec.mV[0], llmax(color_vec.mV[1], color_vec.mV[2]));
	
	// Multiply RGB values by the appropriate factor.
	F32 k = 1.f;
	if (color_ctrl.isSunOrAmbientColor)
	{
		k = 3.f;
	}
	else if (color_ctrl.isBlueHorizonOrDensity)
	{
		k = 2.f;
	}
	
	color_vec *= k; // intensity isn't affected by the multiplication
	
	// Apply the new RGBI value.
	color_ctrl = color_vec;
	color_ctrl.update(LLWLParamManager::getInstance()->mCurParams);
	LLWLParamManager::getInstance()->propagateParameters();
}

void change_shadow_softness(const LLSD& userdata)
{
	LLVector3 render_shadow_gaussian = gSavedSettings.getVector3("RenderShadowGaussian");
	render_shadow_gaussian[VX] = userdata.asReal();
	gSavedSettings.setVector3("RenderShadowGaussian", render_shadow_gaussian);
}

void change_ssao_softness(const LLSD& userdata)
{
	LLVector3 render_shadow_gaussian = gSavedSettings.getVector3("RenderShadowGaussian");
	render_shadow_gaussian[VY] = userdata.asReal();
	gSavedSettings.setVector3("RenderShadowGaussian", render_shadow_gaussian);
}

void change_shadow_clarity(const LLSD& userdata)
{
	LLVector3 render_shadow_split = gSavedSettings.getVector3("RenderShadowSplitExponent");
	render_shadow_split[VY] = userdata.asReal();
	gSavedSettings.setVector3("RenderShadowSplitExponent", render_shadow_split);
}

void darken_ssao(const LLSD& userdata)
{
	LLVector3 render_ssao_effect = gSavedSettings.getVector3("RenderSSAOEffect");
	render_ssao_effect[VX] = userdata.asReal();
	gSavedSettings.setVector3("RenderSSAOEffect", render_ssao_effect);
}

// SLApp handler for activating windlight presets
class LLWindlightSLAppHandler : public LLCommandHandler
{
public:
	LLWindlightSLAppHandler() : LLCommandHandler("windlight", UNTRUSTED_BLOCK) {}
	
	bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web)
	{
		if (params.size() != 2)
			return false;
		
		const std::string& cmd = params[0].asString();
		if (cmd == LLStringExplicit("activate"))
		{
			const std::string& tod = params[1].asString();
			if (tod == LLStringExplicit("sunrise"))
			{
				LLEnvManagerNew::instance().setUseSkyPreset("Sunrise");
				return true;
			}
			else if (tod == LLStringExplicit("noon"))
			{
				LLEnvManagerNew::instance().setUseSkyPreset("Midday");
				return true;
			}
			else if (tod == LLStringExplicit("sunset"))
			{
				LLEnvManagerNew::instance().setUseSkyPreset("Sunset");
				return true;
			}
			else if (tod == LLStringExplicit("midnight"))
			{
				LLEnvManagerNew::instance().setUseSkyPreset("Midnight");
				return true;
			}
			else if (tod == LLStringExplicit("region"))
			{
				LLEnvManagerNew::instance().setUseRegionSettings(true);
				return true;
			}
			else if (LLWLParamManager::instance().presetExists(tod))
			{
				LLEnvManagerNew::instance().setUseSkyPreset(tod);
				return true;
			}
			
		}
		
		return false;
	}
};

LLWindlightSLAppHandler gWindlightSLAppHandler;
