package instance

//#cgo LDFLAGS: -LF:/Coding/GoModule/cgo -lvulkan
import "C"

import (
	"github.com/vkngwrapper/core/v2"
	"github.com/vkngwrapper/core/v2/common"
	"github.com/vkngwrapper/core/v2/core1_0"
	"github.com/vkngwrapper/extensions/v2/khr_surface"
	"github.com/vkngwrapper/extensions/v2/khr_swapchain"
	"zworld/engine/window"
)

type T interface {
	EnumeratePhysicalDevices() []core1_0.PhysicalDevice
	Destroy()
	Init() error
	DrawFrame() error
	Ptr() core1_0.Instance
	WaitIdle() (common.VkResult, error)
}
type FLoaderCreate func() *core.VulkanLoader
type FInstance struct {
	loader  *core.VulkanLoader
	win     *window.FSDLWindow
	ptr     core1_0.Instance
	surface khr_surface.Surface

	physicalDevice core1_0.PhysicalDevice
	device         core1_0.Device

	graphicsQueue core1_0.Queue
	presentQueue  core1_0.Queue

	swapchainExtension    khr_swapchain.Extension
	swapchain             khr_swapchain.Swapchain
	swapchainImages       []core1_0.Image
	swapchainImageFormat  core1_0.Format
	swapchainExtent       core1_0.Extent2D
	swapchainImageViews   []core1_0.ImageView
	swapchainFramebuffers []core1_0.Framebuffer

	renderPass       core1_0.RenderPass
	pipelineLayout   core1_0.PipelineLayout
	graphicsPipeline core1_0.Pipeline

	commandPool    core1_0.CommandPool
	commandBuffers []core1_0.CommandBuffer

	imageAvailableSemaphore []core1_0.Semaphore
	renderFinishedSemaphore []core1_0.Semaphore
	inFlightFence           []core1_0.Fence
	imagesInFlight          []core1_0.Fence
	currentFrame            int
}

func New(appName string, win *window.FSDLWindow, LoaderCreate FLoaderCreate) T {
	loader := LoaderCreate()
	instanceOptions := &core1_0.InstanceCreateInfo{
		APIVersion:         common.APIVersion(common.CreateVersion(1, 1, 0)),
		ApplicationName:    appName,
		ApplicationVersion: common.CreateVersion(0, 1, 0),
		EngineName:         engineName,
		EngineVersion:      common.CreateVersion(0, 2, 1),
	}
	err := help.getCreateInstanceExtension(instanceOptions, loader, win)
	if err != nil {
		return nil
	}
	err = help.getCreateInstanceLayer(instanceOptions, loader, win)
	if err != nil {
		return nil
	}
	handle, _, err := loader.CreateInstance(nil, *instanceOptions)
	if err != nil {
		panic(err)
	}
	return &FInstance{
		ptr:    handle,
		loader: loader,
		win:    win,
	}
}

func (e *FInstance) Ptr() core1_0.Instance {
	return e.ptr
}

func (e *FInstance) Destroy() {
	e.ptr.Destroy(nil)
	e.ptr = nil
}

func (e *FInstance) EnumeratePhysicalDevices() []core1_0.PhysicalDevice {
	r, _, err := e.ptr.EnumeratePhysicalDevices()
	if err != nil {
		panic(err)
	}
	return r
}
func (e *FInstance) WaitIdle() (common.VkResult, error) {
	return e.device.WaitIdle()
}
func (e *FInstance) DrawFrame() error {
	fences := []core1_0.Fence{e.inFlightFence[e.currentFrame]}

	_, err := e.device.WaitForFences(true, common.NoTimeout, fences)
	if err != nil {
		return err
	}

	imageIndex, _, err := e.swapchain.AcquireNextImage(common.NoTimeout, e.imageAvailableSemaphore[e.currentFrame], nil)
	if err != nil {
		return err
	}

	if e.imagesInFlight[imageIndex] != nil {
		_, err := e.device.WaitForFences(true, common.NoTimeout, []core1_0.Fence{e.imagesInFlight[imageIndex]})
		if err != nil {
			return err
		}
	}
	e.imagesInFlight[imageIndex] = e.inFlightFence[e.currentFrame]

	_, err = e.device.ResetFences(fences)
	if err != nil {
		return err
	}

	_, err = e.graphicsQueue.Submit(e.inFlightFence[e.currentFrame], []core1_0.SubmitInfo{
		{
			WaitSemaphores:   []core1_0.Semaphore{e.imageAvailableSemaphore[e.currentFrame]},
			WaitDstStageMask: []core1_0.PipelineStageFlags{core1_0.PipelineStageColorAttachmentOutput},
			CommandBuffers:   []core1_0.CommandBuffer{e.commandBuffers[imageIndex]},
			SignalSemaphores: []core1_0.Semaphore{e.renderFinishedSemaphore[e.currentFrame]},
		},
	})
	if err != nil {
		return err
	}

	_, err = e.swapchainExtension.QueuePresent(e.presentQueue, khr_swapchain.PresentInfo{
		WaitSemaphores: []core1_0.Semaphore{e.renderFinishedSemaphore[e.currentFrame]},
		Swapchains:     []khr_swapchain.Swapchain{e.swapchain},
		ImageIndices:   []int{imageIndex},
	})
	if err != nil {
		return err
	}

	e.currentFrame = (e.currentFrame + 1) % MaxFramesInFlight

	return nil
}
