#pragma once

#include "../CommandHistory.h"
#include <pxr/usd/usd/stage.h>
#include <pxr/usd/usd/timeCode.h>
#include <pxr/usd/sdf/path.h>
#include <pxr/usd/sdf/layer.h>
#include <pxr/base/gf/vec3d.h>
#include <pxr/base/gf/vec3f.h>
#include <pxr/usd/usdGeom/xformCommonAPI.h>
#include <string>

namespace UsdLayerManager {

/// Reverses a complete TRS edit on a single prim (from gizmo drag or
/// Property panel field commit).  Stores the prim path, edit-target layer,
/// and pre/post translate/rotate/scale triples.
///
/// When timeCode is not Default() the edit is a keyframe: values are written
/// as time samples at that frame.  Undo removes samples this command created
/// on ops that had no sample at that frame before the edit.
class TransformCommand : public ICommand {
public:
    /// Whether each common op had an authored sample at the edit time.
    /// Must be queried BEFORE the first live write of an interactive edit
    /// (live writes author the sample, so querying later always finds one).
    struct SamplePresence {
        bool translate = false;
        bool rotate    = false;
        bool scale     = false;
    };
    static SamplePresence QuerySamplePresence(
        pxr::UsdStageRefPtr                       stage,
        const pxr::SdfPath&                       primPath,
        pxr::UsdGeomXformCommonAPI::RotationOrder rotOrder,
        pxr::UsdTimeCode                          timeCode);

    TransformCommand(pxr::UsdStageRefPtr       stage,
                     const pxr::SdfPath&        primPath,
                     pxr::SdfLayerHandle        editLayer,
                     const pxr::GfVec3d&        oldTranslate,
                     const pxr::GfVec3f&        oldRotate,
                     const pxr::GfVec3f&        oldScale,
                     const pxr::GfVec3d&        newTranslate,
                     const pxr::GfVec3f&        newRotate,
                     const pxr::GfVec3f&        newScale,
                     pxr::UsdGeomXformCommonAPI::RotationOrder rotOrder,
                     pxr::UsdTimeCode           timeCode,
                     SamplePresence             preEditSamples,
                     std::string                description = "Transform");

    void Execute() override;
    void Undo()    override;
    std::string GetDescription() const override { return m_description; }

private:
    void Apply(const pxr::GfVec3d& t,
               const pxr::GfVec3f& r,
               const pxr::GfVec3f& s);
    /// Remove the time samples Execute created on ops that previously had
    /// no sample at m_timeCode (no-op when m_timeCode is Default).
    void ClearCreatedSamples();

    pxr::UsdStageRefPtr  m_stage;
    pxr::SdfPath         m_primPath;
    pxr::SdfLayerHandle  m_editLayer;

    pxr::GfVec3d  m_oldTranslate;
    pxr::GfVec3f  m_oldRotate;
    pxr::GfVec3f  m_oldScale;

    pxr::GfVec3d  m_newTranslate;
    pxr::GfVec3f  m_newRotate;
    pxr::GfVec3f  m_newScale;

    pxr::UsdGeomXformCommonAPI::RotationOrder m_rotOrder;
    pxr::UsdTimeCode m_timeCode;
    std::string   m_description;

    // Whether each op already had a sample at m_timeCode before the edit
    // (only meaningful when m_timeCode is not Default).
    SamplePresence m_preEditSamples;
};

} // namespace UsdLayerManager
