#include <Fenster.h>
#include <Globals.h>
#include <MausEreignis.h>
#include <TastaturEreignis.h>
#include <Bildschirm.h>
#include <RenderThread.h>
#include <d3d11.h>
#include <Bild.h>
#include <iostream>
#include <D3DX11async.h>

using namespace Framework;

Bildschirm *bs;

void FClose( void *p, void *zF )
{
    StopNachrichtenSchleife( ( (WFenster*)zF )->getFensterHandle() );
}

struct XMFLOAT3
{
    float x;
    float y;
    float z;
};

struct Vertex
{
    XMFLOAT3 pos;
};

class Kam3D : public Zeichnung
{
private:
    ID3D11Texture2D *d3d11RenderTarget;
    ID3D11Texture2D *d3d11Staging;
    ID3D11RenderTargetView *d3d11RenderTargetView;
    ID3D11DepthStencilView *d3d11DepthStencilView;
    ID3D11ShaderResourceView *d3d11ShaderResourceView;
    ID3D11DeviceContext *context;
    ID3D11Device *device;


    ID3D11Buffer *pVBuffer;    // global
    ID3D11VertexShader *pVS;    // the vertex shader
    ID3D11PixelShader *pPS;     // the pixel shader
    ID3D11InputLayout *pLayout;    // global

public:
    Kam3D()
        : Zeichnung()
    {
        d3d11RenderTarget = 0;
    }
    ~Kam3D()
    {
        d3d11RenderTarget->Release();
        d3d11Staging->Release();
        d3d11RenderTargetView->Release();
        d3d11ShaderResourceView->Release();
        pVBuffer->Release();
        pVS->Release();
        pPS->Release();
        pLayout->Release();
        //d3d11DepthStencilView->Release();
    }

    bool init( ID3D11Device *device, ID3D11DeviceContext *context )
    {
        this->context = context;
        this->device = device;

        D3D11_TEXTURE2D_DESC textureDesc;
        HRESULT result;
        D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
        D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
        D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;


        // Initialize the render target texture description.
        ZeroMemory( &textureDesc, sizeof( textureDesc ) );

        // Setup the render target texture description.
        textureDesc.Width = getBreite();
        textureDesc.Height = getHöhe();
        textureDesc.MipLevels = 1;
        textureDesc.ArraySize = 1;
        textureDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
        textureDesc.SampleDesc.Count = 1;
        textureDesc.Usage = D3D11_USAGE_DEFAULT;
        textureDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
        textureDesc.CPUAccessFlags = 0;
        textureDesc.MiscFlags = 0;

        // Create the render target texture.
        result = device->CreateTexture2D( &textureDesc, NULL, &d3d11RenderTarget );
        if( FAILED( result ) )
        {
            return false;
        }


        // Initialize the render target texture description.
        ZeroMemory( &textureDesc, sizeof( textureDesc ) );

        // Setup the render target texture description.
        textureDesc.Width = getBreite();
        textureDesc.Height = getHöhe();
        textureDesc.MipLevels = 1;
        textureDesc.ArraySize = 1;
        textureDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
        textureDesc.SampleDesc.Count = 1;
        textureDesc.Usage = D3D11_USAGE_STAGING;
        textureDesc.BindFlags = 0;
        textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
        textureDesc.MiscFlags = 0;

        // Create the render target texture.
        result = device->CreateTexture2D( &textureDesc, NULL, &d3d11Staging );
        if( FAILED( result ) )
        {
            return false;
        }

        // Setup the description of the render target view.
        renderTargetViewDesc.Format = textureDesc.Format;
        renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
        renderTargetViewDesc.Texture2D.MipSlice = 0;

        // Create the render target view.
        result = device->CreateRenderTargetView( d3d11RenderTarget, &renderTargetViewDesc, &d3d11RenderTargetView );
        if( FAILED( result ) )
        {
            return false;
        }

        // Setup the description of the shader resource view.
        shaderResourceViewDesc.Format = textureDesc.Format;
        shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
        shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
        shaderResourceViewDesc.Texture2D.MipLevels = 1;

        // Create the shader resource view.
        result = device->CreateShaderResourceView( d3d11RenderTarget, &shaderResourceViewDesc, &d3d11ShaderResourceView );
        if( FAILED( result ) )
        {
            return false;
        }

       /* ZeroMemory( &depthStencilViewDesc, sizeof( D3D11_DEPTH_STENCIL_VIEW_DESC ) );
        depthStencilViewDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
        depthStencilViewDesc.Flags = D3D11_DSV_FLAG::D3D11_DSV_READ_ONLY_DEPTH | D3D11_DSV_FLAG::D3D11_DSV_READ_ONLY_STENCIL;

        result = device->CreateDepthStencilView( d3d11RenderTarget, &depthStencilViewDesc, &d3d11DepthStencilView );
        if( FAILED( result ) )
        {
            return false;
        }*/

        InitGraphics();
        InitPipeline();
        return true;
    }

    void InitGraphics()
    {
        // create a triangle using the VERTEX struct
        Vertex vertexData[ 3 ];
        vertexData[ 0 ].pos = { 0.0f, 0.5f, 0.5f };
        vertexData[ 1 ].pos = { 0.5f, -0.5f, 0.5f };
        vertexData[ 2 ].pos = { -0.5f, -0.5f, 0.5f };


        // create the vertex buffer
        D3D11_BUFFER_DESC bd;
        ZeroMemory( &bd, sizeof( bd ) );

        bd.Usage = D3D11_USAGE_DYNAMIC;                // write access access by CPU and GPU
        bd.ByteWidth = sizeof( Vertex ) * 3;             // size is the VERTEX struct * 3
        bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;       // use as a vertex buffer
        bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;    // allow CPU to write in buffer

        device->CreateBuffer( &bd, NULL, &pVBuffer );       // create the buffer


                                                         // copy the vertices into the buffer
        D3D11_MAPPED_SUBRESOURCE ms;
        context->Map( pVBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms );    // map the buffer
        memcpy( ms.pData, vertexData, sizeof( vertexData ) );                 // copy the data
        context->Unmap( pVBuffer, NULL );                                      // unmap the buffer
    }

    void InitPipeline()
    {
        // load and compile the two shaders
        ID3D10Blob *VS, *PS;
        D3DX11CompileFromFile( "shader.hlsl", 0, 0, "VS", "vs_4_0", 0, 0, 0, &VS, 0, 0 );
        D3DX11CompileFromFile( "shader.hlsl", 0, 0, "PS", "ps_4_0", 0, 0, 0, &PS, 0, 0 );

        // encapsulate both shaders into shader objects
        device->CreateVertexShader( VS->GetBufferPointer(), VS->GetBufferSize(), NULL, &pVS );
        device->CreatePixelShader( PS->GetBufferPointer(), PS->GetBufferSize(), NULL, &pPS );

        // set the shader objects
        context->VSSetShader( pVS, 0, 0 );
        context->PSSetShader( pPS, 0, 0 );

        // create the input layout object
        D3D11_INPUT_ELEMENT_DESC ied[] =
        {
            { "POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0, D3D11_INPUT_PER_VERTEX_DATA,0 }
        };

        device->CreateInputLayout( ied, 1, VS->GetBufferPointer(), VS->GetBufferSize(), &pLayout );

        UINT stride = sizeof( Vertex );
        UINT offset = 0;
        context->IASetVertexBuffers( 0, 1, &pVBuffer, &stride, &offset );
        context->IASetInputLayout( pLayout );
        context->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

        context->OMSetRenderTargets( 1, &d3d11RenderTargetView, 0 );
        context->PSSetShaderResources( 1, 1, &d3d11ShaderResourceView );
    }

    void render( Bild &rObj )
    {
        float color[ 4 ];

        // Setup the color to clear the buffer to.
        color[ 0 ] = 0;
        color[ 1 ] = 0;
        color[ 2 ] = 0;
        color[ 3 ] = 0;

        // Clear the back buffer.
        context->ClearRenderTargetView( d3d11RenderTargetView, color );
        //context->ClearDepthStencilView( d3d11DepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0 );
        context->Draw( 3, 0 );    // draw 3 vertices, starting from vertex 0
        

        //--------------------------------------------------------------------------------
        // Draw to Framework
        context->CopyResource( d3d11Staging, d3d11RenderTarget );

        D3D11_MAPPED_SUBRESOURCE buffer;
        context->Map( d3d11Staging, 0, D3D11_MAP::D3D11_MAP_READ, 0, &buffer );

        int *pixel = rObj.getBuffer();
        for( int y = 0, pitch = 0, bry = pos.x + pos.y * rObj.getBreite(); y < gr.y; ++y, pitch += buffer.RowPitch, bry += rObj.getBreite() )
            memcpy( &pixel[ bry ], &( (BYTE *)buffer.pData )[ pitch ], buffer.RowPitch );

        context->Unmap( d3d11Staging, 0 );

        std::cout << bs->getRenderZeit() << "\n";
    }
};

int main()
{
    initFramework();


    WFenster *f = new WFenster();
    WNDCLASS fc = F_Normal( 0 );
    fc.lpszClassName = "Test";
    f->erstellen( WS_OVERLAPPEDWINDOW, fc );
    f->setGröße( 1600, 900 );
    f->setVSchließAktion( FClose );
    f->setMausAktion( _ret1ME );
    f->setTastaturAktion( _ret1TE );
    f->setAnzeigeModus( 1 );

    bs = new Bildschirm( f->getThis() );
    f->setBildschirm( bs->getThis() );
    bs->setTestRend( 0 );
    bs->setFüll( 0 );

    RenderTh *r = new RenderTh();
    r->setBildschirm( bs->getThis() );
    r->setMaxFps( 60 );
    r->beginn();

    Kam3D *obj = new Kam3D();
    obj->setPosition( 100, 100 );
    obj->setGröße( 1000, 600 );
    bs->addMember( obj );

    ID3D11Device *device;
    ID3D11DeviceContext *context;

    D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0 };
    D3D_FEATURE_LEVEL levelsOk[ 3 ];
    HRESULT hr = D3D11CreateDevice( 0, D3D_DRIVER_TYPE_HARDWARE, 0, D3D11_CREATE_DEVICE_DEBUG, levels, 3, D3D11_SDK_VERSION, &device, levelsOk, &context );
    if( hr == S_OK )
    {
        obj->init( device, context );
    }

    StartNachrichtenSchleife();

    r->beenden();
    r->release();
    f->setBildschirm( 0 );
    bs->release();
    f->release();
    delete obj;
    context->Release();
    device->Release();

    releaseFramework();
    return 0;
}