/* FNA MultiWindow Example
 * Written by Ethan "flibitijibibo" Lee
 * https://www.flibitijibibo.com/
 *
 * Released under public domain.
 * No warranty implied; use at your own risk.
 */

using System;
using SDL2;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

class Program : Game
{
	static void Main(string[] args)
	{
		using (Program p = new Program())
		{
			p.Run();
		}
	}

	// XNA stuff
	SpriteBatch batch;
	Texture2D tex;

	// SDL_Window* stuff
	IntPtr extraWindow;
	uint extraWindowID;

	// Event handling
	SDL.SDL_EventFilter eventFilter;
	SDL.SDL_EventFilter prevEventFilter;

	Program()
	{
		/* Believe it or not you don't really need this in multiwindow,
		 * it's just so we can set the main window size
		 */
		GraphicsDeviceManager gdm = new GraphicsDeviceManager(this);
		gdm.PreferredBackBufferWidth = 1280;
		gdm.PreferredBackBufferHeight = 600;

		// Use a filter to get SDL events for your extra window
		IntPtr prevUserData;
		SDL.SDL_GetEventFilter(
			out prevEventFilter,
			out prevUserData
		);
		eventFilter = EventFilter;
		SDL.SDL_SetEventFilter(
			eventFilter,
			prevUserData
		);
	}

	private unsafe int EventFilter(IntPtr userdata, IntPtr evtPtr)
	{
		SDL.SDL_Event* evt = (SDL.SDL_Event*) evtPtr;
		if (evt->type == SDL.SDL_EventType.SDL_WINDOWEVENT)
		{
			if (evt->window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE)
			{
				// Lazy hack, just exit when any window is closed
				Exit();
				return 0;
			}
			else if (evt->window.windowID == extraWindowID)
			{
				// Filter these out so Game doesn't get weird
				return 0;
			}
		}
		if (prevEventFilter != null)
		{
			return prevEventFilter(userdata, evtPtr);
		}
		return 1;
	}

	protected override void LoadContent()
	{
		// SDL_WINDOW_VULKAN just loads libvulkan, so we can always set it
		extraWindow = SDL.SDL_CreateWindow(
			"Extra Window",
			SDL.SDL_WINDOWPOS_CENTERED,
			SDL.SDL_WINDOWPOS_CENTERED,
			800,
			720,
			(
				SDL.SDL_WindowFlags.SDL_WINDOW_VULKAN |
				SDL.SDL_WindowFlags.SDL_WINDOW_SHOWN
				// Set borderless if shoving into a Form!
			)
		);
		extraWindowID = SDL.SDL_GetWindowID(extraWindow);

		/* Use info.win.win.window if shoving into a Form!
		SDL.SDL_SysWMInfo info;
		SDL.SDL_GetWindowWMInfo(extraWindow, out info);
		*/

		// Just throw any image next to the exe...
		tex = Content.Load<Texture2D>("tex");
		batch = new SpriteBatch(GraphicsDevice);
	}

	protected override void UnloadContent()
	{
		if (batch != null)
		{
			batch.Dispose();
			batch = null;
		}
		if (tex != null)
		{
			tex.Dispose();
			tex = null;
		}
		if (extraWindow != IntPtr.Zero)
		{
			SDL.SDL_DestroyWindow(extraWindow);
		}
	}

	private void ValidateBackBuffer()
	{
		// Get the largest width/height of all windows, use as backbuffer size
		Rectangle bounds = Window.ClientBounds;
		int wx, wy;
		SDL.SDL_GetWindowSize(extraWindow, out wx, out wy);
		int maxW = Math.Max(bounds.Width, wx);
		int maxH = Math.Max(bounds.Height, wy);

		/* Note two details:
		 *
		 * 1. Do NOT call ApplyChanges, that triggers a window resize!
		 * 2. Do NOT pass a window handle here, it may cause a swapchain resize!
		 */
		PresentationParameters pp = GraphicsDevice.PresentationParameters;
		if (pp.BackBufferWidth != maxW || pp.BackBufferHeight != maxH)
		{
			pp.BackBufferWidth = maxW;
			pp.BackBufferHeight = maxH;
			pp.DeviceWindowHandle = IntPtr.Zero;
			GraphicsDevice.Reset(pp);
			Console.WriteLine("GraphicsDevice reset!");
		}
	}

	protected override void Draw(GameTime gameTime)
	{
		ValidateBackBuffer();

		// For each window: Get the size, set the viewport, draw, then present
		int wx, wy;
		SDL.SDL_GetWindowSize(extraWindow, out wx, out wy);
		GraphicsDevice.Clear(Color.Red);
		GraphicsDevice.Viewport = new Viewport(0, 0, wx, wy);
		batch.Begin();
		batch.Draw(tex, Vector2.Zero, Color.White);
		batch.End();
		GraphicsDevice.Present(
			new Rectangle(0, 0, wx, wy),
			null,
			extraWindow
		);

		// Promise you won't use ClientBounds for anything but this...
		Rectangle bounds = Window.ClientBounds;
		GraphicsDevice.Clear(Color.Blue);
		GraphicsDevice.Viewport = new Viewport(0, 0, bounds.Width, bounds.Height);
		batch.Begin();
		batch.Draw(tex, Vector2.Zero, Color.White);
		batch.End();
	}

	// Override this so that we can present with subrectangle
	protected override void EndDraw()
	{
		Rectangle bounds = Window.ClientBounds;
		GraphicsDevice.Present(
			new Rectangle(0, 0, bounds.Width, bounds.Height),
			null,
			Window.Handle
		);
	}
}