/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/

#include <AzToolsFramework/Debug/TraceContext.h>

#include <Cry_Geo.h> // Needed for CGFContent.h
#include <CGFContent.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Math/Color.h>

#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexColorData.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/IClothRule.h>

#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>

#include <Pipeline/RCExt/CgfClothExporter.h>

namespace NvCloth
{
    namespace Pipeline
    {
        namespace
        {
            // Index for the Vertex color stream that contains the cloth inverse masses.
            const int ClothVertexBufferStreamIndex = 1;
        }

        CgfClothExporter::CgfClothExporter()
        {
            // Binding the processing functions so when exporters call
            // SceneAPI::Events::Process<Context>() these functions will
            // get called if their Context was used.
            BindToCall(&CgfClothExporter::ProcessMeshNodeContext);
            BindToCall(&CgfClothExporter::ProcessContainerContext);
        }

        void CgfClothExporter::Reflect(AZ::ReflectContext* context)
        {
            AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
            if (serializeContext)
            {
                serializeContext->Class<CgfClothExporter, AZ::SceneAPI::SceneCore::RCExportingComponent>()->Version(1);
            }
        }

        AZ::SceneAPI::Events::ProcessingResult CgfClothExporter::ProcessContainerContext(AZ::RC::ContainerExportContext& context) const
        {
            if (!context.m_group.GetRuleContainerConst().ContainsRuleOfType<AZ::SceneAPI::DataTypes::IClothRule>())
            {
                return AZ::SceneAPI::Events::ProcessingResult::Ignored;
            }

            if (context.m_phase == AZ::RC::Phase::Finalizing)
            {
                if (context.m_container.GetExportInfo()->bMergeAllNodes)
                {
                    AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow,
                        "Mesh group '%s' has cloth rules and trying to merge all nodes.",
                        context.m_group.GetName().c_str());
                    return AZ::SceneAPI::Events::ProcessingResult::Failure;
                }
            }
            else
            {
                // If the current mesh group contains a cloth rule it should not merge all the nodes.
                context.m_container.GetExportInfo()->bMergeAllNodes = false;
            }

            return AZ::SceneAPI::Events::ProcessingResult::Success;
        }

        AZ::SceneAPI::Events::ProcessingResult CgfClothExporter::ProcessMeshNodeContext(AZ::RC::MeshNodeExportContext& context) const
        {
            if (context.m_phase != AZ::RC::Phase::Filling)
            {
                return AZ::SceneAPI::Events::ProcessingResult::Ignored;
            }

            AZStd::vector<AZ::Color> clothData =
                AZ::SceneAPI::DataTypes::IClothRule::FindClothData(
                    context.m_scene.GetGraph(),
                    context.m_nodeIndex,
                    static_cast<size_t>(context.m_mesh.GetVertexCount()),
                    context.m_group.GetRuleContainerConst());

            if (!clothData.empty())
            {
                const int numVertices = context.m_mesh.GetVertexCount();

                // Allocate and get the vertex color stream for cloth
                context.m_mesh.ReallocStream(CMesh::COLORS, ClothVertexBufferStreamIndex, numVertices);
                auto meshColorStream = context.m_mesh.GetStreamPtr<SMeshColor>(CMesh::COLORS, ClothVertexBufferStreamIndex);
                AZ_Assert(meshColorStream, "Mesh color stream is invalid");

                for (int i = 0; i < numVertices; ++i)
                {
                    const auto& clothVertexData = clothData[i];
                    meshColorStream[i] = SMeshColor(
                        clothVertexData.GetR8(),
                        clothVertexData.GetG8(),
                        clothVertexData.GetB8(),
                        clothVertexData.GetA8());
                }
            }

            return AZ::SceneAPI::Events::ProcessingResult::Success;
        }
    } // namespace Pipeline
} // namespace NvCloth