#include "platform-dx/device-dx.hpp"
#include "graphics/buffer.hpp"
#include "platform-dx/implementations.hpp"
#include "graphics/shader.hpp"

namespace xna {
    static HRESULT shaderCompileFromFile(_In_ LPCWSTR srcFile, _In_ LPCSTR entryPoint, _In_ LPCSTR profile, _Outptr_ ID3DBlob** blob)
	{
		//https://learn.microsoft.com/en-us/windows/win32/direct3d11/how-to--compile-a-shader

        if (!srcFile || !entryPoint || !profile || !blob)
            return E_INVALIDARG;

        *blob = nullptr;

        UINT flags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
        flags |= D3DCOMPILE_DEBUG;
#endif

        const D3D_SHADER_MACRO defines[] =
        {
            "EXAMPLE_DEFINE", "1",
            NULL, NULL
        };

        ID3DBlob* shaderBlob = nullptr;
        ID3DBlob* errorBlob = nullptr;
        HRESULT hr = D3DCompileFromFile(srcFile, defines, D3D_COMPILE_STANDARD_FILE_INCLUDE,
            entryPoint, profile,
            flags, 0, &shaderBlob, &errorBlob);
        if (FAILED(hr))
        {
            if (errorBlob)
            {
                OutputDebugStringA((char*)errorBlob->GetBufferPointer());
                errorBlob->Release();
            }

            if (shaderBlob)
                shaderBlob->Release();

            return hr;
        }

        *blob = shaderBlob;

        return hr;
	}       

    bool Shader::CompileFromFile(WString srcFile, String entryPoint, String profile, DataBuffer& blob) {
        const auto hr = shaderCompileFromFile(srcFile.c_str(), entryPoint.c_str(), profile.c_str(), &blob.impl->_blob);

        return SUCCEEDED(hr);
    }

    VertexShader::~VertexShader() {
        impl = nullptr;
    }

    PixelShader::~PixelShader() {
        impl = nullptr;
    }

    bool VertexShader::Initialize(DataBuffer& buffer, xna_error_ptr_arg)
    {
        if (!impl || !m_device || !m_device->_device || !buffer.impl->_blob) {
            xna_error_apply(err, XnaErrorCode::INVALID_OPERATION);
            return false;
        }

        if (impl->_vertexShader) {
            impl->_vertexShader->Release();
            impl->_vertexShader = nullptr;
        }

        const auto hr = m_device->_device->CreateVertexShader(
            buffer.impl->_blob->GetBufferPointer(),
            buffer.impl->_blob->GetBufferSize(),
            NULL,
            &impl->_vertexShader);

        if (FAILED(hr)) {
            xna_error_apply(err, XnaErrorCode::FAILED_OPERATION);
            return false;
        }

        return true;
    }

    bool PixelShader::Initialize(DataBuffer& buffer, xna_error_ptr_arg)
    {
        if (!impl || !m_device || !m_device->_device || !buffer.impl->_blob) {
            xna_error_apply(err, XnaErrorCode::INVALID_OPERATION);
            return false;
        }

        if (impl->_pixelShader) {
            impl->_pixelShader->Release();
            impl->_pixelShader = nullptr;
        }

        const auto hr = m_device->_device->CreatePixelShader(
            buffer.impl->_blob->GetBufferPointer(),
            buffer.impl->_blob->GetBufferSize(),
            NULL,
            &impl->_pixelShader);

        if (FAILED(hr)) {
            xna_error_apply(err, XnaErrorCode::FAILED_OPERATION);
            return false;
        }

        return true;
    }
}