#include "CreatePrimCommand.h"
#include "../../utils/Logger.h"
#include <pxr/usd/usd/prim.h>
#include <pxr/usd/usdGeom/xformCommonAPI.h>
#include <pxr/usd/usdGeom/metrics.h>
#include <pxr/usd/usdGeom/tokens.h>

namespace UsdLayerManager {

// ---------------------------------------------------------------------------
// ApplyCameraDefaultOrientation
//
// USD cameras always use +Y-up / look-down-−Z in their own local frame.
// For a Y-up stage that is already the standard world forward direction, so
// the identity transform is fine.
//
// For a Z-up stage the identity transform would point the camera straight
// DOWN (world −Z = −up), which produces wrong orientation when the viewport
// switches to the prim, and wrong tumble/roll directions when navigating.
//
// Fix: rotate the camera +90° around its local X axis so that:
//   camera local +Y (up)   → world +Z  (stage up axis)   ✓
//   camera local −Z (view) → world +Y  (horizontal "forward") ✓
// ---------------------------------------------------------------------------
static void ApplyCameraDefaultOrientation(pxr::UsdPrim& prim,
                                           pxr::UsdStageRefPtr stage)
{
    if (!prim.IsValid() || !stage) return;

    bool isZUp = (pxr::UsdGeomGetStageUpAxis(stage) == pxr::UsdGeomTokens->z);
    if (!isZUp) return;   // Y-up default (identity) is already correct

    pxr::UsdGeomXformCommonAPI xformAPI(prim);
    // +90° around X: camera up aligns with world Z, view direction becomes +Y
    xformAPI.SetRotate(pxr::GfVec3f(90.f, 0.f, 0.f),
                       pxr::UsdGeomXformCommonAPI::RotationOrderXYZ,
                       pxr::UsdTimeCode::Default());
}

CreatePrimCommand::CreatePrimCommand(pxr::UsdStageRefPtr stage,
                                     const pxr::SdfPath&  primPath,
                                     const pxr::TfToken&  typeName)
    : m_stage(stage)
    , m_primPath(primPath)
    , m_typeName(typeName)
    , m_description("Create " + typeName.GetString() + " " + primPath.GetString())
{
}

void CreatePrimCommand::Execute() {
    if (!m_stage) return;
    try {
        pxr::UsdPrim prim = m_stage->DefinePrim(m_primPath, m_typeName);
        if (!prim.IsValid()) {
            LOG_ERROR("CreatePrimCommand: failed to define prim " + m_primPath.GetString());
            return;
        }

        // Camera-specific initialisation: orient the prim so the camera looks
        // in the correct "forward" direction for the stage's up-axis.
        if (m_typeName == pxr::TfToken("Camera"))
            ApplyCameraDefaultOrientation(prim, m_stage);

    } catch (const std::exception& e) {
        LOG_ERROR(std::string("CreatePrimCommand::Execute error: ") + e.what());
    }
}

void CreatePrimCommand::Undo() {
    if (!m_stage) return;
    try {
        if (!m_stage->RemovePrim(m_primPath))
            LOG_ERROR("CreatePrimCommand: failed to remove prim " + m_primPath.GetString());
    } catch (const std::exception& e) {
        LOG_ERROR(std::string("CreatePrimCommand::Undo error: ") + e.what());
    }
}

} // namespace UsdLayerManager
