/*
 * Copyright (c) Contributors to the Open 3D Engine Project.
 * For complete copyright and license terms please see the LICENSE at the root of this distribution.
 *
 * SPDX-License-Identifier: Apache-2.0 OR MIT
 *
 */
  
#pragma once

// NOTE: Nest this array, so Azslc will output a size of the bindingslot to 1
struct FloatBuffer
{
    float buffer;
};

// Listed on update frequency
ShaderResourceGroupSemantic FrequencyPerScene 
{
    FrequencyId = 0;
};

ShaderResourceGroupSemantic FloatBufferSemanticId
{
    FrequencyId = 1;
};

ShaderResourceGroupSemantic IndirectionBufferSemanticId
{
    FrequencyId = 2;
};

ShaderResourceGroupSemantic NonBindlessTextureSemanticId
{
    FrequencyId = 3;
};

ShaderResourceGroup FloatBufferSrg : FloatBufferSemanticId
{
    StructuredBuffer<FloatBuffer> m_floatBuffer;
};

ShaderResourceGroup IndirectionBufferSrg : IndirectionBufferSemanticId
{
    ByteAddressBuffer m_indirectionBuffer;
}; 

ShaderResourceGroup SamplerSrg : FrequencyPerScene
{
    Sampler m_sampler
    {
        MaxAnisotropy = 16;
        AddressU = Wrap;
        AddressV = Wrap;
        AddressW = Wrap;
    };
};

ShaderResourceGroup ImageSrg : NonBindlessTextureSemanticId
{
    // Array of textures
    Texture2D m_textureArray[UNBOUNDED_SIZE];
};

// Helper functions to read data from the FloatBuffer. The FloatBuffer is accessed with an offset and an index.
// The offset holds the initial offset within the FloatBuffer, and the index is a sub-index, which increments with each property that is being read.
// The data needs to be read in the same order as it is allocated on the host.

// read floats
void ReadFromFloatBuffer(out float outFloat, in uint offset, inout uint index)
{
    outFloat = FloatBufferSrg::m_floatBuffer[offset + index + 0].buffer;
    index += 1;
}

void ReadFromFloatBuffer(out float2 outFloat, in uint offset, inout uint index)
{
    outFloat.x = FloatBufferSrg::m_floatBuffer[offset + index + 0].buffer;
    outFloat.y = FloatBufferSrg::m_floatBuffer[offset + index + 1].buffer;
    index += 2;
}

void ReadFromFloatBuffer(out float3 outFloat, in uint offset, inout uint index)
{
    outFloat.x = FloatBufferSrg::m_floatBuffer[offset + index + 0].buffer;
    outFloat.y = FloatBufferSrg::m_floatBuffer[offset + index + 1].buffer;
    outFloat.z = FloatBufferSrg::m_floatBuffer[offset + index + 2].buffer;
    index += 3;
}

void ReadFromFloatBuffer(out float4 outFloat, in uint offset, inout uint index)
{
    outFloat.x = FloatBufferSrg::m_floatBuffer[offset + index + 0].buffer;
    outFloat.y = FloatBufferSrg::m_floatBuffer[offset + index + 1].buffer;
    outFloat.z = FloatBufferSrg::m_floatBuffer[offset + index + 2].buffer;
    outFloat.w = FloatBufferSrg::m_floatBuffer[offset + index + 3].buffer;
    index += 4;
}

// Read to matrix
void ReadFromFloatBuffer(out float4x4 outFloat, in uint offset, inout uint index)
{
    [unroll(4)] 
    for(uint i = 0; i < 4; i++)
    {
        ReadFromFloatBuffer(outFloat[i], offset, index);
    }
}

// read uint
void ReadFromFloatBuffer(out uint outUInt, in uint offset, inout uint index)
{
    outUInt = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 0].buffer);
    index += 1;
}

void ReadFromFloatBuffer(out uint2 outUInt, in uint offset, inout uint index)
{
    outUInt.x = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 0].buffer);
    outUInt.y = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 1].buffer);
    index += 2;
}

void ReadFromFloatBuffer(out uint3 outUInt, in uint offset, inout uint index)
{
    outUInt.x = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 0].buffer);
    outUInt.y = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 1].buffer);
    outUInt.z = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 2].buffer);
    index += 3;
}

void ReadFromFloatBuffer(out uint4 outUInt, in uint offset, inout uint index)
{
    outUInt.x = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 0].buffer);
    outUInt.y = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 1].buffer);
    outUInt.z = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 2].buffer);
    outUInt.w = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 3].buffer);
    index += 4;
}

// Read double
void ReadFromFloatBuffer(out double outDouble, in uint offset, inout uint index)
{
    uint lowBits;
    uint highBits;
    ReadFromFloatBuffer(highBits, offset, index);
    ReadFromFloatBuffer(lowBits, offset, index);

    outDouble = asdouble(lowBits, highBits);
}
