/**
 * @file llvisualparam.cpp
 * @brief Implementation of LLPolyMesh class.
 *
 * $LicenseInfo:firstyear=2001&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$
 */
//-----------------------------------------------------------------------------
// Header Files
//-----------------------------------------------------------------------------
#include "linden_common.h"
#include "llvisualparam.h"
//-----------------------------------------------------------------------------
// LLVisualParamInfo()
//-----------------------------------------------------------------------------
LLVisualParamInfo::LLVisualParamInfo()
    :
    mID( -1 ),
    mGroup( VISUAL_PARAM_GROUP_TWEAKABLE ),
    mMinWeight( 0.f ),
    mMaxWeight( 1.f ),
    mDefaultWeight( 0.f ),
    mSex( SEX_BOTH )
{
}
//-----------------------------------------------------------------------------
// parseXml()
//-----------------------------------------------------------------------------
bool LLVisualParamInfo::parseXml(LLXmlTreeNode *node)
{
    // attribute: id
    static LLStdStringHandle id_string = LLXmlTree::addAttributeString("id");
    node->getFastAttributeS32( id_string, mID );
    // attribute: group
    U32 group = 0;
    static LLStdStringHandle group_string = LLXmlTree::addAttributeString("group");
    if( node->getFastAttributeU32( group_string, group ) )
    {
        if( group < NUM_VISUAL_PARAM_GROUPS )
        {
            mGroup = (EVisualParamGroup)group;
        }
    }
    // attribute: value_min, value_max
    static LLStdStringHandle value_min_string = LLXmlTree::addAttributeString("value_min");
    static LLStdStringHandle value_max_string = LLXmlTree::addAttributeString("value_max");
    node->getFastAttributeF32( value_min_string, mMinWeight );
    node->getFastAttributeF32( value_max_string, mMaxWeight );
    // attribute: value_default
    F32 default_weight = 0;
    static LLStdStringHandle value_default_string = LLXmlTree::addAttributeString("value_default");
    if( node->getFastAttributeF32( value_default_string, default_weight ) )
    {
        mDefaultWeight = llclamp( default_weight, mMinWeight, mMaxWeight );
        if( default_weight != mDefaultWeight )
        {
            LL_WARNS() << "value_default attribute is out of range in node " << mName << " " << default_weight << LL_ENDL;
        }
    }
    // attribute: sex
    std::string sex = "both";
    static LLStdStringHandle sex_string = LLXmlTree::addAttributeString("sex");
    node->getFastAttributeString( sex_string, sex ); // optional
    if( sex == "both" )
    {
        mSex = SEX_BOTH;
    }
    else if( sex == "male" )
    {
        mSex = SEX_MALE;
    }
    else if( sex == "female" )
    {
        mSex = SEX_FEMALE;
    }
    else
    {
        LL_WARNS() << "Avatar file:  has invalid sex attribute: " << sex << LL_ENDL;
        return false;
    }
    // attribute: name
    static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
    if( !node->getFastAttributeString( name_string, mName ) )
    {
        LL_WARNS() << "Avatar file:  is missing name attribute" << LL_ENDL;
        return false;
    }
    // attribute: label
    static LLStdStringHandle label_string = LLXmlTree::addAttributeString("label");
    if( !node->getFastAttributeString( label_string, mDisplayName ) )
    {
        mDisplayName = mName;
    }
    // JC - make sure the display name includes the capitalization in the XML file,
    // not the lowercased version.
    LLStringUtil::toLower(mName);
    // attribute: label_min
    static LLStdStringHandle label_min_string = LLXmlTree::addAttributeString("label_min");
    if( !node->getFastAttributeString( label_min_string, mMinName ) )
    {
        mMinName = "Less";
    }
    // attribute: label_max
    static LLStdStringHandle label_max_string = LLXmlTree::addAttributeString("label_max");
    if( !node->getFastAttributeString( label_max_string, mMaxName ) )
    {
        mMaxName = "More";
    }
    return true;
}
//virtual
void LLVisualParamInfo::toStream(std::ostream &out)
{
    out <<  mID << "\t";
    out << mName << "\t";
    out <<  mDisplayName << "\t";
    out <<  mMinName << "\t";
    out <<  mMaxName << "\t";
    out <<  mGroup << "\t";
    out <<  mMinWeight << "\t";
    out <<  mMaxWeight << "\t";
    out <<  mDefaultWeight << "\t";
    out <<  mSex << "\t";
}
//-----------------------------------------------------------------------------
// LLVisualParam()
//-----------------------------------------------------------------------------
LLVisualParam::LLVisualParam()
    : mCurWeight( 0.f ),
    mLastWeight( 0.f ),
    mNext( NULL ),
    mTargetWeight( 0.f ),
    mIsAnimating( false ),
    mIsDummy(false),
    mID( -1 ),
    mInfo( 0 ),
    mParamLocation(LOC_UNKNOWN)
{
}
//-----------------------------------------------------------------------------
// LLVisualParam()
//-----------------------------------------------------------------------------
LLVisualParam::LLVisualParam(const LLVisualParam& pOther)
    : mCurWeight(pOther.mCurWeight),
    mLastWeight(pOther.mLastWeight),
    mNext(pOther.mNext),
    mTargetWeight(pOther.mTargetWeight),
    mIsAnimating(pOther.mIsAnimating),
    mIsDummy(pOther.mIsDummy),
    mID(pOther.mID),
    mInfo(pOther.mInfo),
    mParamLocation(pOther.mParamLocation)
{
}
//-----------------------------------------------------------------------------
// ~LLVisualParam()
//-----------------------------------------------------------------------------
LLVisualParam::~LLVisualParam()
{
    delete mNext;
    mNext = NULL;
}
/*
//=============================================================================
// These virtual functions should always be overridden,
// but are included here for use as templates
//=============================================================================
//-----------------------------------------------------------------------------
// setInfo()
//-----------------------------------------------------------------------------
bool LLVisualParam::setInfo(LLVisualParamInfo *info)
{
    llassert(mInfo == NULL);
    if (info->mID < 0)
        return false;
    mInfo = info;
    mID = info->mID;
    setWeight(getDefaultWeight(), false );
    return true;
}
//-----------------------------------------------------------------------------
// parseData()
//-----------------------------------------------------------------------------
bool LLVisualParam::parseData(LLXmlTreeNode *node)
{
    LLVisualParamInfo *info = new LLVisualParamInfo;
    info->parseXml(node);
    if (!setInfo(info))
        return false;
    return true;
}
*/
//-----------------------------------------------------------------------------
// setWeight()
//-----------------------------------------------------------------------------
void LLVisualParam::setWeight(F32 weight)
{
    if (mIsAnimating)
    {
        //RN: allow overshoot
        mCurWeight = weight;
    }
    else if (mInfo)
    {
        mCurWeight = llclamp(weight, mInfo->mMinWeight, mInfo->mMaxWeight);
    }
    else
    {
        mCurWeight = weight;
    }
    if (mNext)
    {
        mNext->setWeight(weight);
    }
}
//-----------------------------------------------------------------------------
// setAnimationTarget()
//-----------------------------------------------------------------------------
void LLVisualParam::setAnimationTarget(F32 target_value)
{
    // don't animate dummy parameters
    if (mIsDummy)
    {
        setWeight(target_value);
        mTargetWeight = mCurWeight;
        return;
    }
    if (mInfo)
    {
        if (isTweakable())
        {
            mTargetWeight = llclamp(target_value, mInfo->mMinWeight, mInfo->mMaxWeight);
        }
    }
    else
    {
        mTargetWeight = target_value;
    }
    mIsAnimating = true;
    if (mNext)
    {
        mNext->setAnimationTarget(target_value);
    }
}
//-----------------------------------------------------------------------------
// setNextParam()
//-----------------------------------------------------------------------------
void LLVisualParam::setNextParam( LLVisualParam *next )
{
    llassert(!mNext);
    llassert(getWeight() == getDefaultWeight()); // need to establish mNext before we start changing values on this, else initial value won't get mirrored (we can fix that, but better to forbid this pattern)
    mNext = next;
}
//-----------------------------------------------------------------------------
// clearNextParam()
//-----------------------------------------------------------------------------
void LLVisualParam::clearNextParam()
{
    mNext = NULL;
}
//-----------------------------------------------------------------------------
// animate()
//-----------------------------------------------------------------------------
void LLVisualParam::animate( F32 delta)
{
    if (mIsAnimating)
    {
        F32 new_weight = ((mTargetWeight - mCurWeight) * delta) + mCurWeight;
        setWeight(new_weight);
    }
}
//-----------------------------------------------------------------------------
// stopAnimating()
//-----------------------------------------------------------------------------
void LLVisualParam::stopAnimating()
{
    if (mIsAnimating && isTweakable())
    {
        mIsAnimating = false;
        setWeight(mTargetWeight);
    }
}
//virtual
bool LLVisualParam::linkDrivenParams(visual_param_mapper mapper, bool only_cross_params)
{
    // nothing to do for non-driver parameters
    return true;
}
//virtual
void LLVisualParam::resetDrivenParams()
{
    // nothing to do for non-driver parameters
    return;
}
const std::string param_location_name(const EParamLocation& loc)
{
    switch (loc)
    {
        case LOC_UNKNOWN: return "unknown";
        case LOC_AV_SELF: return "self";
        case LOC_AV_OTHER: return "other";
        case LOC_WEARABLE: return "wearable";
        default: return "error";
    }
}
void LLVisualParam::setParamLocation(EParamLocation loc)
{
    if (mParamLocation == LOC_UNKNOWN || loc == LOC_UNKNOWN)
    {
        mParamLocation = loc;
    }
    else if (mParamLocation == loc)
    {
        // no action
    }
    else
    {
        LL_DEBUGS() << "param location is already " << mParamLocation << ", not slamming to " << loc << LL_ENDL;
    }
}