/**************************************************************************
 *
 * Copyright 2010 Luca Barbieri
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 **************************************************************************/

#include "dxgi_private.h"
extern "C" {
#include "native.h"
#include <util/u_format.h>
#include <util/u_inlines.h>
#include <util/u_simple_shaders.h>
#include <pipe/p_shader_tokens.h>
}
#include <iostream>
#include <memory>

struct GalliumDXGIOutput;
struct GalliumDXGIAdapter;
struct GalliumDXGISwapChain;
struct GalliumDXGIFactory;

static HRESULT GalliumDXGISwapChainCreate(GalliumDXGIFactory* factory, IUnknown* device, const DXGI_SWAP_CHAIN_DESC& desc, IDXGISwapChain** out_swap_chain);
static HRESULT GalliumDXGIAdapterCreate(GalliumDXGIFactory* adapter, const struct native_platform* platform, void* dpy, IDXGIAdapter1** out_adapter);
static HRESULT GalliumDXGIOutputCreate(GalliumDXGIAdapter* adapter, const std::string& name, const struct native_connector* connector, IDXGIOutput** out_output);
static void GalliumDXGISwapChainRevalidate(IDXGISwapChain* swap_chain);

template<typename Base = IDXGIObject, typename Parent = IDXGIObject>
struct GalliumDXGIObject : public GalliumPrivateDataComObject<Base>
{
	ComPtr<Parent> parent;

	GalliumDXGIObject(Parent* p_parent = 0)
	{
		this->parent = p_parent;
	}

	virtual HRESULT STDMETHODCALLTYPE GetParent(
		REFIID riid,
		void **out_parent)
	{
		return parent->QueryInterface(riid, out_parent);
	}
};

COM_INTERFACE(IGalliumDXGIBackend, IUnknown)

// TODO: somehow check whether the window is fully obscured or not
struct GalliumDXGIIdentityBackend : public GalliumComObject<IGalliumDXGIBackend>
{
	virtual HRESULT STDMETHODCALLTYPE BeginPresent(
		HWND hwnd,
		void** present_cookie,
		void** window,
		RECT *rect,
		RGNDATA **rgndata,
		BOOL* preserve_aspect_ratio
	)
	{
		*window = (void*)hwnd;
		rect->left = 0;
		rect->top = 0;
		rect->right = INT_MAX;
		rect->bottom = INT_MAX;
		*rgndata = 0;

		// yes, because we like things looking good
		*preserve_aspect_ratio = TRUE;
		*present_cookie = 0;
		return S_OK;
	}

	virtual void STDMETHODCALLTYPE EndPresent(
		HWND hwnd,
		void* present_cookie
	)
	{}

	virtual HRESULT STDMETHODCALLTYPE TestPresent(HWND hwnd)
	{
		return S_OK;
	}

        virtual HRESULT STDMETHODCALLTYPE GetPresentSize(
                HWND hwnd,
                unsigned* width,
                unsigned* height
        )
        {
                *width = 0;
                *height = 0;
                return S_OK;
        }
};

// TODO: maybe install an X11 error hook, so we can return errors properly
struct GalliumDXGIX11IdentityBackend : public GalliumDXGIIdentityBackend
{
	Display* dpy;

	GalliumDXGIX11IdentityBackend(Display* dpy)
	: dpy(dpy)
	{}

	virtual HRESULT STDMETHODCALLTYPE GetPresentSize(
		HWND hwnd,
		unsigned* width,
		unsigned* height
	)
        {
		XWindowAttributes xwa;
		XGetWindowAttributes(dpy, (Window)hwnd, &xwa);
		*width = xwa.width;
		*height = xwa.height;
		return S_OK;
        }
};

struct GalliumDXGIFactory : public GalliumDXGIObject<IDXGIFactory1, IUnknown>
{
	HWND associated_window;
	const struct native_platform* platform;
	void* display;
	ComPtr<IGalliumDXGIBackend> backend;
	void* resolver_cookie;

	GalliumDXGIFactory(const struct native_platform* platform, void* display, IGalliumDXGIBackend* p_backend)
	: GalliumDXGIObject<IDXGIFactory1, IUnknown>((IUnknown*)NULL), platform(platform), display(display)
	 {
		if(p_backend)
			backend = p_backend;
		else if(!strcmp(platform->name, "X11"))
			backend.reset(new GalliumDXGIX11IdentityBackend((Display*)display));
		else
			backend.reset(new GalliumDXGIIdentityBackend());
	}

	virtual HRESULT STDMETHODCALLTYPE EnumAdapters(
		UINT adapter,
		IDXGIAdapter **out_adapter)
	{
		return EnumAdapters1(adapter, (IDXGIAdapter1**)out_adapter);
	}

	virtual HRESULT STDMETHODCALLTYPE EnumAdapters1(
		UINT adapter,
		IDXGIAdapter1 **out_adapter)
	{
		*out_adapter = 0;
		if(adapter == 0)
		{
			return GalliumDXGIAdapterCreate(this, platform, display, out_adapter);
		}
#if 0
		// TODO: enable this
		if(platform == native_get_x11_platform())
		{
			unsigned nscreens = ScreenCount((Display*)display);
			if(adapter < nscreens)
			{
				unsigned def_screen = DefaultScreen(display);
				if(adapter <= def_screen)
					--adapter;
				*out_adapter = GalliumDXGIAdapterCreate(this, platform, display, adapter);
				return S_OK;
			}
		}
#endif
		return DXGI_ERROR_NOT_FOUND;
	}

	/* TODO: this is a mysterious underdocumented magic API
	 * Can we have multiple windows associated?
	 * Can we have multiple windows associated if we use multiple factories?
	 * If so, what should GetWindowAssociation return?
	 * If not, does a new swapchain steal the association?
	 * Does this act for existing swapchains? For new swapchains?
	 */
	virtual HRESULT STDMETHODCALLTYPE MakeWindowAssociation(
		HWND window_handle,
		UINT flags)
	{
		/* TODO: actually implement, for Wine, X11 and KMS*/
		associated_window = window_handle;
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE GetWindowAssociation(
		HWND *pwindow_handle)
	{
		*pwindow_handle = associated_window;
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE CreateSwapChain(
		IUnknown *device,
		DXGI_SWAP_CHAIN_DESC *desc,
		IDXGISwapChain **out_swap_chain)
	{
		return GalliumDXGISwapChainCreate(this, device, *desc, out_swap_chain);
	}

	virtual HRESULT STDMETHODCALLTYPE CreateSoftwareAdapter(
		HMODULE module,
		IDXGIAdapter **out_adapter)
	{
		/* TODO: ignore the module, and just create a Gallium software screen */
		*out_adapter = 0;
		return E_NOTIMPL;
	}

	/* TODO: support hotplug */
	virtual BOOL STDMETHODCALLTYPE IsCurrent( void)
	{
		return TRUE;
	}
};

struct GalliumDXGIAdapter
	: public GalliumMultiComObject<
		 GalliumDXGIObject<IDXGIAdapter1, GalliumDXGIFactory>,
		 IGalliumAdapter>
{
	struct native_display* display;
	const struct native_config** configs;
	std::unordered_multimap<unsigned, unsigned> configs_by_pipe_format;
	std::unordered_map<unsigned, unsigned> configs_by_native_visual_id;
	const struct native_connector** connectors;
	unsigned num_configs;
	DXGI_ADAPTER_DESC1 desc;
	std::vector<ComPtr<IDXGIOutput> > outputs;
	int num_outputs;
	struct native_event_handler handler;

	GalliumDXGIAdapter(GalliumDXGIFactory* factory, const struct native_platform* platform, void* dpy)
	{
		this->parent = factory;

		handler.invalid_surface = handle_invalid_surface;
		handler.new_drm_screen = dxgi_loader_create_drm_screen;
		handler.new_sw_screen = dxgi_loader_create_sw_screen;
		display = platform->create_display(dpy, &handler, this);
		if(!display)
			throw E_FAIL;
		memset(&desc, 0, sizeof(desc));
		std::string s = std::string("GalliumD3D on ") + display->screen->get_name(display->screen) + " by " + display->screen->get_vendor(display->screen);

		/* hopefully no one will decide to use UTF-8 in Gallium name/vendor strings */
		for(int i = 0; i < std::min((int)s.size(), 127); ++i)
			desc.Description[i] = (WCHAR)s[i];

		// TODO: add an interface to get these; for now, return mid/low values
		desc.DedicatedVideoMemory = 256 << 20;
		desc.DedicatedSystemMemory = 256 << 20;
		desc.SharedSystemMemory = 1024 << 20;

		// TODO: we should actually use an unique ID instead
		*(void**)&desc.AdapterLuid = dpy;

		configs = display->get_configs(display, (int*)&num_configs);
		for(unsigned i = 0; i < num_configs; ++i)
		{
			if(configs[i]->window_bit)
			{
				configs_by_pipe_format.insert(std::make_pair(configs[i]->color_format, i));
				configs_by_native_visual_id[configs[i]->native_visual_id] = i;
			}
		}

		connectors = 0;
		num_outputs = 0;

		if(display->modeset)
		{
			int num_crtcs;

			connectors = display->modeset->get_connectors(display, &num_outputs, &num_crtcs);
			if(!connectors)
				num_outputs = 0;
			else if(!num_outputs)
			{
				free(connectors);
				connectors = 0;
			}
		}
		if(!num_outputs)
			num_outputs = 1;
	}

	static void handle_invalid_surface(struct native_display *ndpy, struct native_surface *nsurf, unsigned int seq_num)
	{
		GalliumDXGISwapChainRevalidate((IDXGISwapChain*)nsurf->user_data);
	}

	~GalliumDXGIAdapter()
	{
		display->destroy(display);
		free(configs);
		free(connectors);
	}

	virtual HRESULT STDMETHODCALLTYPE EnumOutputs(
		UINT output,
		IDXGIOutput **out_output)
	{
		if(output >= (unsigned)num_outputs)
			return DXGI_ERROR_NOT_FOUND;

		if(connectors)
		{
			std::ostringstream ss;
			ss << "output #" << output;
			return GalliumDXGIOutputCreate(this, ss.str(), connectors[output], out_output);
		}
		else
			return GalliumDXGIOutputCreate(this, "Unique output", NULL, out_output);
	}

	virtual HRESULT STDMETHODCALLTYPE GetDesc(
		DXGI_ADAPTER_DESC *desc)
	{
		memcpy(desc, &desc, sizeof(*desc));
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE GetDesc1(
		DXGI_ADAPTER_DESC1 *desc)
	{
		memcpy(desc, &desc, sizeof(*desc));
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE CheckInterfaceSupport(
		REFGUID interface_name,
		LARGE_INTEGER *u_m_d_version)
	{
		// these number was taken from Windows 7 with Catalyst 10.8: its meaning is unclear
		if(interface_name == IID_ID3D11Device || interface_name == IID_ID3D10Device1 || interface_name == IID_ID3D10Device)
		{
			u_m_d_version->QuadPart = 0x00080011000a0411ULL;
			return S_OK;
		}
		return DXGI_ERROR_UNSUPPORTED;
	}

	pipe_screen* STDMETHODCALLTYPE GetGalliumScreen()
	{
		return display->screen;
	}

	pipe_screen* STDMETHODCALLTYPE GetGalliumReferenceSoftwareScreen()
	{
		// TODO: give a softpipe screen
		return display->screen;
	}

	pipe_screen* STDMETHODCALLTYPE GetGalliumFastSoftwareScreen()
	{
		// TODO: give an llvmpipe screen
		return display->screen;
	}
};


struct GalliumDXGIOutput : public GalliumDXGIObject<IDXGIOutput, GalliumDXGIAdapter>
{
	DXGI_OUTPUT_DESC desc;
	const struct native_mode** modes;
	DXGI_MODE_DESC* dxgi_modes;
	unsigned num_modes;
	const struct native_connector* connector;
	DXGI_GAMMA_CONTROL* gamma;

	GalliumDXGIOutput(GalliumDXGIAdapter* adapter, std::string name, const struct native_connector* connector = 0)
	: GalliumDXGIObject<IDXGIOutput, GalliumDXGIAdapter>(adapter), connector(connector)
	{
		memset(&desc, 0, sizeof(desc));
		for(unsigned i = 0; i < std::min(name.size(), sizeof(desc.DeviceName) - 1); ++i)
			desc.DeviceName[i] = name[i];
		desc.AttachedToDesktop = TRUE;
		/* TODO: should put an HMONITOR in desc.Monitor */

		gamma = 0;
		num_modes = 0;
		modes = 0;
		if(connector)
		{
			modes = parent->display->modeset->get_modes(parent->display, connector, (int*)&num_modes);
			if(modes && num_modes)
			{
				dxgi_modes = new DXGI_MODE_DESC[num_modes];
				for(unsigned i = 0; i < num_modes; ++i)
				{
					dxgi_modes[i].Width = modes[i]->width;
					dxgi_modes[i].Height = modes[i]->height;
					dxgi_modes[i].RefreshRate.Numerator = modes[i]->refresh_rate;
					dxgi_modes[i].RefreshRate.Denominator = 1;
					dxgi_modes[i].Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
					dxgi_modes[i].ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
				}
			}
			else
			{
				if(modes)
				{
					free(modes);
					modes = 0;
				}
				goto use_fake_mode;
			}
		}
		else
		{
use_fake_mode:
			dxgi_modes = new DXGI_MODE_DESC[1];
			dxgi_modes[0].Width = 1920;
			dxgi_modes[0].Height = 1200;
			dxgi_modes[0].RefreshRate.Numerator = 60;
			dxgi_modes[0].RefreshRate.Denominator = 1;
			dxgi_modes[0].Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
			dxgi_modes[0].ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
		}
	}

	~GalliumDXGIOutput()
	{
		delete [] dxgi_modes;
		free(modes);
		if(gamma)
			delete gamma;
	}

	virtual HRESULT STDMETHODCALLTYPE GetDesc(
		DXGI_OUTPUT_DESC *out_desc)
	{
		*out_desc = desc;
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE GetDisplayModeList(
		DXGI_FORMAT enum_format,
		UINT flags,
		UINT *pcount,
		DXGI_MODE_DESC *desc)
	{
		/* TODO: should we return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE when we don't
		 * support modesetting instead of fake modes?
		 */
		pipe_format format = dxgi_to_pipe_format[enum_format];
		if(parent->configs_by_pipe_format.count(format))
		{
			if(!desc)
			{
				*pcount = num_modes;
				return S_OK;
			}

			unsigned copy_modes = std::min(num_modes, *pcount);
			for(unsigned i = 0; i < copy_modes; ++i)
			{
				desc[i] = dxgi_modes[i];
				desc[i].Format = enum_format;
			}
			*pcount = num_modes;

			if(copy_modes < num_modes)
				return DXGI_ERROR_MORE_DATA;
			else
				return S_OK;
		}
		else
		{
			*pcount = 0;
			return S_OK;
		}
	}

	virtual HRESULT STDMETHODCALLTYPE FindClosestMatchingMode(
		const DXGI_MODE_DESC *pModeToMatch,
		DXGI_MODE_DESC *closest_match,
		IUnknown *concerned_device)
	{
		/* TODO: actually implement this */
		DXGI_FORMAT dxgi_format = pModeToMatch->Format;
		enum pipe_format format = dxgi_to_pipe_format[dxgi_format];
		init_pipe_to_dxgi_format();
		if(!parent->configs_by_pipe_format.count(format))
		{
			if(!concerned_device)
				return E_FAIL;
			else
			{
				format = parent->configs[0]->color_format;
				dxgi_format = pipe_to_dxgi_format[format];
			}
		}

		*closest_match = dxgi_modes[0];
		closest_match->Format = dxgi_format;
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE WaitForVBlank( void)
	{
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE TakeOwnership(
		IUnknown *device,
		BOOL exclusive)
	{
		return S_OK;
	}

	virtual void STDMETHODCALLTYPE ReleaseOwnership( void)
	{
	}

	virtual HRESULT STDMETHODCALLTYPE GetGammaControlCapabilities(
		DXGI_GAMMA_CONTROL_CAPABILITIES *gamma_caps)
	{
		memset(gamma_caps, 0, sizeof(*gamma_caps));
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE SetGammaControl(
			const DXGI_GAMMA_CONTROL *pArray)
	{
		if(!gamma)
			gamma = new DXGI_GAMMA_CONTROL;
		*gamma = *pArray;
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE GetGammaControl(
			DXGI_GAMMA_CONTROL *pArray)
	{
		if(gamma)
			*pArray = *gamma;
		else
		{
			pArray->Scale.Red = 1;
			pArray->Scale.Green = 1;
			pArray->Scale.Blue = 1;
			pArray->Offset.Red = 0;
			pArray->Offset.Green = 0;
			pArray->Offset.Blue = 0;
			for(unsigned i = 0; i <= 1024; ++i)
				pArray->GammaCurve[i].Red = pArray->GammaCurve[i].Green = pArray->GammaCurve[i].Blue = (float)i / 1024.0;
		}
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE SetDisplaySurface(
		IDXGISurface *scanout_surface)
	{
		return E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE GetDisplaySurfaceData(
		IDXGISurface *destination)
	{
		return E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE GetFrameStatistics(
		DXGI_FRAME_STATISTICS *stats)
	{
		memset(stats, 0, sizeof(*stats));
#ifdef _WIN32
		QueryPerformanceCounter(&stats->SyncQPCTime);
#endif
		return E_NOTIMPL;
	}
};

/* Swap chain are rather complex, and Microsoft's documentation is rather
 * lacking. As far as I know, this is the most thorough publicly available
 * description of how swap chains work, based on multiple sources and
 * experimentation.
 *
 * There are two modes (called "swap effects") that a swap chain can operate in:
 * discard and sequential.
 *
 * In discard mode, things always look as if there is a single buffer, which
 * you can get with GetBuffers(0).
 * The 2D texture returned by GetBuffers(0) and can only be
 * used as a render target view and for resource copies, since no CPU access
 * flags are set and only the D3D11_BIND_RENDER_TARGET bind flag is set.
 * On Present, it is copied to the actual display
 * surface and the contents become undefined.
 * D3D may internally use multiple buffers, but you can't observe this, except
 * by looking at the buffer contents after Present (but those are undefined).
 * If it uses multiple buffers internally, then it will normally use buffer_count buffers
 * (this has latency implications).
 * Discard mode seems to internally use a single buffer in windowed mode,
 * even if DWM is enabled, and buffer_count buffers in fullscreen mode.
 *
 * In sequential mode, the runtime alllocates buffer_count buffers.
 * You can get each with GetBuffers(n).
 * GetBuffers(0) ALWAYS points to the backbuffer to be presented and has the
 * same usage constraints as the discard mode.
 * GetBuffer(n) with n > 0 points to resources that are identical to buffer 0, but
 * are classified as "read-only resources" (due to DXGI_USAGE_READ_ONLY),
 * meaning that you can't create render target views on them, or use them as
 * a CopyResource/CopySubresourceRegion destination.
 * It appears the only valid operation is to use them as a source for CopyResource
 * and CopySubresourceRegion as well as just waiting for them to become
 * buffer 0 again.
 * Buffer n - 1 is always displayed on screen.
 * When you call Present(), the contents of the buffers are rotated, so that buffer 0
 * goes to buffer n - 1, and is thus displayed, and buffer 1 goes to buffer 0, becomes
 * the accessible back buffer.
 * The resources themselves are NOT rotated, so that you can still render on the
 * same ID3D11Texture2D*, and views based on it, that you got before Present().
 *
 * Present seems to happen by either copying the relevant buffer into the window,
 * or alternatively making it the current one, either by programming the CRTC or
 * by sending the resource name to the DWM compositor.
 *
 * Hence, you can call GetBuffer(0) once and keep using the same ID3D11Texture2D*
 * and ID3D11RenderTargetView* (and other views if needed) you got from it.
 *
 * If the window gets resized, DXGI will then "emulate" all successive presentations,
 * by using a stretched blit automatically.
 * Thus, you should handle WM_SIZE and call ResizeBuffers to update the DXGI
 * swapchain buffers size to the new window size.
 * Doing so requires you to release all GetBuffers() results and anything referencing
 * them, including views and Direct3D11 deferred context command lists (this is
 * documented).
 *
 * How does Microsoft implement the rotation behavior?
 * It turns out that it does it by calling RotateResourceIdentitiesDXGI in the user-mode
 * DDI driver.
 * This will rotate the kernel buffer handle, or possibly rotate the GPU virtual memory
 * mappings.
 *
 * The reason this is done by driver instead of by the runtime appears to be that
 * this is necessary to support driver-provided command list support, since otherwise
 * the command list would not always target the current backbuffer, since it would
 * be done at the driver level, while only the runtime knows about the rotation.
 *
 * OK, so how do we implement this in Gallium?
 *
 * There are three strategies:
 * 1. Use a single buffer, and always copy it to a window system provided buffer, or
 *	just give the buffer to the window system if it supports that
 * 2. Rotate the buffers in the D3D1x implementation, and recreate and rebind the views.
 *	 Don't support driver-provided command lists
 * 3. Add this rotation functionality to the Gallium driver, with the idea that it would rotate
 *	remap GPU virtual memory, so that virtual address are unchanged, but the physical
 *	ones are rotated (so that pushbuffers remain valid).
 *	If the driver does not support this, either fall back to (1), or have a layer doing this,
 *	putting a deferred context layer over this intermediate layer.
 *
 * (2) is not acceptable since it prevents an optimal implementation.
 * (3) is the ideal solution, but it is complicated.
 *
 * Hence, we implement (1) for now, and will switch to (3) later.
 *
 * Note that (1) doesn't really work for DXGI_SWAP_EFFECT_SEQUENTIAL with more
 * than one buffer, so we just pretend we got asked for a single buffer in that case
 * Fortunately, no one seems to rely on that, so we'll just not implement it at first, and
 * later perform the rotation with blits.
 * Once we switch to (3), we'll just use real rotation to do it..
 *
 * DXGI_SWAP_EFFECT_SEQUENTIAL with more than one buffer is of dubious use
 * anyway, since you can only render or write to buffer 0, and other buffers can apparently
 * be used only as sources for copies.
 * I was unable to find any code using it either in DirectX SDK examples, or on the web.
 *
 * It seems the only reason you would use it is to not have to redraw from scratch, while
 * also possibly avoid a copy compared to buffer_count == 1, assuming that your
 * application is OK with having to redraw starting not from the last frame, but from
 * one/two/more frames behind it.
 *
 * A better design would forbid the user specifying buffer_count explicitly, and
 * would instead let the application give an upper bound on how old the buffer can
 * become after presentation, with "infinite" being equivalent to discard.
 * The runtime would then tell the application with frame number the buffer switched to
 * after present.
 * In addition, in a better design, the application would be allowed to specify the
 * number of buffers available, having all them usable for rendering, so that things
 * like video players could efficiently decode frames in parallel.
 * Present would in such a better design gain a way to specify the number of buffers
 * to present.
 *
 * Other miscellaneous info:
 * DXGI_PRESENT_DO_NOT_SEQUENCE causes DXGI to hold the frame for another
 * vblank interval without rotating the resource data.
 *
 * References:
 * "DXGI Overview" in MSDN
 * IDXGISwapChain documentation on MSDN
 * "RotateResourceIdentitiesDXGI" on MSDN
 * http://forums.xna.com/forums/p/42362/266016.aspx
 */

static float quad_data[] = {
	-1, -1, 0, 0,
	-1, 1, 0, 1,
	1, 1, 1, 1,
	1, -1, 1, 0,
};

struct dxgi_blitter
{
	pipe_context* pipe;
	bool normalized;
	void* fs;
	void* vs;
	void* sampler[2];
	void* elements;
	void* blend;
	void* rasterizer;
	void* zsa;
	struct pipe_clip_state clip;
	struct pipe_vertex_buffer vbuf;
	struct pipe_draw_info draw;

	dxgi_blitter(pipe_context* pipe)
	: pipe(pipe)
	{
		//normalized = !!pipe->screen->get_param(pipe, PIPE_CAP_NPOT_TEXTURES);
		// TODO: need to update buffer in unnormalized case
		normalized = true;

		struct pipe_rasterizer_state rs_state;
		memset(&rs_state, 0, sizeof(rs_state));
		rs_state.cull_face = PIPE_FACE_NONE;
		rs_state.gl_rasterization_rules = 1;
		rs_state.flatshade = 1;
		rasterizer = pipe->create_rasterizer_state(pipe, &rs_state);

		struct pipe_blend_state blendd;
		memset(&blendd, 0, sizeof(blendd));
		blendd.rt[0].colormask = PIPE_MASK_RGBA;
		blend = pipe->create_blend_state(pipe, &blendd);

		struct pipe_depth_stencil_alpha_state zsad;
		memset(&zsad, 0, sizeof(zsad));
		zsa = pipe->create_depth_stencil_alpha_state(pipe, &zsad);

		struct pipe_vertex_element velem[2];
		memset(&velem[0], 0, sizeof(velem[0]) * 2);
		velem[0].src_offset = 0;
		velem[0].src_format = PIPE_FORMAT_R32G32_FLOAT;
		velem[1].src_offset = 8;
		velem[1].src_format = PIPE_FORMAT_R32G32_FLOAT;
		elements = pipe->create_vertex_elements_state(pipe, 2, &velem[0]);

		for(unsigned stretch = 0; stretch < 2; ++stretch)
		{
			struct pipe_sampler_state sampler_state;
			memset(&sampler_state, 0, sizeof(sampler_state));
			sampler_state.min_img_filter = stretch ? PIPE_TEX_FILTER_LINEAR : PIPE_TEX_FILTER_NEAREST;
			sampler_state.mag_img_filter = stretch ? PIPE_TEX_FILTER_LINEAR : PIPE_TEX_FILTER_NEAREST;
			sampler_state.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
			sampler_state.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
			sampler_state.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
			sampler_state.normalized_coords = normalized;

			sampler[stretch] = pipe->create_sampler_state(pipe, &sampler_state);
		}

		fs = util_make_fragment_tex_shader(pipe, normalized ? TGSI_TEXTURE_2D : TGSI_TEXTURE_RECT, TGSI_INTERPOLATE_LINEAR);

		const unsigned semantic_names[] = { TGSI_SEMANTIC_POSITION, TGSI_SEMANTIC_GENERIC };
		const unsigned semantic_indices[] = { 0, 0 };
		vs = util_make_vertex_passthrough_shader(pipe, 2, semantic_names, semantic_indices);

		vbuf.buffer = pipe_buffer_create(pipe->screen, PIPE_BIND_VERTEX_BUFFER, sizeof(quad_data));
		vbuf.buffer_offset = 0;
		vbuf.max_index = ~0;
		vbuf.stride = 4 * sizeof(float);
		pipe_buffer_write(pipe, vbuf.buffer, 0, sizeof(quad_data), quad_data);

		memset(&clip, 0, sizeof(clip));

		memset(&draw, 0, sizeof(draw));
		draw.mode = PIPE_PRIM_QUADS;
		draw.count = 4;
		draw.instance_count = 1;
		draw.max_index = ~0;
	}

	void blit(struct pipe_surface* surf, struct pipe_sampler_view* view, unsigned x, unsigned y, unsigned w, unsigned h)
	{
		struct pipe_framebuffer_state fb;
		memset(&fb, 0, sizeof(fb));
		fb.nr_cbufs = 1;
		fb.cbufs[0] = surf;
		fb.width = surf->width;
		fb.height = surf->height;

		struct pipe_viewport_state viewport;
		float half_width = w * 0.5f;
		float half_height = h * 0.5f;
		viewport.scale[0] = half_width;
		viewport.scale[1] = half_height;
		viewport.scale[2] = 1.0f;
		viewport.scale[3] = 1.0f;
		viewport.translate[0] = x + half_width;
		viewport.translate[1] = y + half_height;
		viewport.translate[2] = 0.0f;
		viewport.translate[3] = 1.0f;

		bool stretch = view->texture->width0 != w || view->texture->height0 != h;
		if(pipe->render_condition)
			pipe->render_condition(pipe, 0, 0);
		pipe->set_framebuffer_state(pipe, &fb);
		pipe->bind_fragment_sampler_states(pipe, 1, &sampler[stretch]);
		pipe->set_viewport_state(pipe, &viewport);
		pipe->set_clip_state(pipe, &clip);
		pipe->bind_rasterizer_state(pipe, rasterizer);
		pipe->bind_depth_stencil_alpha_state(pipe, zsa);
		pipe->bind_blend_state(pipe, blend);
		pipe->bind_vertex_elements_state(pipe, elements);
		pipe->set_vertex_buffers(pipe, 1, &vbuf);
		pipe->bind_fs_state(pipe, fs);
		pipe->bind_vs_state(pipe, vs);
		if(pipe->bind_gs_state)
			pipe->bind_gs_state(pipe, 0);
		if(pipe->bind_stream_output_state)
			pipe->bind_stream_output_state(pipe, 0);
		pipe->set_fragment_sampler_views(pipe, 1, &view);

		pipe->draw_vbo(pipe, &draw);
	}

	~dxgi_blitter()
	{
		pipe->delete_blend_state(pipe, blend);
		pipe->delete_rasterizer_state(pipe, rasterizer);
		pipe->delete_depth_stencil_alpha_state(pipe, zsa);
		pipe->delete_sampler_state(pipe, sampler[0]);
		pipe->delete_sampler_state(pipe, sampler[1]);
		pipe->delete_vertex_elements_state(pipe, elements);
		pipe->delete_vs_state(pipe, vs);
		pipe->delete_fs_state(pipe, fs);
		pipe->screen->resource_destroy(pipe->screen, vbuf.buffer);
	}
};

struct GalliumDXGISwapChain : public GalliumDXGIObject<IDXGISwapChain, GalliumDXGIFactory>
{
	ComPtr<IDXGIDevice>dxgi_device;
	ComPtr<IGalliumDevice>gallium_device;
	ComPtr<GalliumDXGIAdapter> adapter;
	ComPtr<IDXGIOutput> target;

	DXGI_SWAP_CHAIN_DESC desc;

	struct native_surface* surface;
	const struct native_config* config;

	void* window;
	struct pipe_resource* resources[NUM_NATIVE_ATTACHMENTS];
	int width;
	int height;
	unsigned seq_num;
	bool ever_validated;
	bool needs_validation;
	unsigned present_count;

	ComPtr<IDXGISurface> buffer0;
	struct pipe_resource* gallium_buffer0;
	struct pipe_sampler_view* gallium_buffer0_view;

	struct pipe_context* pipe;
	bool owns_pipe;

	BOOL fullscreen;

	std::auto_ptr<dxgi_blitter> blitter;
	bool formats_compatible;

	GalliumDXGISwapChain(GalliumDXGIFactory* factory, IUnknown* p_device, const DXGI_SWAP_CHAIN_DESC& p_desc)
	: GalliumDXGIObject<IDXGISwapChain, GalliumDXGIFactory>(factory), desc(p_desc), surface(0)
	{
		HRESULT hr;

		hr = p_device->QueryInterface(IID_IGalliumDevice, (void**)&gallium_device);
		if(!SUCCEEDED(hr))
			throw hr;

		hr = p_device->QueryInterface(IID_IDXGIDevice, (void**)&dxgi_device);
		if(!SUCCEEDED(hr))
			throw hr;

		hr = dxgi_device->GetAdapter((IDXGIAdapter**)&adapter);
		if(!SUCCEEDED(hr))
			throw hr;

		memset(resources, 0, sizeof(resources));

		if(desc.SwapEffect == DXGI_SWAP_EFFECT_SEQUENTIAL && desc.BufferCount != 1)
		{
			std::cerr << "Gallium DXGI: if DXGI_SWAP_EFFECT_SEQUENTIAL is specified, only buffer_count == 1 is implemented, but " << desc.BufferCount << " was specified: ignoring this" << std::endl;
			// change the returned desc, so that the application might perhaps notice what we did and react well
			desc.BufferCount = 1;
		}

		pipe = gallium_device->GetGalliumContext();
		owns_pipe = false;
		if(!pipe)
		{
			pipe = adapter->display->screen->context_create(adapter->display->screen, 0);
			owns_pipe = true;
		}

		blitter.reset(new dxgi_blitter(pipe));
		window = 0;

		hr = resolve_zero_width_height(true);
		if(!SUCCEEDED(hr))
			throw hr;
	}

	void init_for_window()
	{
		if(surface)
		{
			surface->destroy(surface);
			surface = 0;
		}

		unsigned config_num;
		if(!strcmp(parent->platform->name, "X11"))
		{
			XWindowAttributes xwa;
			XGetWindowAttributes((Display*)parent->display, (Window)window, &xwa);
			assert(adapter->configs_by_native_visual_id.count(xwa.visual->visualid));
			config_num = adapter->configs_by_native_visual_id[xwa.visual->visualid];
		}
		else
		{
			enum pipe_format format = dxgi_to_pipe_format[desc.BufferDesc.Format];
			if(!adapter->configs_by_pipe_format.count(format))
			{
				if(adapter->configs_by_pipe_format.empty())
					throw E_FAIL;
				// TODO: choose the best match
				format = (pipe_format)adapter->configs_by_pipe_format.begin()->first;
			}
			// TODO: choose the best config
			config_num = adapter->configs_by_pipe_format.find(format)->second;
		}

		config = adapter->configs[config_num];
		surface = adapter->display->create_window_surface(adapter->display, (EGLNativeWindowType)window, config);
		surface->user_data = this;

		width = 0;
		height = 0;
		seq_num = 0;
		present_count = 0;
		needs_validation = true;
		ever_validated = false;

		formats_compatible = util_is_format_compatible(
				util_format_description(dxgi_to_pipe_format[desc.BufferDesc.Format]),
				util_format_description(config->color_format));
	}

	~GalliumDXGISwapChain()
	{
		if(owns_pipe)
			pipe->destroy(pipe);
	}

	virtual HRESULT STDMETHODCALLTYPE GetDevice(
		REFIID riid,
		void **pdevice)
	{
		return dxgi_device->QueryInterface(riid, pdevice);
	}

	HRESULT create_buffer0()
	{
		HRESULT hr;
		ComPtr<IDXGISurface> new_buffer0;
		DXGI_USAGE usage = DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_RENDER_TARGET_OUTPUT;
		if(desc.SwapEffect == DXGI_SWAP_EFFECT_DISCARD)
			usage |= DXGI_USAGE_DISCARD_ON_PRESENT;
		// for our blitter
		usage |= DXGI_USAGE_SHADER_INPUT;

		DXGI_SURFACE_DESC surface_desc;
		surface_desc.Format = desc.BufferDesc.Format;
		surface_desc.Width = desc.BufferDesc.Width;
		surface_desc.Height = desc.BufferDesc.Height;
		surface_desc.SampleDesc = desc.SampleDesc;
		hr = dxgi_device->CreateSurface(&surface_desc, 1, usage, 0, &new_buffer0);
		if(!SUCCEEDED(hr))
			return hr;

		ComPtr<IGalliumResource> gallium_resource;
		hr = new_buffer0->QueryInterface(IID_IGalliumResource, (void**)&gallium_resource);
		if(!SUCCEEDED(hr))
			return hr;

		struct pipe_resource* new_gallium_buffer0 = gallium_resource->GetGalliumResource();
		if(!new_gallium_buffer0)
			return E_FAIL;

		buffer0.reset(new_buffer0.steal());
		gallium_buffer0 = new_gallium_buffer0;
		struct pipe_sampler_view templat;
		memset(&templat, 0, sizeof(templat));
		templat.texture = gallium_buffer0;
		templat.swizzle_r = 0;
		templat.swizzle_g = 1;
		templat.swizzle_b = 2;
		templat.swizzle_a = 3;
		templat.format = gallium_buffer0->format;
		gallium_buffer0_view = pipe->create_sampler_view(pipe, gallium_buffer0, &templat);
		return S_OK;
	}

	bool validate()
	{
		unsigned new_seq_num;
		needs_validation = false;

		if(!surface->validate(surface, (1 << NATIVE_ATTACHMENT_BACK_LEFT) | (1 << NATIVE_ATTACHMENT_FRONT_LEFT), &new_seq_num, resources, &width, &height))
			return false;

		if(!ever_validated || seq_num != new_seq_num)
		{
			seq_num = new_seq_num;
			ever_validated = true;
		}
		return true;
	}

	HRESULT resolve_zero_width_height(bool force = false)
	{
		if(!force && desc.BufferDesc.Width && desc.BufferDesc.Height)
			return S_OK;

		unsigned width, height;
		HRESULT hr = parent->backend->GetPresentSize(desc.OutputWindow, &width, &height);
		if(!SUCCEEDED(hr))
			return hr;

		// On Windows, 8 is used, and a debug message saying so gets printed
		if(!width)
			width = 8;
		if(!height)
			height = 8;

		if(!desc.BufferDesc.Width)
			desc.BufferDesc.Width = width;
		if(!desc.BufferDesc.Height)
			desc.BufferDesc.Height = height;
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE Present(
		UINT sync_interval,
		UINT flags)
	{
		HRESULT hr;
		if(flags & DXGI_PRESENT_TEST)
			return parent->backend->TestPresent(desc.OutputWindow);

		if(!buffer0)
		{
			HRESULT hr = create_buffer0();
			if(!SUCCEEDED(hr))
				return hr;
		}

		void* cur_window = 0;
		RECT rect;
		RGNDATA* rgndata;
		BOOL preserve_aspect_ratio;
		unsigned dst_w, dst_h;
		bool db;
		struct pipe_resource* dst;
		struct pipe_resource* src;
		struct pipe_surface* dst_surface;
		enum native_attachment att;

		void* present_cookie;
		hr = parent->backend->BeginPresent(desc.OutputWindow, &present_cookie, &cur_window, &rect, &rgndata, &preserve_aspect_ratio);
		if(hr != S_OK)
			return hr;

		if(!cur_window || rect.left >= rect.right || rect.top >= rect.bottom)
			goto end_present;

		if(cur_window != window)
		{
			window = cur_window;
			init_for_window();
		}

		if(needs_validation)
		{
			if(!validate())
				return DXGI_ERROR_DEVICE_REMOVED;
		}

		db = !!(config->buffer_mask & (1 << NATIVE_ATTACHMENT_BACK_LEFT));
		dst = resources[db ? NATIVE_ATTACHMENT_BACK_LEFT : NATIVE_ATTACHMENT_FRONT_LEFT];
		src = gallium_buffer0;
		dst_surface = 0;

		assert(src);
		assert(dst);

		/* TODO: sharing the context for blitting won't work correctly if queries are active
		 * Hopefully no one is crazy enough to keep queries active while presenting, expecting
		 * sensible results.
		 * We could alternatively force using another context, but that might cause inefficiency issues
		 */

		if((unsigned)rect.right > dst->width0)
			rect.right = dst->width0;
		if((unsigned)rect.bottom > dst->height0)
			rect.bottom = dst->height0;
		if(rect.left > rect.right)
			rect.left = rect.right;
		if(rect.top > rect.bottom)
			rect.top = rect.bottom;

		if(rect.left >= rect.right && rect.top >= rect.bottom)
			goto end_present;

		dst_w = rect.right - rect.left;
		dst_h = rect.bottom - rect.top;

		// TODO: add support for rgndata
//		if(preserve_aspect_ratio || !rgndata)
		if(1)
		{
			unsigned blit_x, blit_y, blit_w, blit_h;
			float black[4] = {0, 0, 0, 0};

			if(!formats_compatible || src->width0 != dst_w || src->height0 != dst_h) {
				struct pipe_surface templat;
				templat.usage = PIPE_BIND_RENDER_TARGET;
				templat.format = dst->format;
				templat.u.tex.level = 0;
				templat.u.tex.first_layer = 0;
				templat.u.tex.last_layer = 0;
				dst_surface = pipe->create_surface(pipe, dst, &templat);
			}

			if(preserve_aspect_ratio)
			{
				int delta = src->width0 * dst_h - dst_w * src->height0;
				if(delta > 0)
				{
					blit_w = dst_w;
					blit_h = dst_w * src->height0 / src->width0;
				}
				else if(delta < 0)
				{
					blit_w = dst_h * src->width0 / src->height0;
					blit_h = dst_h;
				}
				else
				{
					blit_w = dst_w;
					blit_h = dst_h;
				}

				blit_x = (dst_w - blit_w) >> 1;
				blit_y = (dst_h - blit_h) >> 1;
			}
			else
			{
				blit_x = 0;
				blit_y = 0;
				blit_w = dst_w;
				blit_h = dst_h;
			}

			if(blit_x)
				pipe->clear_render_target(pipe, dst_surface, black, rect.left, rect.top, blit_x, dst_h);
			if(blit_y)
				pipe->clear_render_target(pipe, dst_surface, black, rect.left, rect.top, dst_w, blit_y);

			if(formats_compatible && blit_w == src->width0 && blit_h == src->height0)
			{
				pipe_box box;
				box.x = box.y = box.z;
				box.width = blit_w;
				box.height = blit_h;
				box.z = 1;
				pipe->resource_copy_region(pipe, dst, 0, rect.left, rect.top, 0, src, 0, &box);
			}
			else
			{
				blitter->blit(dst_surface, gallium_buffer0_view, rect.left + blit_x, rect.top + blit_y, blit_w, blit_h);
				if(!owns_pipe)
					gallium_device->RestoreGalliumState();
			}

			if(blit_w != dst_w)
				pipe->clear_render_target(pipe, dst_surface, black, rect.left + blit_x + blit_w, rect.top, dst_w - blit_x - blit_w, dst_h);
			if(blit_h != dst_h)
				pipe->clear_render_target(pipe, dst_surface, black, rect.left, rect.top + blit_y + blit_h, dst_w, dst_h - blit_y - blit_h);
		}

		if(dst_surface)
			pipe->surface_destroy(pipe, dst_surface);

		pipe->flush(pipe, PIPE_FLUSH_RENDER_CACHE | PIPE_FLUSH_FRAME, 0);

		att = (db) ? NATIVE_ATTACHMENT_BACK_LEFT : NATIVE_ATTACHMENT_FRONT_LEFT;
		if(!surface->present(surface, att, FALSE, 0))
			return DXGI_ERROR_DEVICE_REMOVED;

end_present:
		parent->backend->EndPresent(desc.OutputWindow, present_cookie);

		++present_count;
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE GetBuffer(
			UINT Buffer,
			REFIID riid,
			void **ppSurface)
	{
		if(Buffer > 0)
		{
			if(desc.SwapEffect == DXGI_SWAP_EFFECT_SEQUENTIAL)
				std::cerr << "DXGI unimplemented: GetBuffer(n) with n > 0 not supported, returning buffer 0 instead!" << std::endl;
			else
				std::cerr << "DXGI error: in GetBuffer(n), n must be 0 for DXGI_SWAP_EFFECT_DISCARD\n" << std::endl;
		}

		if(!buffer0)
		{
			HRESULT hr = create_buffer0();
			if(!SUCCEEDED(hr))
				return hr;
		}
		return buffer0->QueryInterface(riid, ppSurface);
	}

	/* TODO: implement somehow */
	virtual HRESULT STDMETHODCALLTYPE SetFullscreenState(
		BOOL fullscreen,
		IDXGIOutput *target)
	{
		fullscreen = fullscreen;
		target = target;
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE GetFullscreenState(
		BOOL *out_fullscreen,
		IDXGIOutput **out_target)
	{
		if(out_fullscreen)
			*out_fullscreen = fullscreen;
		if(out_target)
			*out_target = target.ref();
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE GetDesc(
		DXGI_SWAP_CHAIN_DESC *out_desc)
	{
		*out_desc = desc;
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE ResizeBuffers(
		UINT buffer_count,
		UINT width,
		UINT height,
		DXGI_FORMAT new_format,
		UINT swap_chain_flags)
	{
		if(buffer0)
		{
			buffer0.p->AddRef();
			ULONG v = buffer0.p->Release();
			// we must fail if there are any references to buffer0 other than ours
			if(v > 1)
				return E_FAIL;
			pipe_sampler_view_reference(&gallium_buffer0_view, 0);
			buffer0 = (IUnknown*)NULL;
			gallium_buffer0 = 0;
		}

		if(desc.SwapEffect != DXGI_SWAP_EFFECT_SEQUENTIAL)
			desc.BufferCount = buffer_count;
		desc.BufferDesc.Format = new_format;
		desc.BufferDesc.Width = width;
		desc.BufferDesc.Height = height;
		desc.Flags = swap_chain_flags;
		return resolve_zero_width_height();
	}

	virtual HRESULT STDMETHODCALLTYPE ResizeTarget(
		const DXGI_MODE_DESC *out_new_target_parameters)
	{
		/* TODO: implement */
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE GetContainingOutput(
		IDXGIOutput **out_output)
	{
		*out_output = adapter->outputs[0].ref();
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE GetFrameStatistics(
		DXGI_FRAME_STATISTICS *out_stats)
	{
		memset(out_stats, 0, sizeof(*out_stats));
#ifdef _WIN32
		QueryPerformanceCounter(&out_stats->SyncQPCTime);
#endif
		out_stats->PresentCount = present_count;
		out_stats->PresentRefreshCount = present_count;
		out_stats->SyncRefreshCount = present_count;
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE GetLastPresentCount(
		UINT *last_present_count)
	{
		*last_present_count = present_count;
		return S_OK;
	}
};

static void GalliumDXGISwapChainRevalidate(IDXGISwapChain* swap_chain)
{
	((GalliumDXGISwapChain*)swap_chain)->needs_validation = true;
}

static HRESULT GalliumDXGIAdapterCreate(GalliumDXGIFactory* factory, const struct native_platform* platform, void* dpy, IDXGIAdapter1** out_adapter)
{
	try
	{
		*out_adapter = new GalliumDXGIAdapter(factory, platform, dpy);
		return S_OK;
	}
	catch(HRESULT hr)
	{
		return hr;
	}
}

static HRESULT GalliumDXGIOutputCreate(GalliumDXGIAdapter* adapter, const std::string& name, const struct native_connector* connector, IDXGIOutput** out_output)
{
	try
	{
		*out_output = new GalliumDXGIOutput(adapter, name, connector);
		return S_OK;
	}
	catch(HRESULT hr)
	{
		return hr;
	}
}

static HRESULT GalliumDXGISwapChainCreate(GalliumDXGIFactory* factory, IUnknown* device, const DXGI_SWAP_CHAIN_DESC& desc, IDXGISwapChain** out_swap_chain)
{
	try
	{
		*out_swap_chain = new GalliumDXGISwapChain(factory, device, desc);
		return S_OK;
	}
	catch(HRESULT hr)
	{
		return hr;
	}
}

struct dxgi_binding
{
	const struct native_platform* platform;
	void* display;
	IGalliumDXGIBackend* backend;
};

static dxgi_binding dxgi_default_binding;
static __thread dxgi_binding dxgi_thread_binding;

void STDMETHODCALLTYPE GalliumDXGIUseNothing()
{
	dxgi_thread_binding.platform = 0;
	dxgi_thread_binding.display = 0;
	if(dxgi_thread_binding.backend)
		dxgi_thread_binding.backend->Release();
	dxgi_thread_binding.backend = 0;
}

#ifdef GALLIUM_DXGI_USE_X11
void STDMETHODCALLTYPE GalliumDXGIUseX11Display(Display* dpy, IGalliumDXGIBackend* backend)
{
	GalliumDXGIUseNothing();
	dxgi_thread_binding.platform = native_get_x11_platform();
	dxgi_thread_binding.display = dpy;

	if(backend)
	{
		dxgi_thread_binding.backend = backend;
		backend->AddRef();
	}
}
#endif

/*
#ifdef GALLIUM_DXGI_USE_DRM
void STDMETHODCALLTYPE GalliumDXGIUseDRMCard(int fd)
{
	GalliumDXGIUseNothing();
	dxgi_thread_binding.platform = native_get_drm_platform();
	dxgi_thread_binding.display = (void*)fd;
	dxgi_thread_binding.backend = 0;
}
#endif

#ifdef GALLIUM_DXGI_USE_FBDEV
void STDMETHODCALLTYPE GalliumDXGIUseFBDev(int fd)
{
	GalliumDXGIUseNothing();
	dxgi_thread_binding.platform = native_get_fbdev_platform();
	dxgi_thread_binding.display = (void*)fd;
	dxgi_thread_binding.backend = 0;
}
#endif

#ifdef GALLIUM_DXGI_USE_GDI
void STDMETHODCALLTYPE GalliumDXGIUseHDC(HDC hdc, PFNHWNDRESOLVER resolver, void* resolver_cookie)
{
	GalliumDXGIUseNothing();
	dxgi_thread_binding.platform = native_get_gdi_platform();
	dxgi_thread_binding.display = (void*)hdc;
	dxgi_thread_binding.backend = 0;
}
#endif
*/
void STDMETHODCALLTYPE GalliumDXGIMakeDefault()
{
	if(dxgi_default_binding.backend)
		dxgi_default_binding.backend->Release();
	dxgi_default_binding = dxgi_thread_binding;
	if(dxgi_default_binding.backend)
		dxgi_default_binding.backend->AddRef();
}

 /* TODO: why did Microsoft add this? should we do something different for DXGI 1.0 and 1.1?
 * Or perhaps what they actually mean is "only create a single factory in your application"?
 * TODO: should we use a singleton here, so we never have multiple DXGI objects for the same thing? */
 HRESULT STDMETHODCALLTYPE CreateDXGIFactory1(
		REFIID riid,
		void **out_factory
)
 {
	 GalliumDXGIFactory* factory;
	 *out_factory = 0;
	 if(dxgi_thread_binding.platform)
		 factory = new GalliumDXGIFactory(dxgi_thread_binding.platform, dxgi_thread_binding.display, dxgi_thread_binding.backend);
	 else if(dxgi_default_binding.platform)
		 factory = new GalliumDXGIFactory(dxgi_default_binding.platform, dxgi_default_binding.display, dxgi_default_binding.backend);
	 else
		 factory = new GalliumDXGIFactory(native_get_x11_platform(), NULL, NULL);
	 HRESULT hres = factory->QueryInterface(riid, out_factory);
	 factory->Release();
	 return hres;
 }

 HRESULT STDMETHODCALLTYPE CreateDXGIFactory(
		 REFIID riid,
		 void **out_factor
)
 {
	 return CreateDXGIFactory1(riid, out_factor);
 }
