#include "DX12Shader.h"

#include "d3dx12.h"
#include "DX12CommandQueue.h"

using namespace Framework;

DX12Shader::DX12Shader(ID3D12Device* device,
    DX12CopyCommandQueue* copy,
    DX12DirectCommandQueue* direct)
    : Shader()
{
    shaderByteBuffer = 0;
    byteBufferSize = 0;
    this->device = device;
    this->copy = copy;
    this->direct = direct;
}

DX12Shader::~DX12Shader()
{
    delete[] shaderByteBuffer;
}

// Creates a constant buffer that passes constant data to the shader
// A maximum of 14 buffers can be created
//  zD3d11Device: The device used to create the buffer
//  size: The size of the buffer in bytes
//  index: The position of the buffer in the buffer array. Existing buffer
//  is replaced. Buffer 1 cannot be created if buffer 0 has not yet
//  been created, etc.
bool DX12Shader::createConstBuffer(int size, int index)
{
    if (index < 0 || index >= 14) return 0;
    while ((size / 256) * 256 != size)
        size++;
    while (!constBuffers->has(index))
        constBuffers->add(0);
    constBuffers->set(new DX12VertexBuffer(1, device, copy, direct), index);
    constBuffers->z(index)->setLength(size);
    constBuffers->z(index)->copyToGPU();
    return 1;
}

// Sets the compiled shader
//  zD3d11Device: The device used to create the shader
//  bytes: The bytes of the compiled code
//  length: the length of the byte array
//  return: true if bytes is valid, false otherwise
bool DX12Shader::setCompiledByteArray(unsigned char* bytes, int length)
{
    delete[] shaderByteBuffer;
    shaderByteBuffer = new unsigned char[length];
    memcpy(shaderByteBuffer, bytes, length);
    byteBufferSize = length;
    return 1;
}

// After calling this function, this shader is used as pixel shader
//  zD3d11Context: The context object used with the shader
void DX12Shader::useShader()
{
    // not needet in DirectX 12
}

// returns the compiled bytes
unsigned char* DX12Shader::getCompiledShader() const
{
    return shaderByteBuffer;
}

// returns the number of compiled bytes
int DX12Shader::getCompiledLength() const
{
    return byteBufferSize;
}

// Creates the root parameter for a constant buffer
//  index: The index of the buffer
//  view: contains the position and size of the buffer in memory after the call
void DX12Shader::getViewDesc(int index, D3D12_CONSTANT_BUFFER_VIEW_DESC& view)
{
    DX12Buffer* zB = (DX12Buffer*)constBuffers->z(index);
    if (!zB) return;
    view.SizeInBytes = zB->getElementCount() * zB->getElementLength();
    view.BufferLocation = zB->zBuffer()->GetGPUVirtualAddress();
}

DX12PixelShader::DX12PixelShader(ID3D12Device* device,
    DX12CopyCommandQueue* copy,
    DX12DirectCommandQueue* direct)
    : DX12Shader(device, copy, direct)
{}

// Constructor
DX12VertexShader::DX12VertexShader(ID3D12Device* device,
    DX12CopyCommandQueue* copy,
    DX12DirectCommandQueue* direct)
    : DX12Shader(device, copy, direct)
{
    inputLayout = 0;
    inputLayoutSize = 0;
}

// Destructor
DX12VertexShader::~DX12VertexShader()
{
    delete[] inputLayout;
}

// Creates an InputLayout for the shader
// Must only be called after compile
//  zD3d11Device: The device used to create the layout
//  descArray: An array with initialization data
//  anz: The number of elements in the array
bool DX12VertexShader::createInputLayout(
    D3D12_INPUT_ELEMENT_DESC* descArray, int anz)
{
    delete[] inputLayout;
    inputLayout = new D3D12_INPUT_ELEMENT_DESC[anz];
    memcpy(inputLayout, descArray, anz * sizeof(D3D12_INPUT_ELEMENT_DESC));
    inputLayoutSize = anz;
    return 1;
}

// Returns the number of input parameters of the shader
int DX12VertexShader::getInputLayoutSize() const
{
    return inputLayoutSize;
}

// Returns a list of formats for each input value
D3D12_INPUT_ELEMENT_DESC* DX12VertexShader::zInputLayout() const
{
    return inputLayout;
}