//
// Copyright 2016 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://openusd.org/license.
//
#ifndef PXR_USD_USD_GEOM_CONSTRAINT_TARGET_H
#define PXR_USD_USD_GEOM_CONSTRAINT_TARGET_H

#include "pxr/pxr.h"
#include "pxr/usd/usdGeom/api.h"
#include "pxr/usd/usd/attribute.h"

#include <string>

PXR_NAMESPACE_OPEN_SCOPE


class GfMatrix4d;
class UsdGeomXformCache;

/// \class UsdGeomConstraintTarget
///
/// Schema wrapper for UsdAttribute for authoring and introspecting
/// attributes that are constraint targets.
/// 
/// Constraint targets correspond roughly to what some DCC's call locators.
/// They are coordinate frames, represented as (animated or static) GfMatrix4d
/// values.  We represent them as attributes in USD rather than transformable
/// prims because generally we require no other coordinated information about 
/// a constraint target other than its name and its matrix value, and because
/// attributes are more concise than prims.
///
/// Because consumer clients often care only about the identity and value of
/// constraint targets and may be able to usefully consume them without caring
/// about the actual geometry with which they may logically correspond,
/// UsdGeom aggregates all constraint targets onto a model's root prim,
/// assuming that an exporter will use property namespacing within the 
/// constraint target attribute's name to indicate a path to a prim within 
/// the model with which the constraint target may correspond.
///
/// To facilitate instancing, and also position-tweaking of baked assets, we
/// stipulate that constraint target values always be recorded in
/// <b>model-relative transformation space</b>.  In other words, to get the
/// world-space value of a constraint target, transform it by the
/// local-to-world transformation of the prim on which it is recorded.  
/// ComputeInWorldSpace() will perform this calculation.
/// 
/// \todo Provide API for extracting prim or property path from a target's
/// namespaced name.
///
class UsdGeomConstraintTarget
{
public:
  
    // Default constructor returns an invalid ConstraintTarget.  Exists for 
    // container classes
    UsdGeomConstraintTarget()
    {
        /* NOTHING */
    }
    
    /// Speculative constructor that will produce a valid
    /// UsdGeomConstraintTarget when \p attr already represents an attribute
    /// that is a UsdGeomConstraintTarget, and produces an \em invalid
    /// UsdGeomConstraintTarget otherwise (i.e.  
    /// \ref UsdGeomConstraintTarget_explicit_bool will return false).
    ///
    /// Calling \c UsdGeomConstraintTarget::IsValid(attr) will return the
    /// same truth value as the object returned by this constructor, but if
    /// you plan to subsequently use the ConstraintTarget anyways, just 
    /// construct the object and bool-evaluate it before proceeding.
    USDGEOM_API
    explicit UsdGeomConstraintTarget(const UsdAttribute &attr);

    /// Test whether a given UsdAttribute represents valid ConstraintTarget, 
    /// which implies that creating a UsdGeomConstraintTarget from the attribute 
    /// will succeed.
    ///
    /// Success implies that \c attr.IsDefined() is true.
    USDGEOM_API
    static bool IsValid(const UsdAttribute &attr);

    // ---------------------------------------------------------------
    /// \name UsdAttribute API
    // ---------------------------------------------------------------
    
    /// Allow UsdGeomConstraintTarget to auto-convert to UsdAttribute, so you 
    /// can pass a UsdGeomConstraintTarget to any function that accepts a 
    /// UsdAttribute or const-ref thereto.
    operator UsdAttribute const& () const { return _attr; }

    /// Explicit UsdAttribute extractor
    UsdAttribute const &GetAttr() const { return _attr; }
    
    /// Return true if the wrapped UsdAttribute::IsDefined(), and in
    /// addition the attribute is identified as a ConstraintTarget.
    bool IsDefined() const { return IsValid(_attr); }

    
    /// \anchor UsdGeomConstraintTarget_explicit_bool
    /// Explicit bool conversion operator. A ConstraintTarget object converts
    /// to \c true iff it is valid for querying and authoring values and 
    /// metadata (which is identically equivalent to IsDefined()). It converts
    /// to \c false otherwise.
    explicit operator bool() const {
        return IsDefined();
    }

    /// Get the attribute value of the ConstraintTarget at \p time 
    USDGEOM_API
    bool Get(GfMatrix4d* value, UsdTimeCode time = UsdTimeCode::Default()) 
            const;

    /// Set the attribute value of the ConstraintTarget at \p time 
    USDGEOM_API
    bool Set(const GfMatrix4d& value, UsdTimeCode time = UsdTimeCode::Default()) 
            const;

    /// Get the stored identifier unique to the enclosing model's namespace for
    /// this constraint target.
    /// \sa SetIdentifier()
    USDGEOM_API
    TfToken GetIdentifier() const;

    /// Explicitly sets the stored identifier to the given string. Clients are
    /// responsible for ensuring the uniqueness of this identifier within the
    /// enclosing model's namespace.
    USDGEOM_API
    void SetIdentifier(const TfToken &identifier);

    /// Returns the fully namespaced constraint attribute name, given the 
    /// constraint name.
    USDGEOM_API
    static TfToken GetConstraintAttrName(const std::string &constraintName);

    /// Computes the value of the constraint target in world space.
    /// 
    /// If a valid UsdGeomXformCache is provided in the argument \p xfCache, 
    /// it is used to evaluate the CTM of the model to which the constraint
    /// target belongs. 
    /// 
    /// To get the constraint value in model-space (or local space), simply 
    /// use UsdGeomConstraintTarget::Get(), since the authored values must 
    /// already be in model-space.
    /// 
    USDGEOM_API
    GfMatrix4d ComputeInWorldSpace(UsdTimeCode time=UsdTimeCode::Default(),
                                   UsdGeomXformCache *xfCache=NULL) const;

private:

    UsdAttribute _attr;
};


PXR_NAMESPACE_CLOSE_SCOPE

#endif // USD_CONSTRAINT_TARGET_H
