#include "Bildschirm.h"
#include "Punkt.h"
#include "Bild.h"
#include "Farbe.h"
#include "Fenster.h"
#include "Text.h"
#include "Objekt.h"

using namespace Framework;

// Inhalt der Bildschirmklass aus Bildschirm.h
// Konstruktor 
Bildschirm::Bildschirm( WFenster *f )
{
	fenster = f;
	pDirect3D = 0;
	pDevice = 0;
	pBackBuffer = 0;
	renderB = new Bild();
	ref = 1;
	InitializeCriticalSection( &threadSave );
	members = new ObjektArray();
	füllFarbe = new Farbe( 0, 0, 0, 0 );
	vollbild = 0;
	backBufferGröße = 0;
}

// Destruktor 
Bildschirm::~Bildschirm()
{
	if( renderB )
		renderB->release();
	renderB = 0;
	if( pDevice )
	{
		pDevice->Release();
		pDevice = NULL;
	}
	if( pDirect3D )
	{
		pDirect3D->Release();
		pDirect3D = NULL;
	}
	if( pBackBuffer )
	{
		pBackBuffer->Release();
		pBackBuffer = NULL;
	}
	if( fenster )
		fenster->release();
	if( füllFarbe )
		füllFarbe->release();
	if( backBufferGröße )
		backBufferGröße->release();
	DeleteCriticalSection( &threadSave );
	delete members;
}

// nicht konstant 
void Bildschirm::lock()
{
	EnterCriticalSection( &threadSave );
}

void Bildschirm::unlock()
{
	LeaveCriticalSection( &threadSave );
}

void Bildschirm::update()
{
	lock();
	HRESULT result;

	backRect.pBits = NULL;
	if( pDirect3D )
	{
		pDirect3D->Release();
		pDirect3D = 0;
	}
	if( pDevice )
	{
		pDevice->Release();
		pDevice = 0;
	}
	if( pBackBuffer )
	{
		pBackBuffer->Release();
		pBackBuffer = 0;
	}
	pDirect3D = Direct3DCreate9( D3D_SDK_VERSION );

    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory( &d3dpp,sizeof( d3dpp ) );
	d3dpp.Windowed = !vollbild;
	d3dpp.hDeviceWindow = fenster->getFensterHandle();
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
	d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
	d3dpp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
	Punkt *größe;
	if( !backBufferGröße )
		größe = fenster->getGröße();
	else
		größe = backBufferGröße->getThis();
	d3dpp.BackBufferHeight = größe->getY();
	d3dpp.BackBufferWidth = größe->getX();
	if( renderB )
		renderB->release();
	renderB = new Bild();
	if( !füllFarbe )
		renderB->neuBild( größe, new Farbe() );
	else
		renderB->neuBild( größe, füllFarbe->getThis() );

    result = pDirect3D->CreateDevice( D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,fenster->getFensterHandle(),
		D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE,&d3dpp,&pDevice );

	result = pDevice->GetBackBuffer( 0,0,D3DBACKBUFFER_TYPE_MONO,&pBackBuffer );
	unlock();
}

void Bildschirm::addMember( Objekt *obj ) // Fügt ein Objekt hinzu
{
	lock();
	members->addObjekt( obj );
	members->updateIndex( 0 );
	unlock();
}

void Bildschirm::removeMember( Objekt *obj ) // Entfernt ein Objekt
{
	lock();
	members->removeObjekt( obj );
	members->updateIndex( 0 );
	unlock();
}

void Bildschirm::render()
{
	int count = 0;
	if( renderB && pDevice )
	{
		lock();
		if( füllFarbe ) // clear screen
			renderB->füllRegion( 0, 0, renderB->getBreite(), renderB->getHöhe(), füllFarbe->getFarbe() );
		members->render( renderB ); // zeichnen nach zwischenbuffer
		// Beginne Bild 
		HRESULT result;
		result = pDevice->Clear( 0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),0.0f,0 );
		result = pBackBuffer->LockRect( &backRect, 0, 0 );
		// kopieren zum Bildschrirm 
		for(int y = 0; y < renderB->getHöhe(); y++)
		{
			memcpy( &( ( BYTE *)backRect.pBits )[backRect.Pitch * y], (void*)&( ( renderB->getBuffer() )[renderB->getBreite() * y] ), sizeof( D3DCOLOR ) * renderB->getBreite() );
		}
		// Beende Bild 
		result = pBackBuffer->UnlockRect();
		result = pDevice->Present( 0, 0, 0, 0 );
		if( result != S_OK )
		{
			count++;
			update();
		}
		else if( count )
			count--;
		unlock();
	}
	if( !pDevice )
	{
		count++;
		update();
	}
	if( count > 10 )
	{
		WMessageBox( fenster ? fenster->getFensterHandle() : 0, new Text( "Fehler" ), new Text( "Es int ein Fehler beim rendern." ), MB_ICONERROR );
		count = 0;
	}
}

void Bildschirm::setFüllFarbeZ( Farbe *f ) // setzt die Fill Farbe
{
	if( füllFarbe )
		füllFarbe->release();
	füllFarbe = f;
}

void Bildschirm::setFüllFarbe( Farbe *f )
{
	if( !füllFarbe )
		füllFarbe = new Farbe();
	füllFarbe->setFarbe( f->getFarbe() );
}

void Bildschirm::setFüllFarbe( int f )
{
	if( !füllFarbe )
		füllFarbe = new Farbe();
	füllFarbe->setFarbe( f );
}

void Bildschirm::setVollbild( bool vollbild ) // setzt vollbild
{
	this->vollbild = vollbild;
}

void Bildschirm::tick( double tickval )
{
	members->tick( tickval );
}

void Bildschirm::setBackBufferGröße( int breite, int höhe ) // setzt die Größe des Backbuffers
{
	if( !backBufferGröße )
		backBufferGröße = new Punkt();
	backBufferGröße->setP( breite, höhe );
}

void Bildschirm::setBackBufferGröße( Punkt *größe )
{
	if( !backBufferGröße )
		backBufferGröße = new Punkt();
	backBufferGröße->setP( größe->x, größe->y );
	größe->release();
}

void Bildschirm::setBackBufferGrößeZ( Punkt *größe )
{
	if( backBufferGröße )
		backBufferGröße->release();
	backBufferGröße = größe;
}

// constant 
Bild *Bildschirm::getRenderBild() const
{
	return renderB->getThis();
}

ObjektArray *Bildschirm::getMembers() const // gibt die Objekte zurück
{
	return members;
}

Farbe *Bildschirm::getFüllFarbe() const // gibt die Füll Farbe zurück
{
	return füllFarbe ? füllFarbe->getThis(): 0;
}

Farbe *Bildschirm::zFüllFarbe() const
{
	return füllFarbe;
}

bool Bildschirm::istVolbild() const // gibt zurück, ob vollbild an ist
{
	return vollbild;
}

Punkt *Bildschirm::getBackBufferGröße() const // gibt die Größe des Backbuffers zurück
{
	if( backBufferGröße )
		return backBufferGröße->getThis();
	return 0;
}

Punkt *Bildschirm::zBackBufferGröße() const
{
	return backBufferGröße;
}

// Reference Counting 
Bildschirm *Bildschirm::getThis()
{
	ref++;
	return this;
}

Bildschirm *Bildschirm::release()
{
	ref--;
	if( !ref )
		delete this;
	return 0;
}