using MoonWorks;
using MoonWorks.Audio;
using MoonWorks.Graphics;
using MoonWorks.Input;
using System.IO;
using System.Threading.Tasks;

namespace MoonWorksTest
{
    public class TestGame : Game
    {
        ShaderModule passthroughVertexShaderModule;
        ShaderModule raymarchFragmentShaderModule;
        RaymarchUniforms raymarchUniforms;

        Texture woodTexture;
        Texture noiseTexture;
        Sampler sampler;

        Buffer vertexBuffer;

        Color clearColor;

        GraphicsPipeline mainGraphicsPipeline;

        byte[] screenshotPixels;
        Buffer screenshotBuffer;
        uint screenshotBufferSize;

        StaticSound music;
        StaticSoundInstance musicInstance;
        StreamingSoundOgg musicStream;

        bool screenshotInProgress = false;

        public TestGame(WindowCreateInfo windowCreateInfo, PresentMode presentMode, int targetTimestep = 60, bool debugMode = false) : base(windowCreateInfo, presentMode, targetTimestep, debugMode)
        {
            var windowWidth = windowCreateInfo.WindowWidth;
            var windowHeight = windowCreateInfo.WindowHeight;

            passthroughVertexShaderModule = new ShaderModule(GraphicsDevice, Path.Combine("Content", "passthrough_vert.spv"));
            raymarchFragmentShaderModule = new ShaderModule(GraphicsDevice, Path.Combine("Content", "hexagon_grid.spv"));

            raymarchUniforms.time = 0;
            raymarchUniforms.padding = 0;
            raymarchUniforms.resolutionX = windowWidth;
            raymarchUniforms.resolutionY = windowHeight;

            var uploadCommandBuffer = GraphicsDevice.AcquireCommandBuffer();

            woodTexture = Texture.LoadPNG(GraphicsDevice, uploadCommandBuffer, "Content/woodgrain.png");
            noiseTexture = Texture.LoadPNG(GraphicsDevice, uploadCommandBuffer, "Content/noise.png");

            sampler = new Sampler(GraphicsDevice, SamplerCreateInfo.LinearWrap);

            /* Load Vertex Data */

            var vertices = new PositionTexture[3];
            vertices[0].Position.X = -1;
            vertices[0].Position.Y = -1;
            vertices[0].Position.Z = 0;
            vertices[0].Texture.X = 0;
            vertices[0].Texture.Y = 1;

            vertices[1].Position.X = 3;
            vertices[1].Position.Y = -1;
            vertices[1].Position.Z = 0;
            vertices[1].Texture.X = 1;
            vertices[1].Texture.Y = 1;

            vertices[2].Position.X = -1;
            vertices[2].Position.Y = 3;
            vertices[2].Position.Z = 0;
            vertices[2].Texture.X = 0;
            vertices[2].Texture.Y = 0;

            vertexBuffer = Buffer.Create<PositionTexture>(GraphicsDevice, BufferUsageFlags.Vertex, 3);
            uploadCommandBuffer.SetBufferData(vertexBuffer, vertices);

            GraphicsDevice.Submit(uploadCommandBuffer);

            /* Pipeline */

            mainGraphicsPipeline = new GraphicsPipeline(
                GraphicsDevice,
                new GraphicsPipelineCreateInfo
                {
                    AttachmentInfo = new GraphicsPipelineAttachmentInfo(
                        new ColorAttachmentDescription(
                            GraphicsDevice.GetSwapchainFormat(Window),
                            ColorAttachmentBlendState.None
                        )
                    ),
                    DepthStencilState = DepthStencilState.Disable,
                    VertexShaderInfo = GraphicsShaderInfo.Create(passthroughVertexShaderModule, "main", 0),
                    VertexInputState = new VertexInputState(
                        VertexBinding.Create<PositionTexture>(),
                        VertexAttribute.Create<PositionTexture>("Position", 0),
                        VertexAttribute.Create<PositionTexture>("Texture", 1)
                    ),
                    PrimitiveType = PrimitiveType.TriangleList,
                    FragmentShaderInfo = GraphicsShaderInfo.Create<RaymarchUniforms>(raymarchFragmentShaderModule, "main", 2),
                    RasterizerState = RasterizerState.CW_CullBack,
                    ViewportState = new ViewportState((int)Window.Width, (int)Window.Height),
                    MultisampleState = MultisampleState.None
                }
            );

            screenshotBufferSize = windowWidth * windowHeight * 4;
            screenshotPixels = new byte[screenshotBufferSize];
            screenshotBuffer = new Buffer(GraphicsDevice, 0, screenshotBufferSize);

            music = StaticSound.LoadOgg(AudioDevice, Path.Combine("Content", "title_screen.ogg"));
            musicInstance = music.CreateInstance();
            // musicInstance.Play();

            musicStream = StreamingSoundOgg.Load(AudioDevice, Path.Combine("Content", "title_screen.ogg"), false, true);
            musicStream.Play();
        }

        protected override void Update(System.TimeSpan dt)
        {
            raymarchUniforms.time += (float)dt.TotalSeconds;
        }

        protected override void Draw(System.TimeSpan dt, double alpha)
        {
            var commandBuffer = GraphicsDevice.AcquireCommandBuffer();

            var swapchainTexture = commandBuffer.AcquireSwapchainTexture(Window);
            var takeScreenshot = Inputs.Keyboard.IsPressed(Keycode.S) && !screenshotInProgress && (swapchainTexture != null);

            if (swapchainTexture != null)
            {
                commandBuffer.BeginRenderPass(
                    new ColorAttachmentInfo(swapchainTexture, clearColor)
                );

                commandBuffer.BindGraphicsPipeline(mainGraphicsPipeline);

                commandBuffer.BindVertexBuffers(vertexBuffer);
                commandBuffer.BindFragmentSamplers(
                    new TextureSamplerBinding(woodTexture, sampler),
                    new TextureSamplerBinding(noiseTexture, sampler)
                );

                var fragmentParamOffset = commandBuffer.PushFragmentShaderUniforms(raymarchUniforms);
                commandBuffer.DrawPrimitives(0, 1, 0, fragmentParamOffset);
                commandBuffer.EndRenderPass();

                if (takeScreenshot)
                {
                    commandBuffer.CopyTextureToBuffer(new TextureSlice(swapchainTexture), screenshotBuffer);
                }
            }

            GraphicsDevice.Submit(commandBuffer);

            if (takeScreenshot)
            {
                Task.Run(() => SaveScreenshot());
            }
        }

        private void SaveScreenshot()
        {
            screenshotInProgress = true;

            var name = "MoonWorksTest-" + System.DateTime.Now.ToString("MM-dd-yyyy-hh-mm-ss") + ".png";
            System.Console.WriteLine("Saving screenshot " + name + " ...");

            GraphicsDevice.Wait();
            screenshotBuffer.GetData(screenshotPixels, screenshotBufferSize);

            Texture.SavePNG(
                name,
                1280,
                720,
                GraphicsDevice.GetSwapchainFormat(Window),
                screenshotPixels
            );

            System.Console.WriteLine("Screenshot saved!");

            screenshotInProgress = false;
        }

        protected override void OnDestroy()
        {

        }
    }
}
