#include "d3d8Wrapper.h"

D3D8Base::LPDIRECT3D8 g_D3D=NULL;

HMODULE realDLL;

extern "C"
{
	namespace D3D8Wrapper
	{	
		IDirect3DDevice8 *last_device = NULL;
		IDirect3DSurface8 *render_surface = NULL;
		void (*rendering_callback)( int );

		D3D8Wrapper::IDirect3D8* WINAPI Direct3DCreate8(UINT Version)
		{
			//sometimes, Intel drivers will clear the dll path. So let's save and restore it (do their job for them)
			char oldDllDirectory[MAX_PATH];
			GetDllDirectory(MAX_PATH, oldDllDirectory);

			// Get the real DLL path from the system directory, needs to be specific to avoid binding to the d3d8.dll we're in now!
			// Might be unsafe
			CHAR dll_path[1024];
			GetSystemDirectory(dll_path,1024);
			strcat(dll_path,"\\d3d8.dll");

			realDLL = LoadLibrary(dll_path);

			D3D8Wrapper::D3DCREATE realDirect3DCreate8 = (D3D8Wrapper::D3DCREATE)GetProcAddress(realDLL, "Direct3DCreate8");

			// Use the real Direct3DCreate8 to make the base object
			D3D8Base::IDirect3D8* realD3D = realDirect3DCreate8(D3D_SDK_VERSION);

			// Wrap the object
			D3D8Wrapper::IDirect3D8* wrappedD3D = D3D8Wrapper::IDirect3D8::GetDirect3D(realD3D);

			//restore old DLL directory
			SetDllDirectory(oldDllDirectory);

			return wrappedD3D;
		}
	}


	__declspec(dllexport) void __cdecl CloseDLL()
	{
		FreeLibrary(realDLL);
	}
	
	__declspec(dllexport) void __cdecl SetRenderingCallback(void (*callback)(int))
	{
		D3D8Wrapper::rendering_callback = callback;
	}

	__declspec(dllexport) void __cdecl ReadScreen(void *dest, int *width, int *height)
	{
		if (D3D8Wrapper::last_device == NULL)
		{
			*width = 0;
			*height = 0;
			return;
		}

		// surface...
		// make a D3DSURFACE_DESC, pass to GetDesc
		D3D8Base::D3DSURFACE_DESC desc;
		D3D8Wrapper::render_surface->GetDesc(&desc);

		// get out height/width
		*width = desc.Width;
		*height = desc.Height;

		// if dest isn't null
		if (dest != NULL)
		{
			// make a RECT with size of buffer
			RECT entire_buffer;
			entire_buffer.left = 0;
			entire_buffer.top = 0;
			entire_buffer.right = desc.Width;
			entire_buffer.bottom = desc.Height;

			//resolve rendertarget to a system memory texture, for locking
			//TODO! UNACCEPTABLE CODE! THIS IS HORRIBLE!
			//X8R8G8B8 or fail -- A8R8G8B8 will malfunction (is it because the source surface format isnt matching? I think so)
			static D3D8Wrapper::IDirect3DTexture8* tex = NULL;
			static RECT texRect;
			if(!tex || (texRect.right != entire_buffer.right) || (texRect.bottom != entire_buffer.bottom))
			{
				if(tex)
					tex->Release();
				texRect = entire_buffer;
				D3D8Wrapper::last_device->CreateTexture(desc.Width, desc.Height, 1, 0, D3D8Base::D3DFMT_X8R8G8B8, D3D8Base::D3DPOOL_SYSTEMMEM, &tex);
			}

			D3D8Wrapper::IDirect3DSurface8* surf;
			tex->GetSurfaceLevel(0,&surf);
			HRESULT hr = D3D8Wrapper::last_device->CopyRects(D3D8Wrapper::render_surface,NULL,0,surf,NULL);

			// make a D3DLOCKED_RECT, pass to LockRect
			D3D8Base::D3DLOCKED_RECT locked;
			hr = surf->LockRect(&locked,&entire_buffer,D3DLOCK_READONLY);

			//this loop was reversed from the original.
			//it should be faster anyway if anything since the reading can be prefetched forwardly.
			//TODO - allow bizhawk to handle flipped images.... nonetheless, it might not handle images with unusual pitches, although maybe we should consider that.
			//so this code will probably remain for quite some time
			int dest_row = desc.Height - 1;
			for (UINT from_row = 0; from_row < desc.Height; from_row++)
			{
				memcpy((char*)dest + (dest_row * desc.Width*4),(char*)locked.pBits + from_row * locked.Pitch, desc.Width*4);
				dest_row--;
			}

			// unlock rect
			surf->UnlockRect();
			surf->Release();
		}

		// release the surface
		//backbuffer->Release();
		
		// we're done, maybe?
	}
}