/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */
#pragma once

#include "Scene/Geometry.hpp"

#include <sstream>

namespace OpenVulkano::Scene
{
    void WriteUsdContents(std::ostream& output, OpenVulkano::Scene::Geometry* geometry)
    {
        output << R"(#usda 1.0
(
    defaultPrim = "root"
    doc = "Exported from OpenVulkano"
    metersPerUnit = 1
    upAxis = "Y"
)

def Xform "root" (
    customData = {
        dictionary Blender = {
            bool generated = 1
        }
    }
)
{
    def Xform "model"
    {
        custom string userProperties:blender:object_name = "model"
        float3 xformOp:rotateXYZ = (0, -0, 0)
        float3 xformOp:scale = (1, 1, 1)
        double3 xformOp:translate = (0, 0, 0)
        uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ", "xformOp:scale"]

        def Mesh "model" (
            active = true
            prepend apiSchemas = ["MaterialBindingAPI"]
        )
        {
            uniform bool doubleSided = 1
            float3[] extent = [(-0.5, -0.5, 0), (0.5, 0.5, 0)]
            int[] faceVertexCounts = [)";
        
            for (size_t i = 0; i < geometry->indexCount; ++i)
            {
                if ((i + 1) % 3 == 0)
                {
                    if (i > 2) output << ", ";
                    output << "3";
                }
            }

            output << R"(]
            int[] faceVertexIndices = [)";

            for (size_t i = 0; i < geometry->indexCount; ++i)
            {
                if (i > 0) output << ", ";
                output << geometry->GetIndex(i);
            }
            
            output << R"(]
            rel material:binding = </root/_materials/Material0>
            normal3f[] normals = [)";

            output << std::fixed << std::setprecision(6);
            for (size_t i = 0; i < geometry->vertexCount; ++i)
            {
                const auto& v = geometry->vertices[i];
                if (i > 0) output << ", ";
                output << "(" << v.normal.x << ", " << v.normal.y << ", " << v.normal.z << ")";
            }

            output << R"(] (
                interpolation = "faceVarying"
            )
            point3f[] points = [)";
            
            for (size_t i = 0; i < geometry->vertexCount; ++i)
            {
                const auto& v = geometry->vertices[i];
                if (i > 0) output << ", ";
                output << "(" << v.position.x << ", " << v.position.y << ", " << v.position.z << ")";
            }

            output << R"(]
            texCoord2f[] primvars:st = [)";
            
            output << std::fixed << std::setprecision(6);

            for (size_t i = 0; i < geometry->indexCount; ++i)
            {
                const size_t vertexIndex = geometry->GetIndex(i);
                const auto& v = geometry->vertices[vertexIndex];
                if (i > 0) output << ", ";
                output << "(" << v.textureCoordinates.x << ", " << v.textureCoordinates.y << ")";
            }

            output << R"(] (
                interpolation = "faceVarying"
            )
            uniform token subdivisionScheme = "none"
            custom string userProperties:blender:data_name = "model"
        }
    }

    def Scope "_materials"
    {
        def Material "Material0"
        {
            token outputs:surface.connect = </root/_materials/Material0/Principled_BSDF.outputs:surface>
            custom string userProperties:blender:data_name = "Material0"

            def Shader "Principled_BSDF"
            {
                uniform token info:id = "UsdPreviewSurface"
                float inputs:clearcoat = 0
                float inputs:clearcoatRoughness = 0.03
                color3f inputs:diffuseColor.connect = </root/_materials/Material0/Image_Texture.outputs:rgb>
                float inputs:ior = 1.5
                float inputs:metallic = 0
                float inputs:opacity = 1
                float inputs:roughness = 1
                float inputs:specular = 0
                token outputs:surface
            }

            def Shader "Image_Texture"
            {
                uniform token info:id = "UsdUVTexture"
                asset inputs:file = @./texture.png@
                token inputs:sourceColorSpace = "sRGB"
                float2 inputs:st.connect = </root/_materials/Material0/uvmap.outputs:result>
                token inputs:wrapS = "repeat"
                token inputs:wrapT = "repeat"
                float3 outputs:rgb
            }

            def Shader "uvmap"
            {
                uniform token info:id = "UsdPrimvarReader_float2"
                string inputs:varname = "st"
                float2 outputs:result
            }
        }
    }
}
)";
    }
}