/**
 * @file lljoint.cpp
 * @brief Implementation of LLJoint 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 "lljoint.h"

#include "llmath.h"
#include "llcallstack.h"
#include <boost/algorithm/string.hpp>

S32 LLJoint::sNumUpdates = 0;
S32 LLJoint::sNumTouches = 0;

template <class T>
bool attachment_map_iter_compare_key(const T& a, const T& b)
{
    return a.first < b.first;
}

bool LLVector3OverrideMap::findActiveOverride(LLUUID& mesh_id, LLVector3& pos) const
{
    pos = LLVector3(0,0,0);
    mesh_id = LLUUID();
    bool found = false;

    map_type::const_iterator it = std::max_element(m_map.begin(),
                                                   m_map.end(),
                                                   attachment_map_iter_compare_key<map_type::value_type>);
    if (it != m_map.end())
    {
        found = true;
        pos = it->second;
        mesh_id = it->first;
    }
    return found;
}

void LLVector3OverrideMap::showJointVector3Overrides( std::ostringstream& os ) const
{
    map_type::const_iterator max_it = std::max_element(m_map.begin(),
                                                       m_map.end(),
                                                       attachment_map_iter_compare_key<map_type::value_type>);
    for (const map_type::value_type& pos_pair : m_map)
    {
        const LLVector3& pos = pos_pair.second;
        os << " " << "[" << pos_pair.first <<": " << pos << "]" << ((pos_pair==(*max_it)) ? "*" : "");
    }
}

U32 LLVector3OverrideMap::count() const
{
    return m_map.size();
}

void LLVector3OverrideMap::add(const LLUUID& mesh_id, const LLVector3& pos)
{
    m_map[mesh_id] = pos;
}

bool LLVector3OverrideMap::remove(const LLUUID& mesh_id)
{
    U32 remove_count = m_map.erase(mesh_id);
    return (remove_count > 0);
}

void LLVector3OverrideMap::clear()
{
    m_map.clear();
}

//-----------------------------------------------------------------------------
// LLJoint()
// Class Constructor
//-----------------------------------------------------------------------------


void LLJoint::init()
{
    mName = "unnamed";
    mParent = NULL;
    mXform.setScaleChildOffset(TRUE);
    mXform.setScale(LLVector3(1.0f, 1.0f, 1.0f));
    mDirtyFlags = MATRIX_DIRTY | ROTATION_DIRTY | POSITION_DIRTY;
    mUpdateXform = TRUE;
    mSupport = SUPPORT_BASE;
    mEnd = LLVector3(0.0f, 0.0f, 0.0f);
}

LLJoint::LLJoint() :
    mJointNum(-1)
{
    init();
    touch();
}

LLJoint::LLJoint(S32 joint_num) :
    mJointNum(joint_num)
{
    init();
    touch();
}

//-----------------------------------------------------------------------------
// LLJoint()
// Class Constructor
//-----------------------------------------------------------------------------
LLJoint::LLJoint(const std::string &name, LLJoint *parent) :
    mJointNum(-2)
{
    init();
    mUpdateXform = FALSE;

    setName(name);
    if (parent)
    {
        parent->addChild( this );
    }
    touch();
}

//-----------------------------------------------------------------------------
// ~LLJoint()
// Class Destructor
//-----------------------------------------------------------------------------
LLJoint::~LLJoint()
{
    if (mParent)
    {
        mParent->removeChild( this );
    }
    removeAllChildren();
}


//-----------------------------------------------------------------------------
// setup()
//-----------------------------------------------------------------------------
void LLJoint::setup(const std::string &name, LLJoint *parent)
{
    setName(name);
    if (parent)
    {
        parent->addChild( this );
    }
}

//-----------------------------------------------------------------------------
// setSupport()
//-----------------------------------------------------------------------------
void LLJoint::setSupport(const std::string& support_name)
{
    if (support_name == "extended")
    {
        setSupport(SUPPORT_EXTENDED);
    }
    else if (support_name == "base")
    {
        setSupport(SUPPORT_BASE);
    }
    else
    {
        LL_WARNS() << "unknown support string " << support_name << LL_ENDL;
        setSupport(SUPPORT_BASE);
    }
}


//-----------------------------------------------------------------------------
// touch()
// Sets all dirty flags for all children, recursively.
//-----------------------------------------------------------------------------
void LLJoint::touch(U32 flags)
{
    if ((flags | mDirtyFlags) != mDirtyFlags)
    {
        sNumTouches++;
        mDirtyFlags |= flags;
        U32 child_flags = flags;
        if (flags & ROTATION_DIRTY)
        {
            child_flags |= POSITION_DIRTY;
        }

        for (LLJoint* joint : mChildren)
        {
            joint->touch(child_flags);
        }
    }
}

//-----------------------------------------------------------------------------
// setJointNum()
//-----------------------------------------------------------------------------
void LLJoint::setJointNum(S32 joint_num)
{
    mJointNum = joint_num;
    if (mJointNum + 2 >= LL_CHARACTER_MAX_ANIMATED_JOINTS)
    {
        LL_INFOS() << "LL_CHARACTER_MAX_ANIMATED_JOINTS needs to be increased" << LL_ENDL;
        LL_ERRS() << "joint_num " << joint_num << " + 2 is too large for " << LL_CHARACTER_MAX_ANIMATED_JOINTS << LL_ENDL;
    }
}
//-----------------------------------------------------------------------------
// getRoot()
//-----------------------------------------------------------------------------
LLJoint *LLJoint::getRoot()
{
    if ( getParent() == NULL )
    {
        return this;
    }
    return getParent()->getRoot();
}


//-----------------------------------------------------------------------------
// findJoint()
//-----------------------------------------------------------------------------
LLJoint *LLJoint::findJoint( const std::string &name )
{
    if (name == getName())
        return this;

    for (LLJoint* joint : mChildren)
    {
        LLJoint *found = joint->findJoint(name);
        if (found)
        {
            return found;
        }
    }

    return NULL;
}


//--------------------------------------------------------------------
// addChild()
//--------------------------------------------------------------------
void LLJoint::addChild(LLJoint* joint)
{
    if (joint->mParent)
        joint->mParent->removeChild(joint);

    mChildren.push_back(joint);
    joint->mXform.setParent(&mXform);
    joint->mParent = this;
    joint->touch();
}


//--------------------------------------------------------------------
// removeChild()
//--------------------------------------------------------------------
void LLJoint::removeChild(LLJoint* joint)
{
    joints_t::iterator iter = std::find(mChildren.begin(), mChildren.end(), joint);
    if (iter != mChildren.end())
    {
        mChildren.erase(iter);

        joint->mXform.setParent(NULL);
        joint->mParent = NULL;
        joint->touch();
    }
}


//--------------------------------------------------------------------
// removeAllChildren()
//--------------------------------------------------------------------
void LLJoint::removeAllChildren()
{
    for (LLJoint* joint : mChildren)
    {
        if (joint)
        {
            joint->mXform.setParent(NULL);
            joint->mParent = NULL;
            joint->touch();
            //delete joint;
        }
    }
    mChildren.clear();
}


//--------------------------------------------------------------------
// getPosition()
//--------------------------------------------------------------------
const LLVector3& LLJoint::getPosition()
{
    return mXform.getPosition();
}

bool do_debug_joint(const std::string& name)
{
    if (std::find(LLJoint::s_debugJointNames.begin(), LLJoint::s_debugJointNames.end(),name) != LLJoint::s_debugJointNames.end())
    {
        return true;
    }
    return false;
}

//--------------------------------------------------------------------
// setPosition()
//--------------------------------------------------------------------
void LLJoint::setPosition( const LLVector3& requested_pos, bool apply_attachment_overrides )
{
    LLVector3 pos(requested_pos);

    LLVector3 active_override;
    LLUUID mesh_id;
    if (apply_attachment_overrides && m_attachmentPosOverrides.findActiveOverride(mesh_id,active_override))
    {
        if (pos != active_override && do_debug_joint(getName()))
        {
            LLScopedContextString str("setPosition");
            LL_DEBUGS("Avatar") << " joint " << getName() << " requested_pos " << requested_pos
                                << " overriden by attachment " << active_override << LL_ENDL;
        }
        pos = active_override;
    }
    if ((pos != getPosition()) && do_debug_joint(getName()))
    {
        LLScopedContextString str("setPosition");
        LLCallStack cs;
        LLContextStatus con_status;
        LL_DEBUGS("Avatar") << " joint " << getName() << " set pos " << pos << LL_ENDL;
        LL_DEBUGS("Avatar") << "CONTEXT:\n" << "====================\n" << con_status << "====================" << LL_ENDL;
        LL_DEBUGS("Avatar") << "STACK:\n" << "====================\n" << cs << "====================" << LL_ENDL;
    }
    if (pos != getPosition())
    {
        mXform.setPosition(pos);
        touch(MATRIX_DIRTY | POSITION_DIRTY);
    }
}

void LLJoint::setDefaultPosition( const LLVector3& pos )
{
    mDefaultPosition = pos;
}

const LLVector3& LLJoint::getDefaultPosition() const
{
    return mDefaultPosition;
}

void LLJoint::setDefaultScale( const LLVector3& scale )
{
    mDefaultScale = scale;
}

const LLVector3& LLJoint::getDefaultScale() const
{
    return mDefaultScale;
}

void showJointPosOverrides( const LLJoint& joint, const std::string& note, const std::string& av_info )
{
        std::ostringstream os;
        os << joint.m_posBeforeOverrides;
        joint.m_attachmentPosOverrides.showJointVector3Overrides(os);
        LL_DEBUGS("Avatar") << av_info << " joint " << joint.getName() << " " << note << " " << os.str() << LL_ENDL;
}

void showJointScaleOverrides( const LLJoint& joint, const std::string& note, const std::string& av_info )
{
        std::ostringstream os;
        os << joint.m_scaleBeforeOverrides;
        joint.m_attachmentScaleOverrides.showJointVector3Overrides(os);
        LL_DEBUGS("Avatar") << av_info << " joint " << joint.getName() << " " << note << " " << os.str() << LL_ENDL;
}

bool LLJoint::aboveJointPosThreshold(const LLVector3& pos) const
{
    LLVector3 diff = pos - getDefaultPosition();
    const F32 max_joint_pos_offset = LL_JOINT_TRESHOLD_POS_OFFSET; // 0.1 mm
    return diff.lengthSquared() > max_joint_pos_offset * max_joint_pos_offset;
}

bool LLJoint::aboveJointScaleThreshold(const LLVector3& scale) const
{
    LLVector3 diff = scale - getDefaultScale();
    const F32 max_joint_scale_offset = 0.0001f; // 0.1 mm
    return diff.lengthSquared() > max_joint_scale_offset * max_joint_scale_offset;
}

//--------------------------------------------------------------------
// addAttachmentPosOverride()
//--------------------------------------------------------------------
void LLJoint::addAttachmentPosOverride( const LLVector3& pos, const LLUUID& mesh_id, const std::string& av_info, bool& active_override_changed )
{
    active_override_changed = false;
    if (mesh_id.isNull())
    {
        return;
    }
    LLVector3 before_pos;
    LLUUID before_mesh_id;
    bool has_active_override_before = hasAttachmentPosOverride( before_pos, before_mesh_id );
    if (!m_attachmentPosOverrides.count())
    {
        if (do_debug_joint(getName()))
        {
            LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " saving m_posBeforeOverrides " << getPosition() << LL_ENDL;
        }
        m_posBeforeOverrides = getPosition();
    }
    m_attachmentPosOverrides.add(mesh_id,pos);
    LLVector3 after_pos;
    LLUUID after_mesh_id;
    hasAttachmentPosOverride(after_pos, after_mesh_id);
    if (!has_active_override_before || (after_pos != before_pos))
    {
        active_override_changed = true;
        if (do_debug_joint(getName()))
        {
            LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " addAttachmentPosOverride for mesh " << mesh_id << " pos " << pos << LL_ENDL;
        }
        updatePos(av_info);
    }
}

//--------------------------------------------------------------------
// removeAttachmentPosOverride()
//--------------------------------------------------------------------
void LLJoint::removeAttachmentPosOverride( const LLUUID& mesh_id, const std::string& av_info, bool& active_override_changed )
{
    active_override_changed = false;
    if (mesh_id.isNull())
    {
        return;
    }
    LLVector3 before_pos;
    LLUUID before_mesh_id;
    hasAttachmentPosOverride( before_pos, before_mesh_id );
    if (m_attachmentPosOverrides.remove(mesh_id))
    {
        LLVector3 after_pos;
        LLUUID after_mesh_id;
        bool has_active_override_after = hasAttachmentPosOverride(after_pos, after_mesh_id);
        if (!has_active_override_after || (after_pos != before_pos))
        {
            active_override_changed = true;
            if (do_debug_joint(getName()))
            {
                LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName()
                                    << " removeAttachmentPosOverride for " << mesh_id << LL_ENDL;
                showJointPosOverrides(*this, "remove", av_info);
            }
            updatePos(av_info);
        }
    }
}

//--------------------------------------------------------------------
 // hasAttachmentPosOverride()
 //--------------------------------------------------------------------
bool LLJoint::hasAttachmentPosOverride( LLVector3& pos, LLUUID& mesh_id ) const
{
    return m_attachmentPosOverrides.findActiveOverride(mesh_id,pos);
}

//--------------------------------------------------------------------
// clearAttachmentPosOverrides()
//--------------------------------------------------------------------
void LLJoint::clearAttachmentPosOverrides()
{
    if (m_attachmentPosOverrides.count())
    {
        m_attachmentPosOverrides.clear();
        setPosition(m_posBeforeOverrides);
    }
}

//--------------------------------------------------------------------
// getAllAttachmentPosOverrides()
//--------------------------------------------------------------------
void LLJoint::getAllAttachmentPosOverrides(S32& num_pos_overrides,
                                           std::set<LLVector3>& distinct_pos_overrides) const
{
    num_pos_overrides = m_attachmentPosOverrides.count();
    for (const LLVector3OverrideMap::map_type::value_type& pos_override_pair : m_attachmentPosOverrides.getMap())
    {
        distinct_pos_overrides.insert(pos_override_pair.second);
    }
}

//--------------------------------------------------------------------
// getAllAttachmentScaleOverrides()
//--------------------------------------------------------------------
void LLJoint::getAllAttachmentScaleOverrides(S32& num_scale_overrides,
                                             std::set<LLVector3>& distinct_scale_overrides) const
{
    num_scale_overrides = m_attachmentScaleOverrides.count();
    for (const LLVector3OverrideMap::map_type::value_type& scale_override_pair : m_attachmentScaleOverrides.getMap())
    {
        distinct_scale_overrides.insert(scale_override_pair.second);
    }
}

//--------------------------------------------------------------------
// showAttachmentPosOverrides()
//--------------------------------------------------------------------
void LLJoint::showAttachmentPosOverrides(const std::string& av_info) const
{
    LLVector3 active_override;
    bool has_active_override;
    LLUUID mesh_id;
    has_active_override = m_attachmentPosOverrides.findActiveOverride(mesh_id,active_override);
    U32 count = m_attachmentPosOverrides.count();
    if (count==1)
    {
        LLVector3OverrideMap::map_type::const_iterator it = m_attachmentPosOverrides.getMap().begin();
        std::string highlight = (has_active_override && (it->second == active_override)) ? "*" : "";
        LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName()
                            << " has single attachment pos override " << highlight << "" << it->second << " default " << mDefaultPosition << LL_ENDL;
    }
    else if (count>1)
    {
        LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " has " << count << " attachment pos overrides" << LL_ENDL;
        std::set<LLVector3> distinct_offsets;
        for (const LLVector3OverrideMap::map_type::value_type& pos_override_pair : m_attachmentPosOverrides.getMap())
        {
            distinct_offsets.insert(pos_override_pair.second);
        }
        if (distinct_offsets.size()>1)
        {
            LL_DEBUGS("Avatar") << "CONFLICTS, " << distinct_offsets.size() << " different values" << LL_ENDL;
        }
        else
        {
            LL_DEBUGS("Avatar") << "no conflicts" << LL_ENDL;
        }
        for (const LLVector3& offset : distinct_offsets)
        {
            std::string highlight = (has_active_override && offset == active_override) ? "*" : "";
            LL_DEBUGS("Avatar") << "  POS " << highlight << "" << offset << " default " << mDefaultPosition << LL_ENDL;
        }
    }
}

//--------------------------------------------------------------------
// updatePos()
//--------------------------------------------------------------------
void LLJoint::updatePos(const std::string& av_info)
{
    LLVector3 pos, found_pos;
    LLUUID mesh_id;
    if (m_attachmentPosOverrides.findActiveOverride(mesh_id,found_pos))
    {
        if (do_debug_joint(getName()))
        {
            LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " updatePos, winner of " << m_attachmentPosOverrides.count() << " is mesh " << mesh_id << " pos " << found_pos << LL_ENDL;
        }
        pos = found_pos;
    }
    else
    {
        if (do_debug_joint(getName()))
        {
            LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " updatePos, winner is posBeforeOverrides " << m_posBeforeOverrides << LL_ENDL;
        }
        pos = m_posBeforeOverrides;
    }
    setPosition(pos);
}

//--------------------------------------------------------------------
// updateScale()
//--------------------------------------------------------------------
void LLJoint::updateScale(const std::string& av_info)
{
    LLVector3 scale, found_scale;
    LLUUID mesh_id;
    if (m_attachmentScaleOverrides.findActiveOverride(mesh_id,found_scale))
    {
        if (do_debug_joint(getName()))
        {
            LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " updateScale, winner of " << m_attachmentScaleOverrides.count() << " is mesh " << mesh_id << " scale " << found_scale << LL_ENDL;
        }
        scale = found_scale;
    }
    else
    {
        if (do_debug_joint(getName()))
        {
            LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " updateScale, winner is scaleBeforeOverrides " << m_scaleBeforeOverrides << LL_ENDL;
        }
        scale = m_scaleBeforeOverrides;
    }
    setScale(scale);
}

//--------------------------------------------------------------------
// addAttachmentScaleOverride()
//--------------------------------------------------------------------
void LLJoint::addAttachmentScaleOverride( const LLVector3& scale, const LLUUID& mesh_id, const std::string& av_info )
{
    if (mesh_id.isNull())
    {
        return;
    }
    if (!m_attachmentScaleOverrides.count())
    {
        if (do_debug_joint(getName()))
        {
            LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " saving m_scaleBeforeOverrides " << getScale() << LL_ENDL;
        }
        m_scaleBeforeOverrides = getScale();
    }
    m_attachmentScaleOverrides.add(mesh_id,scale);
    if (do_debug_joint(getName()))
    {
        LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " addAttachmentScaleOverride for mesh " << mesh_id << " scale " << scale << LL_ENDL;
    }
    updateScale(av_info);
}

//--------------------------------------------------------------------
// removeAttachmentScaleOverride()
//--------------------------------------------------------------------
void LLJoint::removeAttachmentScaleOverride( const LLUUID& mesh_id, const std::string& av_info )
{
    if (mesh_id.isNull())
    {
        return;
    }
    if (m_attachmentScaleOverrides.remove(mesh_id))
    {
        if (do_debug_joint(getName()))
        {
            LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName()
                                << " removeAttachmentScaleOverride for " << mesh_id << LL_ENDL;
            showJointScaleOverrides(*this, "remove", av_info);
        }
        updateScale(av_info);
    }
}

//--------------------------------------------------------------------
 // hasAttachmentScaleOverride()
 //--------------------------------------------------------------------
bool LLJoint::hasAttachmentScaleOverride( LLVector3& scale, LLUUID& mesh_id ) const
{
    return m_attachmentScaleOverrides.findActiveOverride(mesh_id,scale);
}

//--------------------------------------------------------------------
// clearAttachmentScaleOverrides()
//--------------------------------------------------------------------
void LLJoint::clearAttachmentScaleOverrides()
{
    if (m_attachmentScaleOverrides.count())
    {
        m_attachmentScaleOverrides.clear();
        setScale(m_scaleBeforeOverrides);
    }
}

//--------------------------------------------------------------------
// showAttachmentScaleOverrides()
//--------------------------------------------------------------------
void LLJoint::showAttachmentScaleOverrides(const std::string& av_info) const
{
    LLVector3 active_override;
    bool has_active_override;
    LLUUID mesh_id;
    has_active_override = m_attachmentScaleOverrides.findActiveOverride(mesh_id,active_override);
    U32 count = m_attachmentScaleOverrides.count();
    if (count==1)
    {
        LLVector3OverrideMap::map_type::const_iterator it = m_attachmentScaleOverrides.getMap().begin();
        std::string highlight = (has_active_override && (it->second == active_override)) ? "*" : "";
        LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName()
                            << " has single attachment scale override " << highlight << "" << it->second << " default " << mDefaultScale << LL_ENDL;
    }
    else if (count>1)
    {
        LL_DEBUGS("Avatar") << "av " << av_info << " joint " << getName() << " has " << count << " attachment scale overrides" << LL_ENDL;
        std::set<LLVector3> distinct_offsets;
        for (const LLVector3OverrideMap::map_type::value_type& scale_override_pair : m_attachmentScaleOverrides.getMap())
        {
            distinct_offsets.insert(scale_override_pair.second);
        }
        if (distinct_offsets.size()>1)
        {
            LL_DEBUGS("Avatar") << "CONFLICTS, " << distinct_offsets.size() << " different values" << LL_ENDL;
        }
        else
        {
            LL_DEBUGS("Avatar") << "no conflicts" << LL_ENDL;
        }
        for (const LLVector3& offset : distinct_offsets)
        {
            std::string highlight = (has_active_override && offset == active_override) ? "*" : "";
            LL_DEBUGS("Avatar") << "  POS " << highlight << "" << offset << " default " << mDefaultScale << LL_ENDL;
        }
    }
}

// init static
LLJoint::debug_joint_name_t LLJoint::s_debugJointNames = debug_joint_name_t();

//--------------------------------------------------------------------
// setDebugJointNames
//--------------------------------------------------------------------
void LLJoint::setDebugJointNames(const debug_joint_name_t& names)
{
    s_debugJointNames = names;
}
void LLJoint::setDebugJointNames(const std::string& names_string)
{
    debug_joint_name_t names;
    boost::split(names, names_string, boost::is_any_of(" :,"));
    setDebugJointNames(names);
}

//--------------------------------------------------------------------
// getWorldPosition()
//--------------------------------------------------------------------
LLVector3 LLJoint::getWorldPosition()
{
    updateWorldPRSParent();
    return mXform.getWorldPosition();
}

//-----------------------------------------------------------------------------
// getLastWorldPosition()
//-----------------------------------------------------------------------------
LLVector3 LLJoint::getLastWorldPosition()
{
    return mXform.getWorldPosition();
}
//--------------------------------------------------------------------
// setWorldPosition()
//--------------------------------------------------------------------
void LLJoint::setWorldPosition( const LLVector3& pos )
{
    if (mParent == NULL)
    {
        this->setPosition( pos );
        return;
    }

    LLMatrix4 temp_matrix = getWorldMatrix();
    temp_matrix.mMatrix[VW][VX] = pos.mV[VX];
    temp_matrix.mMatrix[VW][VY] = pos.mV[VY];
    temp_matrix.mMatrix[VW][VZ] = pos.mV[VZ];

    LLMatrix4 parentWorldMatrix = mParent->getWorldMatrix();
    LLMatrix4 invParentWorldMatrix = parentWorldMatrix.invert();

    temp_matrix *= invParentWorldMatrix;

    LLVector3 localPos( temp_matrix.mMatrix[VW][VX],
                        temp_matrix.mMatrix[VW][VY],
                        temp_matrix.mMatrix[VW][VZ] );

    setPosition( localPos );
}


//--------------------------------------------------------------------
// getRotation()
//--------------------------------------------------------------------
const LLQuaternion& LLJoint::getRotation()
{
    return mXform.getRotation();
}


//--------------------------------------------------------------------
// setRotation()
//--------------------------------------------------------------------
void LLJoint::setRotation( const LLQuaternion& rot )
{
    if (rot.isFinite())
    {
    //  if (mXform.getRotation() != rot)
        {
            mXform.setRotation(rot);
            touch(MATRIX_DIRTY | ROTATION_DIRTY);
        }
    }
}


//--------------------------------------------------------------------
// getWorldRotation()
//--------------------------------------------------------------------
LLQuaternion LLJoint::getWorldRotation()
{
    updateWorldPRSParent();

    return mXform.getWorldRotation();
}

//-----------------------------------------------------------------------------
// getLastWorldRotation()
//-----------------------------------------------------------------------------
LLQuaternion LLJoint::getLastWorldRotation()
{
    return mXform.getWorldRotation();
}

//--------------------------------------------------------------------
// setWorldRotation()
//--------------------------------------------------------------------
void LLJoint::setWorldRotation( const LLQuaternion& rot )
{
    if (mParent == NULL)
    {
        this->setRotation( rot );
        return;
    }

    LLMatrix4 temp_mat(rot);

    LLMatrix4 parentWorldMatrix = mParent->getWorldMatrix();
    parentWorldMatrix.mMatrix[VW][VX] = 0;
    parentWorldMatrix.mMatrix[VW][VY] = 0;
    parentWorldMatrix.mMatrix[VW][VZ] = 0;

    LLMatrix4 invParentWorldMatrix = parentWorldMatrix.invert();

    temp_mat *= invParentWorldMatrix;

    setRotation(LLQuaternion(temp_mat));
}


//--------------------------------------------------------------------
// getScale()
//--------------------------------------------------------------------
const LLVector3& LLJoint::getScale()
{
    return mXform.getScale();
}

//--------------------------------------------------------------------
// setScale()
//--------------------------------------------------------------------
void LLJoint::setScale( const LLVector3& requested_scale, bool apply_attachment_overrides )
{
    LLVector3 scale(requested_scale);
    LLUUID mesh_id;
    LLVector3 active_override;
    if (apply_attachment_overrides && m_attachmentScaleOverrides.findActiveOverride(mesh_id,active_override))
    {
        if (scale != active_override && do_debug_joint(getName()))
        {
            LLScopedContextString str("setScale");
            LL_DEBUGS("Avatar") << " joint " << getName() << " requested_scale " << requested_scale
                                << " overriden by attachment " << active_override << LL_ENDL;
        }
        scale = active_override;
    }
    if ((mXform.getScale() != scale) && do_debug_joint(getName()))
    {
        LLScopedContextString str("setScale");
        LLCallStack cs;
        LLContextStatus con_status;
        LL_DEBUGS("Avatar") << " joint " << getName() << " set scale " << scale << LL_ENDL;
        LL_DEBUGS("Avatar") << "CONTEXT:\n" << "====================\n" << con_status << LL_ENDL;
        LL_DEBUGS("Avatar") << "STACK:\n" << "====================\n" << cs << "====================" << LL_ENDL;
    }
    mXform.setScale(scale);
    touch();

}



//--------------------------------------------------------------------
// getWorldMatrix()
//--------------------------------------------------------------------
const LLMatrix4 &LLJoint::getWorldMatrix()
{
    updateWorldMatrixParent();

    return mXform.getWorldMatrix();
}

const LLMatrix4a& LLJoint::getWorldMatrix4a()
{
    updateWorldMatrixParent();

    return mWorldMatrix;
}


//--------------------------------------------------------------------
// setWorldMatrix()
//--------------------------------------------------------------------
void LLJoint::setWorldMatrix( const LLMatrix4& mat )
{
    LL_INFOS() << "WARNING: LLJoint::setWorldMatrix() not correctly implemented yet" << LL_ENDL;
    // extract global translation
    LLVector3 trans(    mat.mMatrix[VW][VX],
                        mat.mMatrix[VW][VY],
                        mat.mMatrix[VW][VZ] );

    // extract global rotation
    LLQuaternion rot( mat );

    setWorldPosition( trans );
    setWorldRotation( rot );
}

//-----------------------------------------------------------------------------
// updateWorldMatrixParent()
//-----------------------------------------------------------------------------
void LLJoint::updateWorldMatrixParent()
{
    if (mDirtyFlags & MATRIX_DIRTY)
    {
        LLJoint *parent = getParent();
        if (parent)
        {
            parent->updateWorldMatrixParent();
        }
        updateWorldMatrix();
    }
}

//-----------------------------------------------------------------------------
// updateWorldPRSParent()
//-----------------------------------------------------------------------------
void LLJoint::updateWorldPRSParent()
{
    if (mDirtyFlags & (ROTATION_DIRTY | POSITION_DIRTY))
    {
        LLJoint *parent = getParent();
        if (parent)
        {
            parent->updateWorldPRSParent();
        }

        mXform.update();
        mDirtyFlags &= ~(ROTATION_DIRTY | POSITION_DIRTY);
    }
}

//-----------------------------------------------------------------------------
// updateWorldMatrixChildren()
//-----------------------------------------------------------------------------
void LLJoint::updateWorldMatrixChildren()
{
    if (!this->mUpdateXform) return;

    if (mDirtyFlags & MATRIX_DIRTY)
    {
        updateWorldMatrix();
    }
    for (LLJoint* joint : mChildren)
    {
        joint->updateWorldMatrixChildren();
    }
}

//-----------------------------------------------------------------------------
// updateWorldMatrix()
//-----------------------------------------------------------------------------
void LLJoint::updateWorldMatrix()
{
    if (mDirtyFlags & MATRIX_DIRTY)
    {
        sNumUpdates++;
        mXform.updateMatrix(FALSE);
        mWorldMatrix.loadu(mXform.getWorldMatrix());
        mDirtyFlags = 0x0;
    }
}

//--------------------------------------------------------------------
// getSkinOffset()
//--------------------------------------------------------------------
const LLVector3 &LLJoint::getSkinOffset()
{
    return mSkinOffset;
}


//--------------------------------------------------------------------
// setSkinOffset()
//--------------------------------------------------------------------
void LLJoint::setSkinOffset( const LLVector3& offset )
{
    mSkinOffset = offset;
}


//-----------------------------------------------------------------------------
// clampRotation()
//-----------------------------------------------------------------------------
void LLJoint::clampRotation(LLQuaternion old_rot, LLQuaternion new_rot)
{
    LLVector3 main_axis(1.f, 0.f, 0.f);

    for (LLJoint* joint : mChildren)
    {
        if (joint->isAnimatable())
        {
            main_axis = joint->getPosition();
            main_axis.normVec();
            // only care about first animatable child
            break;
        }
    }
}

// End