// This code was written for the OpenTK library and has been released
// to the Public Domain.
// It is provided "as is" without express or implied warranty of any kind.

using System;
using System.Runtime.InteropServices;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;

namespace Examples.Tests
{
    [Example("External Context Test", ExampleCategory.OpenTK, "OpenGL")]
    class ExternalContext
    {
        public static void Main()
        {
            using (Toolkit.Init(new ToolkitOptions { Backend = PlatformBackend.PreferNative }))
            {
                // Create a window and context using a third-party toolkit
                // (in this case SDL2)
                var window = SDL.CreateWindow("Test", 0, 0, 640, 480,
                    WindowFlags.AllowHighDpi | WindowFlags.OpenGL);
                var context = SDL.GL.CreateContext(window);

                // The external context must be made current,
                // in order to correctly initialize OpenTK.Graphics
                SDL.GL.MakeCurrent(window, context);

                // Now we need to initialize OpenTK.Graphics using
                // the external context. This can be achieved in
                // two ways:
                //
                // var dummy = new GraphicsContext(ContextHandle.Zero, null);
                // -- or --
                // var dummy = new GraphicsContext(
                //     new ContextHandle(context),
                //     (name) => SDL.GL.GetAddress(name),
                //     () => new ContextHandle(SDL.GL.GetCurrentContext()));
                //
                // The first approach works only on Windows, Mac and Linux/X11.
                // 
                // The second approach will work on all platforms supported
                // by the external toolkit. This means that you can use
                // OpenTK.Graphics everywhere, even on platforms not directly
                // supported by OpenTK.

                using (var dummy = new GraphicsContext(
                    new ContextHandle(context),
                    SDL.GL.GetAddress,
                    () => new ContextHandle(SDL.GL.GetCurrentContext())))
                {
                    for (int i = 0; i < 100; i++)
                    {
                        SDL.PumpEvents();
                        GL.ClearColor(i / 100.0f, i / 100.0f, i / 100.0f, i / 100.0f);
                        GL.Clear(ClearBufferMask.ColorBufferBit);

                        SDL.GL.SwapWindow(window);
                    }

                    SDL.DestroyWindow(window);
                }
            }
        }
    }

    #region SDL2 bindings

    public enum WindowFlags
    {
        Default = 0,
        OpenGL = 0x00000002,
        AllowHighDpi = 0x00002000,
    }

    static class SDL
    {
        const string lib = "SDL2.dll";

        [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_CreateWindow", ExactSpelling = true)]
        public static extern IntPtr CreateWindow(string title, int x, int y, int w, int h, WindowFlags flags);

        [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_DestroyWindow", ExactSpelling = true)]
        public static extern void DestroyWindow(IntPtr window);

        [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_PumpEvents", ExactSpelling = true)]
        public static extern void PumpEvents();

        public static class GL
        {
            [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_CreateContext", ExactSpelling = true)]
            public static extern IntPtr CreateContext(IntPtr window);

            [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetCurrentContext", ExactSpelling = true)]
            public static extern IntPtr GetCurrentContext();

            [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetProcAddress", ExactSpelling = true)]
            public static extern IntPtr GetAddress(string name);

            [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_MakeCurrent", ExactSpelling = true)]
            public static extern int MakeCurrent(IntPtr window, IntPtr context);

            [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_SwapWindow", ExactSpelling = true)]
            public static extern void SwapWindow(IntPtr window);
        }
    }

    #endregion

}