#include "ReparentPrimCommand.h"
#include "../../utils/Logger.h"
#include <pxr/usd/usd/namespaceEditor.h>
#include <pxr/usd/usd/prim.h>
#include <pxr/base/tf/token.h>

PXR_NAMESPACE_USING_DIRECTIVE

namespace UsdLayerManager {

ReparentPrimCommand::ReparentPrimCommand(UsdStageRefPtr     stage,
                                         const SdfPath&     srcPath,
                                         const SdfPath&     dstParentPath,
                                         const std::string& finalName)
    : m_stage(stage)
    , m_srcPath(srcPath)
    , m_origParentPath(srcPath.GetParentPath())
    , m_origName(srcPath.GetName())
    , m_dstParentPath(dstParentPath)
    , m_finalName(finalName)
    , m_description("Reparent " + srcPath.GetString() + " \xe2\x86\x92 " +
                     dstParentPath.GetString() + "/" + finalName)
{
    m_actualNewPath = dstParentPath.AppendChild(TfToken(finalName));
}

static UsdPrim GetParentPrim(UsdStageRefPtr stage, const SdfPath& parentPath) {
    if (parentPath == SdfPath("/") || parentPath == SdfPath::AbsoluteRootPath())
        return stage->GetPseudoRoot();
    return stage->GetPrimAtPath(parentPath);
}

void ReparentPrimCommand::Execute() {
    UsdPrim src = m_stage->GetPrimAtPath(m_srcPath);
    if (!src.IsValid()) {
        LOG_ERROR("ReparentPrimCommand::Execute: source prim not found: " + m_srcPath.GetString());
        return;
    }
    UsdPrim dst = GetParentPrim(m_stage, m_dstParentPath);
    if (!dst.IsValid()) {
        LOG_ERROR("ReparentPrimCommand::Execute: destination parent not found: " + m_dstParentPath.GetString());
        return;
    }
    UsdNamespaceEditor editor(m_stage);
    editor.ReparentPrim(src, dst, TfToken(m_finalName));
    std::string whyNot;
    if (!editor.CanApplyEdits(&whyNot)) {
        LOG_ERROR("ReparentPrimCommand::Execute: cannot reparent " + m_srcPath.GetString() + ": " + whyNot);
        return;
    }
    if (!editor.ApplyEdits()) {
        LOG_ERROR("ReparentPrimCommand::Execute: ApplyEdits failed");
    }
}

void ReparentPrimCommand::Undo() {
    UsdPrim moved = m_stage->GetPrimAtPath(m_actualNewPath);
    if (!moved.IsValid()) {
        LOG_ERROR("ReparentPrimCommand::Undo: moved prim not found: " + m_actualNewPath.GetString());
        return;
    }
    UsdPrim origParent = GetParentPrim(m_stage, m_origParentPath);
    if (!origParent.IsValid()) {
        LOG_ERROR("ReparentPrimCommand::Undo: original parent not found: " + m_origParentPath.GetString());
        return;
    }
    UsdNamespaceEditor editor(m_stage);
    editor.ReparentPrim(moved, origParent, TfToken(m_origName));
    std::string whyNot;
    if (!editor.CanApplyEdits(&whyNot)) {
        LOG_ERROR("ReparentPrimCommand::Undo: cannot reparent back " + m_actualNewPath.GetString() + ": " + whyNot);
        return;
    }
    if (!editor.ApplyEdits()) {
        LOG_ERROR("ReparentPrimCommand::Undo: ApplyEdits failed");
    }
}

} // namespace UsdLayerManager
