#include "stdafx.h"
#include "Global.h"
#include "DxWrapper.h"
#include "aabb.h"
#include "PlayerCraft.h"
#include "MyLogStream.h"

using namespace std;

// statische Variablen initialisieren:
DxWrapper* DxWrapper::mInstance = NULL;

DxWrapper::DxWrapper()
{
	timeStampAtStart = GetTickCount();
 
    d3d = NULL;
    d3ddev = NULL;
    d3dspt = NULL;
    z_buffer = NULL;    // the pointer to the z-buffer    
    ZeroMemory(&d3dpp, sizeof(d3dpp));

    // DirectInput
    din = NULL;
    dinkeyboard = NULL;
    dinmouse = NULL; 

    m_DrawBoundingBoxes = false;
    m_logFontSize = 15;   

    // Assimp

    // reset the camera view to the default position
    m_Camera.vPos = aiVector3D(0.0f,0.0f,-10.0f);
    m_Camera.vLookAt = aiVector3D(0.0f,0.0f,0.0f);
    m_Camera.vUp = aiVector3D(0.0f,1.0f,0.0f);
    m_Camera.vRight = aiVector3D(1.0f,0.0f,0.0f);

    // read out the current directory to set the applicationBaseDirectory
    WCHAR buffer[MAX_PATH];
    DWORD len = GetCurrentDirectory(sizeof(buffer), buffer);
 
    assert(len);
    assert(len < MAX_PATH - 50);

    applicationBaseDirectory = new WCHAR[len+2];
    wcscpy_s(applicationBaseDirectory, len+2, buffer);

	std::string debugstr = std::string("DxWrapper: application base directory=");
	debugstr.append(std::string(CW2A(applicationBaseDirectory)));
	LogWrite(debugstr);


	// Create a logger instance 
	Assimp::DefaultLogger::create("",Assimp::Logger::VERBOSE);

	// Attaching it to the default logger instance:
	unsigned int severity = 0;
	severity |= Assimp::Logger::DEBUGGING;
	severity |= Assimp::Logger::INFO;
	severity |= Assimp::Logger::WARN;
	severity |= Assimp::Logger::ERR;	

	myLogStream = new MyLogStream();

	// Attaching it to the default logger
	Assimp::DefaultLogger::get()->attachStream( myLogStream, severity );
}

DxWrapper::~DxWrapper()
{
}

/** Singleton: gibt die einzige Instanz des DxWrappers zurück
*/
DxWrapper* DxWrapper::Instance(void)
{
    if (!DxWrapper::mInstance)
        DxWrapper::mInstance = new DxWrapper();

    return DxWrapper::mInstance;
}

// Create Direct3D and the Direct3D Device
void DxWrapper::InitDirect3D(GAMEWINDOW* gw)
{
    m_GameWindow = *gw;
    DxWrapper::d3d = Direct3DCreate9(D3D_SDK_VERSION);

	LogWrite("InitDirect3D()");

    if(DxWrapper::d3d == NULL)
    {
        MessageBox(NULL, L"The Direct3DCreate9 function failed", L"Error", MB_OK);
        PostQuitMessage(0);
    }
    
	this->assimpRenderer = AssimpRendererD3d::AssimpRenderer::Instance();
	this->assimpRenderer->AttachLogStream(myLogStream, 0);


	// fill the presentation parameter structure
	d3dpp.EnableAutoDepthStencil	= TRUE;
	if (m_GameWindow.Windowed)
		d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;			// pnx: war eigentlich D3DPRESENT_INTERVAL_ONE
	else
		d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;			// pnx: war eigentlich D3DPRESENT_INTERVAL_ONE

	d3dpp.SwapEffect				= D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat          = D3DFMT_X8R8G8B8;                      // war eigentlich nicht drin
	d3dpp.BackBufferCount			= 1;
	d3dpp.Windowed = m_GameWindow.Windowed;
	d3dpp.BackBufferWidth = m_GameWindow.Width;
	d3dpp.BackBufferHeight = m_GameWindow.Height;
	d3dpp.hDeviceWindow = m_GameWindow.hWnd;

	AssimpRendererD3d::RenderOptions renderOptions;
    d3ddev = assimpRenderer->CreateD3dDevice(d3d, &d3dpp, renderOptions);

    //InitDirect3DDevice();

    InitDirect3DSprite();

    InitFont(m_logFontSize);    

    InitLight();

    d3ddev->SetRenderState(D3DRS_LIGHTING, TRUE);    // turn on the 3D lighting
    d3ddev->SetRenderState(D3DRS_ZENABLE, TRUE);    // turn on the z-buffer
    d3ddev->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(100, 100, 100));    // ambient light
}


void DxWrapper::InitDirect3DSprite()
{
    assert(d3ddev);

	LogWrite("InitDirect3DSprite()");

    D3DXCreateSprite(d3ddev, &d3dspt);		// create the Sprite Interface
    assert(d3dspt);

    // neu für 3D: create the z-buffer
    d3ddev->CreateDepthStencilSurface(m_GameWindow.Width,
        m_GameWindow.Height,
        D3DFMT_D16,
        D3DMULTISAMPLE_NONE,
        0,
        TRUE,
        &z_buffer,
        NULL);


    //d3ddev->SetRenderState(D3DRS_LIGHTING, TRUE);    // turn on the 3D lighting
    //d3ddev->SetRenderState(D3DRS_ZENABLE, TRUE);    // turn on the z-buffer
    //d3ddev->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(100, 100, 100));    // ambient light
}



// Close the Device and Direct3D
void DxWrapper::CloseDirect3D()
{
    d3ddev->Release();
    d3d->Release();
}


bool DxWrapper::LoadModel(Model* pModel, LPCTSTR file, LPCTSTR path)
{
    DxWrapper* dxw = DxWrapper::Instance();
    LPD3DXBUFFER buffer;

	DxWrapper::Instance()->LogWrite("LoadModel()");

    SetCurrentDirectory(dxw->applicationBaseDirectory);
	SetCurrentDirectory(FILEPATH_RESOURCES);
    SetCurrentDirectory(path);

    if (FAILED(D3DXLoadMeshFromX(file,    // load this file
        D3DXMESH_SYSTEMMEM,    // load the mesh into system memory
        dxw->d3ddev,    // the Direct3D Device
        NULL,    // we aren't using adjacency
        &buffer,    // put the materials here
        NULL,    // we aren't using effect instances
        &pModel->numMaterials,    // the number of materials in this model
        &pModel->Mesh))  // put the mesh here
        )    
    {
        MessageBox(NULL, L"MODEL NOT FOUND!", L"Error", MB_OK);
        PostQuitMessage(0);
        return false;
    }

    // retrieve the pointer to the buffer containing the material information
    D3DXMATERIAL* tempMaterials = (D3DXMATERIAL*)buffer->GetBufferPointer();

    // create a new material buffer for each material in the mesh
    pModel->Materials = new D3DMATERIAL9[pModel->numMaterials];
    pModel->Textures = new LPDIRECT3DTEXTURE9[pModel->numMaterials];
    for(DWORD i = 0; i < pModel->numMaterials; i++)    // for each material...
    {
        pModel->Materials[i] = tempMaterials[i].MatD3D;    // get the material info
        pModel->Materials[i].Ambient = pModel->Materials[i].Diffuse;    // make ambient the same as diffuse

        // Texturen laden:
        USES_CONVERSION;    // allows certain string conversions. Was passiert da genau ? Wenn man das auskommentiert, geht's trotzdem..!    

        // if there is a texture to load, load it
        if(FAILED(D3DXCreateTextureFromFile(dxw->d3ddev, 
            CA2W(tempMaterials[i].pTextureFilename),    // der filename ist im LPSTR format gespeichert. Die Methode D3DXCreateTextureFromFile() braucht aber den string im LPCWSTR format. Mit dem CA2W Macro kann man konvertieren.
            &pModel->Textures[i])))
        {
            pModel->Textures[i] = NULL;    // if there is no texture, set the texture to NULL
        }
    }

    // calculate bounding box
    BYTE* pVertices=NULL;
    HRESULT hr=pModel->Mesh->LockVertexBuffer(D3DLOCK_READONLY, (LPVOID*)&pVertices);
    if (FAILED(hr))
        return FALSE;

    // die Vertices werden als Vector aus drei floats hintereinander abgelegt, deshalb kann man gleich auf D3DCVECTOR3 casten:
    D3DXComputeBoundingBox((D3DXVECTOR3*)pVertices, pModel->Mesh->GetNumVertices(), D3DXGetFVFVertexSize(pModel->Mesh->GetFVF()), &pModel->minBound, &pModel->maxBound);

    cout << "  minBound=" << pModel->minBound.x << "," << pModel->minBound.y << "," << pModel->minBound.z << "    maxBound=" << pModel->maxBound.x << "," << pModel->maxBound.y << "," << pModel->maxBound.z << std::endl;
    pModel->Mesh->UnlockVertexBuffer();

    return true;
}




void DxWrapper::ClearScreen()
{
	d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(100,100,100), 1.0f, 0);
    d3ddev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(100,100,100), 1.0f, 0);
}
// Start rendering
bool DxWrapper::BeginScene()
{
    int rc = d3ddev->TestCooperativeLevel();

    switch (rc)
    {

    case D3DERR_DEVICELOST:
        LogWrite("D3DERR_DEVICELOST");
        return false;
        break;

    case D3DERR_DEVICENOTRESET:
        {
            LogWrite("D3DERR_DEVICENOTRESET");
            ResetDevice();            
            //InitDirect3DDevice();
            return false;
        }
        break;

    case D3DERR_DRIVERINTERNALERROR:
        LogWrite("D3DERR_DRIVERINTERNALERROR !");
        return false;
        break;
    }
    d3ddev->BeginScene();

    return true;
}

// Stop rendering
void DxWrapper::EndScene()
{
    d3ddev->EndScene();
}

void DxWrapper::StartRender2D()
{    
    d3dspt->Begin(D3DXSPRITE_ALPHABLEND);   
}

// Stop rendering
void DxWrapper::EndRender2D()
{
    DxWrapper::d3dspt->End();
}

void DxWrapper::DisplayFrame()
{
    // display the debug log
    if(m_showLog)
    {
        for(unsigned int i=0; i<vecLog.size(); ++i)
        {
            RenderText(vecLog[i], 0, m_logFontSize * i);
        }
    }
    d3ddev->Present(NULL, NULL, NULL, NULL);
}

// Load a graphic into a SPRITE object
void DxWrapper::LoadSprite(SPRITE* pSprite, LPCTSTR File, LPCTSTR Path, int width, int height, int cols, int rows)
{
	//DxWrapper::Instance()->LogWrite("LoadSprite()");

    SetCurrentDirectory(applicationBaseDirectory);
    SetCurrentDirectory(Path);

    assert(d3ddev != NULL);

    if (SUCCEEDED (D3DXCreateTextureFromFileEx(d3ddev,
            File,
            D3DX_DEFAULT,
            D3DX_DEFAULT,
            D3DX_DEFAULT,
            NULL,
            D3DFMT_A8R8G8B8,
            D3DPOOL_MANAGED,
            D3DX_DEFAULT,
            D3DX_DEFAULT,
            D3DCOLOR_XRGB(255, 0, 255),
            NULL,
            NULL,
            &pSprite->tex)))
    {
        pSprite->width = width;
        pSprite->height = height;
        pSprite->cols = cols;
        pSprite->rows = rows;
    }
    else
    {
        pSprite->tex = NULL;
        pSprite->width = 0;
        pSprite->height = 0;
        pSprite->cols = 0;
        pSprite->rows = 0;

		std::string debugStr("LoadSprite(): SPRITE NOT FOUND!");
		debugStr.append("\ncurrentDirectory=");
		debugStr += GetCurrentDir();
		debugStr.append(" file=");
		debugStr.append(CW2A(File));
		LogWrite(debugStr);
    }
}

// Draw a frame from a SPRITE object
void DxWrapper::RenderSprite(const SPRITE* pSprite, int Frame, float x, float y, float z)
{
    if (pSprite && pSprite->tex && pSprite->cols)
    {
        RECT FrameBox;
        FrameBox.left = (Frame % pSprite->cols) * pSprite->width;
        FrameBox.top = (Frame / pSprite->cols) * pSprite->height;
        FrameBox.right = FrameBox.left + pSprite->width;
        FrameBox.bottom = FrameBox.top + pSprite->height;

        D3DXVECTOR3 center(0.0f, 0.0f, 0.0f);    // center at the upper-left corner. Der 3. Parameter ist laut Direcxtutorial.com der Tiefenoffset, aber ist in der DirectX-Doku nicht erwähnt.
        D3DXVECTOR3 position(x, y, z);
        
        d3ddev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
        d3ddev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);

        d3dspt->Draw(pSprite->tex, &FrameBox, &center, &position, D3DCOLOR_XRGB(255, 255, 255));
    }
}


void DxWrapper::RenderObject(GameObject3D *obj)
{	
	//DxWrapper::Instance()->LogWrite("RenderObject()");

    D3DXMATRIX matTrans, matRotateX, matRotateY, matRotateZ, matUnity, matScaling;
    D3DXMatrixIdentity(&matUnity);
    D3DXMatrixTranslation(&matTrans, obj->m_Position.x, obj->m_Position.y, obj->m_Position.z);
    D3DXMatrixRotationX(&matRotateX, D3DXToRadian(obj->m_RotX + obj->m_Model.calibrationAngleX));
    D3DXMatrixRotationY(&matRotateY, D3DXToRadian(obj->m_RotY + obj->m_Model.calibrationAngleY));
    D3DXMatrixRotationZ(&matRotateZ, D3DXToRadian(-obj->m_RotZ + obj->m_Model.calibrationAngleZ)); 
    D3DXMatrixScaling(&matScaling, obj->m_ScaleFactorX, obj->m_ScaleFactorY, obj->m_ScaleFactorZ);
    d3ddev->SetTransform(D3DTS_WORLD, &( matScaling * matRotateX * matRotateY * matRotateZ * matTrans));    
	
	HRESULT resultCode = 0;
    for(DWORD i = 0; i < obj->m_Model.numMaterials; i++)    // loop through each subset
    {        
		d3ddev->SetMaterial(&obj->m_Model.Materials[i]);    // set the material for the subset
        
        if(obj->m_Model.Textures[i] != NULL)    // if the subset has a texture
            d3ddev->SetTexture(0, obj->m_Model.Textures[i]);    // ...then set the texture
        else
            d3ddev->SetTexture(0, NULL);
			        
        d3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);    // turn off the color blending

        resultCode = obj->m_Model.Mesh->DrawSubset(i);    // draw the subset
        assert (resultCode == D3D_OK);				
    }
	
	
    // Bounding Boxes malen
    d3ddev->SetTransform(D3DTS_WORLD, &(matUnity));
    if (m_DrawBoundingBoxes)
    {
        RenderBoundingBox(obj->GetBoundingBoxOfFlightPath());                
    }
	
}



void DxWrapper::RenderBoundingBox(const IvOBB& obb)
{
    IvMatrix33 rotMat = obb.GetRotation();
    IvVector3 ivCenter = obb.GetCenter(); 
    
    IvVector3 ivExt = obb.GetExtents();
    
    IvVector3 point1(ivExt.x, ivExt.y, ivExt.z);
    IvVector3 point2(- ivExt.x, ivExt.y, ivExt.z);
    IvVector3 point3(ivExt.x, - ivExt.y, ivExt.z);
    IvVector3 point4(- ivExt.x, - ivExt.y, ivExt.z);
    IvVector3 point5(ivExt.x, ivExt.y, - ivExt.z);
    IvVector3 point6(- ivExt.x, ivExt.y, - ivExt.z);
    IvVector3 point7(ivExt.x, - ivExt.y, - ivExt.z);
    IvVector3 point8(- ivExt.x, - ivExt.y, - ivExt.z);
    
    point1 = point1 * rotMat + ivCenter;
    point2 = point2 * rotMat + ivCenter;
    point3 = point3 * rotMat + ivCenter;
    point4 = point4 * rotMat + ivCenter;
    point5 = point5 * rotMat + ivCenter;
    point6 = point6 * rotMat + ivCenter;
    point7 = point7 * rotMat + ivCenter;
    point8 = point8 * rotMat + ivCenter;

    //vorne
    RenderLine(point1, point2, 255);
    RenderLine(point2, point4, 255);
    RenderLine(point4, point3, 255);
    RenderLine(point3, point1, 255);

    //hinten
    RenderLine(point5, point7);
    RenderLine(point7, point8);
    RenderLine(point8, point6);
    RenderLine(point6, point5);

    // vorne nach hinten
    RenderLine(point1, point5);
    RenderLine(point2, point6);
    RenderLine(point4, point8);
    RenderLine(point3, point7);
}


void DxWrapper::RenderLine(const CPos& pointA, const CPos& pointB)
{
    SVertex vertexList[2];
    vertexList[0].x = pointA.x;
    vertexList[0].y = pointA.y;
    vertexList[0].z = pointA.z;
    vertexList[0].color = D3DCOLOR_XRGB(0, 0, 0);
    vertexList[1].x = pointB.x;
    vertexList[1].y = pointB.y;
    vertexList[1].z = pointB.z;
    vertexList[1].color = D3DCOLOR_XRGB(0, 0, 0);
    RenderVertexListAsLines((SVertex*)&vertexList, 2);
}

void DxWrapper::RenderLine(const IvVector3 pointA, const IvVector3 pointB, int r, int g, int b)
{
    SVertex vertexList[2];
    vertexList[0].x = pointA.x;
    vertexList[0].y = pointA.y;
    vertexList[0].z = pointA.z;
    vertexList[0].color = D3DCOLOR_XRGB(r, g, b);
    vertexList[1].x = pointB.x;
    vertexList[1].y = pointB.y;
    vertexList[1].z = pointB.z;
    vertexList[1].color = D3DCOLOR_XRGB(r, g, b);

    
    DWORD oldLight;   // alten Lightning-Zustand merken, damit wir Lighting vorübergehend ausschalten können, damit die Farben stimmen
    d3ddev->GetRenderState(D3DRS_LIGHTING, &oldLight);
    d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE);    // turn off the 3D lighting
    RenderVertexListAsLines((SVertex*)&vertexList, 2);
    d3ddev->SetRenderState(D3DRS_LIGHTING, oldLight);    // reset the 3D lighting
}


// This renders a list of SVertex type vertices that is "numVerts" long, as D3DPT_LINELIST
bool DxWrapper::RenderVertexListAsLines(SVertex *vertList, int numVerts)
{
    // nur gerade Zahlen
    assert(numVerts % 2 == 0);

    // The first thing we have to do before rendering is tell D3D what
    // type of vertices we'll be rendering.  We do this by passing the
    // #define that stipulates our vertex type.
    d3ddev->SetFVF(SVertexType);

    // Now we are ready to render.  We can do this with one function call.
    // You'll notice the "UP" in the function name.  This stands for "user pointer" and 
    // means that we are passing D3D a pointer to a list of vertices to be rendered.  
    // Optionally we could use a D3D vertex buffer, this is a more specialized class that
    // increases rendering speeds.

    // This function takes vertex arrays and index arrays (declared in typical C++ fashion)
    // and renders them to the screen.  By parameter:
    // D3DPT_TRIANGLELIST -- The type of primitive to render
    // numVerts / 3 -- Number of primitives (in our case triangles) to render
    // iList -- Pointer to index array
    // vertList -- Pointer to our vertex array
    // vList -- Point to the vertex array
    // sizeof(SVertex) -- Size of an individual vertex
    HRESULT rc = d3ddev->DrawPrimitiveUP(D3DPT_LINELIST, numVerts / 2, vertList, sizeof(SVertex));
    return (rc == D3D_OK); // Returns status of DrawPrimitiveUP()
}

void DxWrapper::SetProjection(int fieldOfViewDegrees)
{
    D3DXMatrixPerspectiveFovLH(&matProjection,
        D3DXToRadian(fieldOfViewDegrees),    // the horizontal field of view
        (FLOAT)d3dpp.BackBufferWidth/(FLOAT)d3dpp.BackBufferHeight,    // the aspect ratio
        0.0f,    // the near view-plane
        100.0f);    // the far view-plane
    d3ddev->SetTransform(D3DTS_PROJECTION, &matProjection);    // set the projection
}

void DxWrapper::SetCamera(const AssimpRendererD3d::Camera& sourceCamera)
{
	//DxWrapper::Instance()->LogWrite("SetCamera()");
    m_Camera = sourceCamera;

    // not die lookAt-Matrix setzen, für die fixed function pipeline
    D3DXMatrixLookAtLH(&matView,
        &D3DXVECTOR3 (m_Camera.vPos.x, m_Camera.vPos.y, m_Camera.vPos.z),    // the camera position
        &D3DXVECTOR3 (m_Camera.vLookAt.x, m_Camera.vLookAt.y, m_Camera.vLookAt.z),    // the look-at position
        &D3DXVECTOR3 (0.0f, 1.0f, 0.0f));    // the up direction
    d3ddev->SetTransform(D3DTS_VIEW, &matView);    // set the view transform to matView 

	// der assimp renderer hat auch seine eigene Camera
	assimpRenderer->SetCamera(sourceCamera);
}


void DxWrapper::SetWorldTransform(float posX, float posY, float posZ, float rotX, float rotY, float rotZ)
{
    D3DXMATRIX matTrans, matRotateX, matRotateY, matRotateZ;
    D3DXMatrixTranslation(&matTrans, posX, posY, posZ);
    D3DXMatrixRotationX(&matRotateX, rotX);
    D3DXMatrixRotationY(&matRotateY, rotY);
    D3DXMatrixRotationZ(&matRotateZ, rotZ);

    matWorldTransform = (matRotateX * matRotateY * matRotateZ * matTrans); 
       
    d3ddev->SetTransform(D3DTS_WORLD, &(matRotateX * matRotateY * matRotateZ * matTrans));
}

void DxWrapper::SetWorldTransform(const GameObject3D& obj)
{
    D3DXMATRIX matTrans, matRotateX, matRotateY, matRotateZ, matUnity, matScaling;

    D3DXMatrixTranslation(&matTrans, obj.m_Position.x, obj.m_Position.y, obj.m_Position.z);
    D3DXMatrixRotationX(&matRotateX, D3DXToRadian(obj.m_RotX + obj.m_Model.calibrationAngleX));
    D3DXMatrixRotationY(&matRotateY, D3DXToRadian(obj.m_RotY + obj.m_Model.calibrationAngleY));
    D3DXMatrixRotationZ(&matRotateZ, D3DXToRadian(-obj.m_RotZ + obj.m_Model.calibrationAngleZ)); 
    D3DXMatrixScaling(&matScaling, obj.m_ScaleFactorX, obj.m_ScaleFactorY, obj.m_ScaleFactorZ);
    
    matWorldTransform = (matScaling * matRotateX * matRotateY * matRotateZ * matTrans);

    d3ddev->SetTransform(D3DTS_WORLD, &matWorldTransform);
}


// this is the function that sets up the lights and materials
void DxWrapper::InitLight(void)
{
	DxWrapper::Instance()->LogWrite("InitLight()");

    D3DLIGHT9 light;    // create the light struct

    ZeroMemory(&light, sizeof(light));    // clear out the struct for use
    light.Type = D3DLIGHT_DIRECTIONAL;    // make the light type 'directional light'
    light.Diffuse.r = 0.5f;    // .5 red
    light.Diffuse.g = 0.5f;    // .5 green
    light.Diffuse.b = 0.5f;    // .5 blue
    light.Diffuse.a = 1.0f;    // full alpha (we'll get to that soon)

    D3DVECTOR vecDirection = {1.0f, 0.3f, 1.0f};    // the direction of the light
    light.Direction = vecDirection;    // set the direction

    d3ddev->SetLight(0, &light);    // send the light struct properties to light #0
    d3ddev->LightEnable(0, TRUE);    // turn on light #0
}

void DxWrapper::SetLight(D3DLIGHT9& light, int lightIndex)
{
	DxWrapper::Instance()->LogWrite("SetLight()");

    light.Type = D3DLIGHT_POINT;
    light.Diffuse.r = 0.5f;
    light.Diffuse.g = 0.5f;
    light.Diffuse.b = 0.5f;
    light.Diffuse.a = 1.0f;
    light.Range = 100.0f;
    light.Attenuation0 = 0.0f;
    light.Attenuation1 = 0.125f;
    light.Attenuation2 = 0.0f;

    // fixed function pipeline:
    //d3ddev->SetLight(lightIndex, &light);
    //d3ddev->LightEnable(lightIndex, TRUE);     
}


void DxWrapper::InitFont(int size)
{
	DxWrapper::Instance()->LogWrite("InitFont()");

    assert(d3ddev);
    HRESULT resultCode;

    m_logFontSize = size;
    resultCode = D3DXCreateFont(d3ddev,    // the D3D Device
        size,    // font height of 30
        0,    // default font width
        FW_NORMAL,    // font weight
        1,    // not using MipLevels
        true,    // italic font
        DEFAULT_CHARSET,    // default character set
        OUT_DEFAULT_PRECIS,    // This is usually used to let Windows determine what font should be used when the exact font desired is unclear. Because you usually know exactly what font you want in game development, this can be set to OUT_DEFAULT_PRECIS, for a normal output precision.
        DEFAULT_QUALITY,    // default Quality
        DEFAULT_PITCH | FF_DONTCARE,    // default pitch and family. Pitch refers to the number of charcters in an inch, where the smaller the pitch, the more the characters are spread out ( l  i  k  e     t  h  i  s ). 
        // A Font Family is a group of fonts that have similar characteristics and styles. Unless you are really picky, these should be set to their defaults, DEFAULT_PITCH || FF_DONTCARE.
        L"Arial",    // use Facename Arial
        &dxfont);    // the font object

    assert(resultCode == S_OK);
}

// TODO: überflüssig ! kann mit std::string abgefrühstückt werden (const fehlt)
void DxWrapper::RenderText(wchar_t* text, int posX , int posY)
{
    // create a RECT to contain the text
    static RECT textbox;
    int returnCode;
    SetRect(&textbox, posX, posY, d3dpp.BackBufferWidth, d3dpp.BackBufferHeight); 

    // draw the Hello World text

    assert (dxfont != NULL);

    returnCode = dxfont->DrawTextA(NULL,             // hier kann man ein Sprite-Objekt übergeben, auf das gemalt wird. Wenn man NULL übergibt, generiert DirectX intern ein eigenes.
        CW2A(text),                     // Text
        static_cast<INT>(wcslen(text)), // Number of chars to draw
        &textbox,                       // pointer to a rect that defines the position and the borders
        DT_LEFT | DT_TOP | DT_WORDBREAK,         // Font Format Flags
        D3DCOLOR_ARGB(255, 255, 255, 255)); // This parameter defines the color of the text to be displayed.  You can use both XRGB and ARGB formats for this.
    
    assert(returnCode != 0);
}

void DxWrapper::RenderText(const std::string &text, int posX , int posY)
{
    RenderText(CA2W(text.c_str()), posX, posY); 
}



void DxWrapper::ResetDevice()
{
    if(d3ddev)
    {
        if (d3dspt)
        {
            //d3dspt->OnLostDevice();     // If you are using sprites, you need to prepare the sprite interface for the reset.  Neglecting to do this will result in a loss of all your textures, and you will have to reload them. HAT NICHT GEFUNZT
            d3dspt->Release();
            d3dspt = NULL;
        }
        // This tells Direct3DSprite that the device has been lost, and that all of the video memory it has claimed should be released.  
        if (dxfont)
        {
            //dxfont->OnLostDevice();	  // das gleiche für Fonts  ... hat aber nicht geklappt, musste komplett Releasen, weil ich sonst nicht resetten konnte.
            dxfont->Release();
            dxfont = NULL;
        }

        d3ddev->Reset(&d3dpp);		// nach dem Rauswechseln ist das Device im state "lost" und muss wieder reinitialisiert werden mit den jeweiligen Parametern die wir für die Applikation definiert haben.        
        
        // auskommentiert, weil hat nicht gefunzt:
        //d3dspt->OnResetDevice();    // The tells Direct3DSprite that the device has been reset, and that all the memory can be re-acquired.
        //dxfont->OnResetDevice();    // das gleiche für Fonts
    }
}



// -------- Direct Input -------



void DxWrapper::Input(INPUTDATA* InputData, HWND hWnd)   
{
    static BYTE Keys[256];
    //static DIMOUSESTATE Mouse;    // We won't need the mouse for this

    GetKeys(&Keys[0]);
    //GetMouse(&Mouse);

    ZeroMemory(InputData, sizeof(INPUTDATA));

    if(Keys[DIK_UP])    // Up or W
        InputData->up = true;
    if(Keys[DIK_DOWN])
        InputData->down = true;
    if(Keys[DIK_LEFT] || Keys[DIK_A])
        InputData->left = true;
    if(Keys[DIK_RIGHT] || Keys[DIK_D])
        InputData->right = true;
    if(Keys[DIK_SPACE])
        InputData->fire1 = true;
    if(Keys[DIK_BACKSPACE])
        InputData->del = true;
    if(Keys[DIK_RETURN])
        InputData->enter = true;
    if(Keys[DIK_NUMPADMINUS])
        InputData->numMinus = true;
    if(Keys[DIK_NUMPADPLUS])
        InputData->numPlus = true;
    if(Keys[DIK_W])
        InputData->q = true;
    if(Keys[DIK_E])
        InputData->e = true;
    if(Keys[DIK_S])
        InputData->s = true;
    if(Keys[DIK_NUMPADENTER])
        InputData->numEnter = true;
    if(Keys[DIK_X])
        InputData->fire2 = true;
    if(Keys[DIK_H])
        InputData->h = true;

    if (Keys[DIK_NUMPAD4])
        InputData->num4 = true;
    if (Keys[DIK_NUMPAD2])
        InputData->num2 = true;
    if (Keys[DIK_NUMPAD6])
        InputData->num6 = true;
    if (Keys[DIK_NUMPAD8])
        InputData->num8 = true;
    if (Keys[DIK_NUMPAD9])
        InputData->num9 = true;
    if (Keys[DIK_NUMPAD3])
        InputData->num3 = true;


    if (Keys[DIK_ESCAPE])
        PostMessage(hWnd, WM_DESTROY, 0, 0);
}


// Create DirectInput and the Keyboard and Mouse Devices
void DxWrapper::InitDirectInput(HINSTANCE hInstance, GAMEWINDOW* gw)
{
    DirectInput8Create(hInstance,
        DIRECTINPUT_VERSION,
        IID_IDirectInput8,
        (void**)&din,
        NULL);

    din->CreateDevice(GUID_SysKeyboard, &dinkeyboard, NULL);
    din->CreateDevice(GUID_SysMouse, &dinmouse, NULL);

    dinkeyboard->SetDataFormat(&c_dfDIKeyboard);
    dinmouse->SetDataFormat(&c_dfDIMouse);

    dinkeyboard->SetCooperativeLevel(gw->hWnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
    dinmouse->SetCooperativeLevel(gw->hWnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);

    dinmouse->Acquire();
}

// Get the Current Keyboard Status
void DxWrapper::GetKeys(BYTE* KeyState)
{
    dinkeyboard->Acquire();
    dinkeyboard->GetDeviceState(256, (LPVOID)KeyState);

    for(int Index = 0; Index < 256; Index++)
        *(KeyState + Index) &= 0x80;
}

// Get the Latest Mouse Movements and Mouse Buttons
void DxWrapper::GetMouse(DIMOUSESTATE* MouseState)
{
    dinmouse->Acquire();

    dinmouse->GetDeviceState(sizeof(DIMOUSESTATE), (LPVOID)MouseState);

    MouseState->rgbButtons[0] &= 0x80;
    MouseState->rgbButtons[1] &= 0x80;
    MouseState->rgbButtons[2] &= 0x80;
    MouseState->rgbButtons[3] &= 0x80;
}

// Close the Devices and DirectInput
void DxWrapper::CloseDirectInput(void)
{
    dinkeyboard->Unacquire();
    dinmouse->Unacquire();
    din->Release(); 
}


// Debug Output / Logger

void DxWrapper::LogWrite(string s, boolean addTimeStamp)
{
	if (addTimeStamp)
	{
		long timeStamp = GetTimestampSinceStart();
		float timeSeconds = timeStamp / 1000.0f;
	    stringstream sstr(s);
		sstr  << "[" << timeSeconds << "]" << " : " << s << endl;
		s = sstr.str();
	}

	cout << s << endl;

	vecLog.insert(vecLog.begin(), s);
 	if (vecLog.size() > 30)
 	{
 		vecLog.pop_back();
 	}
}

void DxWrapper::LogClear()
{
    vecLog.clear();
}


DWORD DxWrapper::GetTimestampSinceStart()
{
	DWORD currentTime = GetTickCount(); 

	return currentTime - timeStampAtStart;
}

std::string DxWrapper::GetCurrentDir()
{
	// read out the current directory to set the applicationBaseDirectory
	WCHAR buffer[MAX_PATH];
	DWORD len = GetCurrentDirectory(sizeof(buffer), buffer);

	std::string returnString = std::string(CW2A(buffer));

	return returnString;
}



