#include "common.h"
#include "modeset.h"

#include "kernel/vc4_packet.h"

#include "declarations.h"

#ifdef __cplusplus
extern "C" {
#endif

VKAPI_ATTR VkResult VKAPI_CALL RPIFUNC(vkGetPhysicalDeviceDisplayPropertiesKHR)(
	VkPhysicalDevice                            physicalDevice,
	uint32_t*                                   pPropertyCount,
	VkDisplayPropertiesKHR*                     pProperties)
{
	PROFILESTART(RPIFUNC(vkGetPhysicalDeviceDisplayPropertiesKHR));

	assert(physicalDevice);
	assert(pPropertyCount);

	uint32_t numDisplays;
	modeset_display displays[16];
	modeset_enum_displays(controlFd, &numDisplays, displays);

	if(!pProperties)
	{
		*pPropertyCount = numDisplays;

		PROFILEEND(RPIFUNC(vkGetPhysicalDeviceDisplayPropertiesKHR));
		return VK_SUCCESS;
	}

	uint32_t arraySize = *pPropertyCount;
	uint32_t elementsWritten = min(numDisplays, arraySize);

	for(uint32_t c = 0; c < elementsWritten; ++c)
	{
		pProperties[c].display = displays[c].connectorID;
		//fprintf(stderr, "display id %i\n", pProperties[c].display );
		char* name = (char*)malloc(32);
		memcpy(name, displays[c].name, 32);
		pProperties[c].displayName = (const char*)name;
		pProperties[c].physicalDimensions.width = displays[c].mmWidth;
		pProperties[c].physicalDimensions.height = displays[c].mmHeight;
		pProperties[c].physicalResolution.width = displays[c].resWidth;
		pProperties[c].physicalResolution.height = displays[c].resHeight;
	}

	*pPropertyCount = elementsWritten;

	if(arraySize < numDisplays)
	{
		PROFILEEND(RPIFUNC(vkGetPhysicalDeviceDisplayPropertiesKHR));
		return VK_INCOMPLETE;
	}

	PROFILEEND(RPIFUNC(vkGetPhysicalDeviceDisplayPropertiesKHR));
	return VK_SUCCESS;
}

VKAPI_ATTR VkResult VKAPI_CALL RPIFUNC(vkGetDisplayModePropertiesKHR)(
	VkPhysicalDevice                            physicalDevice,
	VkDisplayKHR                                display,
	uint32_t*                                   pPropertyCount,
	VkDisplayModePropertiesKHR*                 pProperties)
{
	PROFILESTART(RPIFUNC(vkGetDisplayModePropertiesKHR));

	assert(physicalDevice);
	assert(display);
	assert(pPropertyCount);

	uint32_t numModes;
	modeset_display_mode modes[1024];
	modeset_enum_modes_for_display(controlFd, display, &numModes, &modes);

	if(!pProperties)
	{
		*pPropertyCount = numModes;

		PROFILEEND(RPIFUNC(vkGetDisplayModePropertiesKHR));
		return VK_SUCCESS;
	}

	uint32_t arraySize = *pPropertyCount;
	uint32_t elementsWritten = min(numModes, arraySize);

	for(uint32_t c = 0; c < elementsWritten; ++c)
	{
		_displayMode mode = { modes[c].connectorID, modes[c].modeID };
		memcpy(&pProperties[c].displayMode, &mode, sizeof(_displayMode));
		pProperties[c].parameters.visibleRegion.width = modes[c].resWidth;
		pProperties[c].parameters.visibleRegion.height = modes[c].resHeight;
		pProperties[c].parameters.refreshRate = modes[c].refreshRate;
	}

	*pPropertyCount = elementsWritten;

	if(arraySize < numModes)
	{
		PROFILEEND(RPIFUNC(vkGetDisplayModePropertiesKHR));
		return VK_INCOMPLETE;
	}

	PROFILEEND(RPIFUNC(vkGetDisplayModePropertiesKHR));
	return VK_SUCCESS;
}

VKAPI_ATTR VkResult VKAPI_CALL RPIFUNC(vkCreateDisplayPlaneSurfaceKHR)(
	VkInstance                                  instance,
	const VkDisplaySurfaceCreateInfoKHR*        pCreateInfo,
	const VkAllocationCallbacks*                pAllocator,
	VkSurfaceKHR*                               pSurface)
{
	PROFILESTART(RPIFUNC(vkCreateDisplayPlaneSurfaceKHR));

	assert(instance);
	assert(pSurface);

	_displayMode mode;
	memcpy(&mode, &pCreateInfo->displayMode, sizeof(_displayMode));

	modeset_display_surface* surface = ALLOCATE(sizeof(modeset_display_surface), 1, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
	modeset_create_surface_for_mode(controlFd, mode.connectorID, mode.modeID, surface);

	*pSurface = surface;

	PROFILEEND(RPIFUNC(vkCreateDisplayPlaneSurfaceKHR));

	return VK_SUCCESS;
}

/*
 * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkDestroySurfaceKHR
 * Destroying a VkSurfaceKHR merely severs the connection between Vulkan and the native surface,
 * and does not imply destroying the native surface, closing a window, or similar behavior
 * (but we'll do so anyways...)
 */
VKAPI_ATTR void VKAPI_CALL RPIFUNC(vkDestroySurfaceKHR)(
		VkInstance                                  instance,
		VkSurfaceKHR                                surface,
		const VkAllocationCallbacks*                pAllocator)
{
	PROFILESTART(RPIFUNC(vkDestroySurfaceKHR));

	assert(instance);

	if(surface)
	{
		modeset_destroy_surface(controlFd, surface);
	}

	FREE(surface);

	PROFILEEND(RPIFUNC(vkDestroySurfaceKHR));
}

/*
 * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkGetPhysicalDeviceSurfaceCapabilitiesKHR
 * The capabilities of a swapchain targetting a surface are the intersection of the capabilities of the WSI platform,
 * the native window or display, and the physical device. The resulting capabilities can be obtained with the queries listed
 * below in this section. Capabilities that correspond to image creation parameters are not independent of each other:
 * combinations of parameters that are not supported as reported by vkGetPhysicalDeviceImageFormatProperties are not supported
 * by the surface on that physical device, even if the capabilities taken individually are supported as part of some other parameter combinations.
 *
 * capabilities the specified device supports for a swapchain created for the surface
 */
VKAPI_ATTR VkResult VKAPI_CALL RPIFUNC(vkGetPhysicalDeviceSurfaceCapabilitiesKHR)(
		VkPhysicalDevice                            physicalDevice,
		VkSurfaceKHR                                surface,
		VkSurfaceCapabilitiesKHR*                   pSurfaceCapabilities)
{
	PROFILESTART(RPIFUNC(vkGetPhysicalDeviceSurfaceCapabilitiesKHR));

	assert(physicalDevice);
	assert(surface);
	assert(pSurfaceCapabilities);

	modeset_display_surface* surf = surface;

	uint32_t width = surf->connector->modes[surf->modeID].hdisplay;
	uint32_t height = surf->connector->modes[surf->modeID].vdisplay;

	pSurfaceCapabilities->minImageCount = 1;
	pSurfaceCapabilities->maxImageCount = 2; //TODO max 2 for double buffering for now...
	pSurfaceCapabilities->currentExtent.width = width;
	pSurfaceCapabilities->currentExtent.height = height;
	pSurfaceCapabilities->minImageExtent.width = width;
	pSurfaceCapabilities->minImageExtent.height = height;
	pSurfaceCapabilities->maxImageExtent.width = width;
	pSurfaceCapabilities->maxImageExtent.height = height;
	pSurfaceCapabilities->maxImageArrayLayers = 1;
	pSurfaceCapabilities->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
	pSurfaceCapabilities->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
	pSurfaceCapabilities->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
	pSurfaceCapabilities->supportedUsageFlags =
			VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | //well we want to draw on the screen right
			VK_IMAGE_USAGE_TRANSFER_DST_BIT |  //for clears
			VK_IMAGE_USAGE_TRANSFER_SRC_BIT;  //for screenshots

	PROFILEEND(RPIFUNC(vkGetPhysicalDeviceSurfaceCapabilitiesKHR));
	return VK_SUCCESS;
}

/*
 * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkGetPhysicalDeviceSurfaceFormatsKHR
 * If pSurfaceFormats is NULL, then the number of format pairs supported for the given surface is returned in pSurfaceFormatCount.
 * The number of format pairs supported will be greater than or equal to 1. Otherwise, pSurfaceFormatCount must point to a variable
 * set by the user to the number of elements in the pSurfaceFormats array, and on return the variable is overwritten with the number
 * of structures actually written to pSurfaceFormats. If the value of pSurfaceFormatCount is less than the number of format pairs supported,
 * at most pSurfaceFormatCount structures will be written. If pSurfaceFormatCount is smaller than the number of format pairs supported for the given surface,
 * VK_INCOMPLETE will be returned instead of VK_SUCCESS to indicate that not all the available values were returned.
 */
VKAPI_ATTR VkResult VKAPI_CALL RPIFUNC(vkGetPhysicalDeviceSurfaceFormatsKHR)(
		VkPhysicalDevice                            physicalDevice,
		VkSurfaceKHR                                surface,
		uint32_t*                                   pSurfaceFormatCount,
		VkSurfaceFormatKHR*                         pSurfaceFormats)
{
	PROFILESTART(RPIFUNC(vkGetPhysicalDeviceSurfaceFormatsKHR));

	assert(physicalDevice);
	assert(surface);
	assert(pSurfaceFormatCount);

	const int numFormats = 1;

	if(!pSurfaceFormats)
	{
		*pSurfaceFormatCount = numFormats;

		PROFILEEND(RPIFUNC(vkGetPhysicalDeviceSurfaceFormatsKHR));
		return VK_SUCCESS;
	}

	int arraySize = *pSurfaceFormatCount;
	int elementsWritten = min(numFormats, arraySize);

	for(int c = 0; c < elementsWritten; ++c)
	{
		pSurfaceFormats[c] = supportedSurfaceFormats[c];
	}

	*pSurfaceFormatCount = elementsWritten;

	if(elementsWritten < numFormats)
	{
		PROFILEEND(RPIFUNC(vkGetPhysicalDeviceSurfaceFormatsKHR));
		return VK_INCOMPLETE;
	}

	PROFILEEND(RPIFUNC(vkGetPhysicalDeviceSurfaceFormatsKHR));
	return VK_SUCCESS;
}

/*
 * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkGetPhysicalDeviceSurfacePresentModesKHR
 * If pPresentModes is NULL, then the number of presentation modes supported for the given surface is returned in pPresentModeCount.
 * Otherwise, pPresentModeCount must point to a variable set by the user to the number of elements in the pPresentModes array,
 * and on return the variable is overwritten with the number of values actually written to pPresentModes.
 * If the value of pPresentModeCount is less than the number of presentation modes supported, at most pPresentModeCount values will be written.
 * If pPresentModeCount is smaller than the number of presentation modes supported for the given surface, VK_INCOMPLETE will be returned instead of
 * VK_SUCCESS to indicate that not all the available values were returned.
 */
VKAPI_ATTR VkResult VKAPI_CALL RPIFUNC(vkGetPhysicalDeviceSurfacePresentModesKHR)(
		VkPhysicalDevice                            physicalDevice,
		VkSurfaceKHR                                surface,
		uint32_t*                                   pPresentModeCount,
		VkPresentModeKHR*                           pPresentModes)
{
	PROFILESTART(RPIFUNC(vkGetPhysicalDeviceSurfacePresentModesKHR));

	assert(physicalDevice);
	assert(surface);
	assert(pPresentModeCount);

	if(!pPresentModes)
	{
		*pPresentModeCount = numSupportedPresentModes;

		PROFILEEND(RPIFUNC(vkGetPhysicalDeviceSurfacePresentModesKHR));
		return VK_SUCCESS;
	}

	uint32_t arraySize = *pPresentModeCount;
	uint32_t elementsWritten = min(numSupportedPresentModes, arraySize);

	for(uint32_t c = 0; c < elementsWritten; ++c)
	{
		pPresentModes[c] = supportedPresentModes[c];
	}

	*pPresentModeCount = elementsWritten;

	if(elementsWritten < numSupportedPresentModes)
	{
		PROFILEEND(RPIFUNC(vkGetPhysicalDeviceSurfacePresentModesKHR));
		return VK_INCOMPLETE;
	}

	PROFILEEND(RPIFUNC(vkGetPhysicalDeviceSurfacePresentModesKHR));
	return VK_SUCCESS;
}

/*
 * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkCreateSwapchainKHR
 */
VKAPI_ATTR VkResult VKAPI_CALL RPIFUNC(vkCreateSwapchainKHR)(
		VkDevice                                    device,
		const VkSwapchainCreateInfoKHR*             pCreateInfo,
		const VkAllocationCallbacks*                pAllocator,
		VkSwapchainKHR*                             pSwapchain)
{
	PROFILESTART(RPIFUNC(vkCreateSwapchainKHR));

	assert(device);
	assert(pCreateInfo);
	assert(pSwapchain);

	*pSwapchain = ALLOCATE(sizeof(_swapchain), 1, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
	if(!*pSwapchain)
	{
		PROFILEEND(RPIFUNC(vkCreateSwapchainKHR));
		return VK_ERROR_OUT_OF_HOST_MEMORY;
	}

	_swapchain* s = *pSwapchain;

	//TODO flags, layers, queue sharing, pretransform, composite alpha..., clipped, oldswapchain

	s->images = ALLOCATE(sizeof(_image) * pCreateInfo->minImageCount, 1, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
	if(!s->images)
	{
		PROFILEEND(RPIFUNC(vkCreateSwapchainKHR));
		return VK_ERROR_OUT_OF_HOST_MEMORY;
	}

	s->inFlight = ALLOCATE(sizeof(uint32_t) * pCreateInfo->minImageCount, 1, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
	if(!s->inFlight)
	{
		PROFILEEND(RPIFUNC(vkCreateSwapchainKHR));
		return VK_ERROR_OUT_OF_HOST_MEMORY;
	}

	s->backbufferIdx = 0;
	s->numImages = pCreateInfo->minImageCount;
	s->surface = pCreateInfo->surface;

	for(uint32_t c = 0; c < pCreateInfo->minImageCount; ++c)
	{
		s->inFlight[c] = 0;

		VkImageCreateInfo imageCreateInfo = {0};
		imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
		imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
		imageCreateInfo.format = pCreateInfo->imageFormat;
		imageCreateInfo.mipLevels = 1;
		imageCreateInfo.arrayLayers = pCreateInfo->imageArrayLayers;
		imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
		imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
		imageCreateInfo.usage = pCreateInfo->imageUsage;
		imageCreateInfo.sharingMode = pCreateInfo->imageSharingMode;
		imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
		imageCreateInfo.queueFamilyIndexCount = pCreateInfo->queueFamilyIndexCount;
		imageCreateInfo.pQueueFamilyIndices = pCreateInfo->pQueueFamilyIndices;
		imageCreateInfo.extent.width = pCreateInfo->imageExtent.width;
		imageCreateInfo.extent.height = pCreateInfo->imageExtent.height;
		imageCreateInfo.extent.depth = 1;

		VkImage img;
		RPIFUNC(vkCreateImage)(device, &imageCreateInfo, pAllocator, &img);

		s->images[c] = *(_image*)img;

		s->images[c].imageSpace = pCreateInfo->imageColorSpace;
		s->images[c].preTransformMode = pCreateInfo->preTransform;
		s->images[c].compositeAlpha = pCreateInfo->compositeAlpha;
		s->images[c].presentMode = pCreateInfo->presentMode;
		s->images[c].clipped = pCreateInfo->clipped;

		VkMemoryRequirements mr;
		RPIFUNC(vkGetImageMemoryRequirements)(device, &s->images[c], &mr);

		s->images[c].alignment = mr.alignment;

		VkMemoryAllocateInfo ai;
		ai.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
		ai.allocationSize = mr.size;
		for(uint32_t d = 0; d < numMemoryTypes; ++d)
		{
			if(memoryTypes[d].propertyFlags == mr.memoryTypeBits)
			{
				ai.memoryTypeIndex = d;
				break;
			}
		}

		VkDeviceMemory mem;
		RPIFUNC(vkAllocateMemory)(device, &ai, pAllocator, &mem);

		RPIFUNC(vkBindImageMemory)(device, &s->images[c], mem, 0);

		modeset_create_fb_for_surface(controlFd, &s->images[c], pCreateInfo->surface); assert(s->images[c].fb);
	}

	PROFILEEND(RPIFUNC(vkCreateSwapchainKHR));
	return VK_SUCCESS;
}

/*
 * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkGetSwapchainImagesKHR
 * If pSwapchainImages is NULL, then the number of presentable images for swapchain is returned in pSwapchainImageCount.
 * Otherwise, pSwapchainImageCount must point to a variable set by the user to the number of elements in the pSwapchainImages array,
 * and on return the variable is overwritten with the number of structures actually written to pSwapchainImages.
 * If the value of pSwapchainImageCount is less than the number of presentable images for swapchain, at most pSwapchainImageCount structures will be written.
 * If pSwapchainImageCount is smaller than the number of presentable images for swapchain, VK_INCOMPLETE will be returned instead of VK_SUCCESS to
 * indicate that not all the available values were returned.
 */
VKAPI_ATTR VkResult VKAPI_CALL RPIFUNC(vkGetSwapchainImagesKHR)(
		VkDevice                                    device,
		VkSwapchainKHR                              swapchain,
		uint32_t*                                   pSwapchainImageCount,
		VkImage*                                    pSwapchainImages)
{
	PROFILESTART(RPIFUNC(vkGetSwapchainImagesKHR));

	assert(device);
	assert(swapchain);
	assert(pSwapchainImageCount);

	_swapchain* s = swapchain;

	if(!pSwapchainImages)
	{
		*pSwapchainImageCount = s->numImages;

		PROFILEEND(RPIFUNC(vkGetSwapchainImagesKHR));
		return VK_SUCCESS;
	}

	uint32_t arraySize = *pSwapchainImageCount;
	uint32_t elementsWritten = min(s->numImages, arraySize);

	for(uint32_t c = 0; c < elementsWritten; ++c)
	{
		pSwapchainImages[c] = &s->images[c];
	}

	*pSwapchainImageCount = elementsWritten;

	if(elementsWritten < s->numImages)
	{
		PROFILEEND(RPIFUNC(vkGetSwapchainImagesKHR));
		return VK_INCOMPLETE;
	}

	PROFILEEND(RPIFUNC(vkGetSwapchainImagesKHR));
	return VK_SUCCESS;
}

/*
 * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkAcquireNextImageKHR
 */
VKAPI_ATTR VkResult VKAPI_CALL RPIFUNC(vkAcquireNextImageKHR)(
		VkDevice                                    device,
		VkSwapchainKHR                              swapchain,
		uint64_t                                    timeout,
		VkSemaphore                                 semaphore,
		VkFence                                     fence,
		uint32_t*                                   pImageIndex)
{
	PROFILESTART(RPIFUNC(vkAcquireNextImageKHR));

	assert(device);
	assert(swapchain);

	assert(semaphore != VK_NULL_HANDLE || fence != VK_NULL_HANDLE);

	sem_t* sem = semaphore;
	_swapchain* sc = swapchain;

	//TODO we need to keep track of currently acquired images?

	//TODO wait timeout?

	_image* i;
	modeset_display_surface* surf;
	modeset_acquire_image(controlFd, &i, &surf);

	if(i && surf)
	{
		for(uint32_t c = 0; c < sc->numImages; ++c)
		{
			if(&sc->images[c] == i && sc->surface == surf)
			{
				sc->inFlight[c] = 0;
				break;
			}
		}
	}

	uint32_t candidateIdx = (sc->backbufferIdx + 1) % sc->numImages;
	for(uint32_t c = 0; c < sc->numImages; ++c)
	{
		if(!sc->inFlight[candidateIdx])
		{
			sc->backbufferIdx = candidateIdx;
			break;
		}

		candidateIdx = (candidateIdx + 1) % sc->numImages;
	}

	*pImageIndex = sc->backbufferIdx; //return back buffer index

	//signal semaphore
	int semVal; sem_getvalue(sem, &semVal); assert(semVal <= 0); //make sure semaphore is unsignalled
	sem_post(sem);

	_fence* f = fence;
	if(f)
	{
		f->signaled = 1;
	}

	PROFILEEND(RPIFUNC(vkAcquireNextImageKHR));
	return VK_SUCCESS;
}

/*
 * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkQueuePresentKHR
 * Any writes to memory backing the images referenced by the pImageIndices and pSwapchains members of pPresentInfo,
 * that are available before vkQueuePresentKHR is executed, are automatically made visible to the read access performed by the presentation engine.
 * This automatic visibility operation for an image happens-after the semaphore signal operation, and happens-before the presentation engine accesses the image.
 * Queueing an image for presentation defines a set of queue operations, including waiting on the semaphores and submitting a presentation request to the presentation engine.
 * However, the scope of this set of queue operations does not include the actual processing of the image by the presentation engine.
 * If vkQueuePresentKHR fails to enqueue the corresponding set of queue operations, it may return VK_ERROR_OUT_OF_HOST_MEMORY or VK_ERROR_OUT_OF_DEVICE_MEMORY.
 * If it does, the implementation must ensure that the state and contents of any resources or synchronization primitives referenced is unaffected by the call or its failure.
 * If vkQueuePresentKHR fails in such a way that the implementation is unable to make that guarantee, the implementation must return VK_ERROR_DEVICE_LOST.
 * However, if the presentation request is rejected by the presentation engine with an error VK_ERROR_OUT_OF_DATE_KHR or VK_ERROR_SURFACE_LOST_KHR,
 * the set of queue operations are still considered to be enqueued and thus any semaphore to be waited on gets unsignaled when the corresponding queue operation is complete.
 */
VKAPI_ATTR VkResult VKAPI_CALL RPIFUNC(vkQueuePresentKHR)(
		VkQueue                                     queue,
		const VkPresentInfoKHR*                     pPresentInfo)
{
	PROFILESTART(RPIFUNC(vkQueuePresentKHR));

	static unsigned frameProfile = 0;
	if(frameProfile)
	{
		PROFILEEND(&frameProfile);
	}

	assert(queue);
	assert(pPresentInfo);

	//wait for semaphore in present info set by submit ioctl to make sure cls are flushed
	for(uint32_t c = 0; c < pPresentInfo->waitSemaphoreCount; ++c)
	{
		sem_wait((sem_t*)pPresentInfo->pWaitSemaphores[c]);
	}

	for(uint32_t c = 0; c < pPresentInfo->swapchainCount; ++c)
	{
		_swapchain* s = pPresentInfo->pSwapchains[c];
		modeset_present(controlFd, &s->images[pPresentInfo->pImageIndices[c]], s->surface, queue->lastEmitSeqno);
		s->inFlight[pPresentInfo->pImageIndices[c]] = 1;
	}

	PROFILEEND(RPIFUNC(vkQueuePresentKHR));

	PROFILESTART(&frameProfile);
	frameProfile = 1;

	endFrame();

	return VK_SUCCESS;
}

/*
 * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkDestroySwapchainKHR
 */
VKAPI_ATTR void VKAPI_CALL RPIFUNC(vkDestroySwapchainKHR)(
		VkDevice                                    device,
		VkSwapchainKHR                              swapchain,
		const VkAllocationCallbacks*                pAllocator)
{
	PROFILESTART(RPIFUNC(vkDestroySwapchainKHR));

	assert(device);

	//TODO flush all ops

	_swapchain* s = swapchain;

	if(s)
	{
		for(uint32_t c = 0; c < s->numImages; ++c)
		{
			RPIFUNC(vkFreeMemory)(device, s->images[c].boundMem, 0);
			modeset_destroy_fb(controlFd, &s->images[c]);
		}

		FREE(s->images);
	}

	FREE(s);

	PROFILEEND(RPIFUNC(vkDestroySwapchainKHR));

	profilePrintResults();
}

/*
 * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkGetPhysicalDeviceSurfaceSupportKHR
 * does this queue family support presentation to this surface?
 */
VKAPI_ATTR VkResult VKAPI_CALL RPIFUNC(vkGetPhysicalDeviceSurfaceSupportKHR)(
		VkPhysicalDevice                            physicalDevice,
		uint32_t                                    queueFamilyIndex,
		VkSurfaceKHR                                surface,
		VkBool32*                                   pSupported)
{
	PROFILESTART(RPIFUNC(vkGetPhysicalDeviceSurfaceSupportKHR));

	assert(pSupported);
	assert(surface);
	assert(physicalDevice);

	assert(queueFamilyIndex < numQueueFamilies);

	//TODO if we plan to support headless rendering, there should be 2 families
	//one using /dev/dri/card0 which has modesetting
	//other using /dev/dri/renderD128 which does not support modesetting, this would say false here
	*pSupported = VK_TRUE;

	PROFILEEND(RPIFUNC(vkGetPhysicalDeviceSurfaceSupportKHR));
	return VK_SUCCESS;
}

#ifdef __cplusplus
}
#endif
