#include "DX12CommandQueue.h"
#include "Text.h"
#include "Fenster.h"

#include <iostream>
#include <d3d12.h>

using namespace Framework;


DX12CommandQueue::DX12CommandQueue( int typ, ID3D12Device* device )
    : ReferenceCounter(),
    device( device ),
    event( CreateEvent( 0, 0, 0, 0 ) ),
    fenceValue( 0 )
{
    D3D12_COMMAND_QUEUE_DESC desc = {};
    desc.Type = (D3D12_COMMAND_LIST_TYPE)typ;
    desc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
    desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
    desc.NodeMask = 0;
    HRESULT res = device->CreateCommandQueue( &desc, __uuidof(ID3D12CommandQueue), (void**)&queue );
    if( FAILED( res ) )
    {
        std::cout << "ERROR: CreateCommandQueue returned " << res << "\n";
        WMessageBox( 0, new Text( "Fehler" ), new Text( "CreateCommandQueue ist Fehlgeschlagen." ), MB_ICONERROR );
    }

    res = device->CreateCommandAllocator( (D3D12_COMMAND_LIST_TYPE)typ, __uuidof(ID3D12CommandAllocator), (void**)&allocator );
    if( FAILED( res ) )
    {
        std::cout << "ERROR: CreateCommandAllocator returned " << res << "\n";
        WMessageBox( 0, new Text( "Fehler" ), new Text( "CreateCommandAllocator ist Fehlgeschlagen." ), MB_ICONERROR );
    }

    res = device->CreateCommandList( 0, (D3D12_COMMAND_LIST_TYPE)typ, allocator, nullptr, __uuidof(ID3D12GraphicsCommandList), (void**)&commandList );
    if( FAILED( res ) )
    {
        std::cout << "ERROR: CreateCommandList returned " << res << "\n";
        WMessageBox( 0, new Text( "Fehler" ), new Text( "CreateCommandList ist Fehlgeschlagen." ), MB_ICONERROR );
    }

    res = device->CreateFence( 0, D3D12_FENCE_FLAG_NONE, __uuidof(ID3D12Fence), (void**)&fence );
    if( FAILED( res ) )
    {
        std::cout << "ERROR: CreateFence returned " << res << "\n";
        WMessageBox( 0, new Text( "Fehler" ), new Text( "CreateFence ist Fehlgeschlagen." ), MB_ICONERROR );
    }
}

DX12CommandQueue::~DX12CommandQueue()
{
    flush();
    if( commandList )
        commandList->Release();
    if( allocator )
        allocator->Release();
    if( queue )
        queue->Release();
}

unsigned __int64 DX12CommandQueue::addSignalFromGPU()
{
    queue->Signal( fence, ++fenceValue );
    return fenceValue;
}

void DX12CommandQueue::whaitForGPUSignal( unsigned __int64 value )
{
    if( fence->GetCompletedValue() < value )
    {
        fence->SetEventOnCompletion( value, event );
        WaitForSingleObject( event, INFINITE );
    }
}

void DX12CommandQueue::whaitForGPUSignal()
{
    if( fence->GetCompletedValue() < fenceValue )
    {
        fence->SetEventOnCompletion( fenceValue, event );
        WaitForSingleObject( event, INFINITE );
    }
}

void DX12CommandQueue::flush()
{
    whaitForGPUSignal( addSignalFromGPU() );
}

ID3D12CommandAllocator* DX12CommandQueue::getAllocator() const
{
    return allocator;
}

ID3D12GraphicsCommandList* DX12CommandQueue::getCommandList() const
{
    return commandList;
}

ID3D12CommandQueue* DX12CommandQueue::getQueue() const
{
    return queue;
}

void DX12CommandQueue::execute()
{
    commandList->Close();
    queue->ExecuteCommandLists( 1, (ID3D12CommandList**)&commandList );
    flush();
    allocator->Reset();
    commandList->Reset( allocator, nullptr );
}


DX12DirectCommandQueue::DX12DirectCommandQueue( ID3D12Device* device )
    : DX12CommandQueue( D3D12_COMMAND_LIST_TYPE_DIRECT, device )
{}


DX12CopyCommandQueue::DX12CopyCommandQueue( ID3D12Device* device )
    : DX12CommandQueue( D3D12_COMMAND_LIST_TYPE_COPY, device )
{}


DX12ComputeCommandQueue::DX12ComputeCommandQueue( ID3D12Device* device )
    : DX12CommandQueue( D3D12_COMMAND_LIST_TYPE_COMPUTE, device )
{}