using System.Collections.Immutable;
using System.IO;
using Encompass;
using KavTest.Components;
using KavTest.Engines;
using KavTest.Extensions;
using KavTest.Messages;
using KavTest.Renderers;
using KavTest.Spawners;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MoonTools.Curve;

namespace KavTest
{
    class KavTestGame : Game
    {
        GraphicsDeviceManager graphics;

        WorldBuilder WorldBuilder { get; } = new WorldBuilder();
        World World { get; set; }

        public KavTestGame()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = 1600;
            graphics.PreferredBackBufferHeight = 900;
            graphics.PreferMultiSampling = false;
            Content.RootDirectory = "Content";

            Window.AllowUserResizing = true;
            IsMouseVisible = true;

            Microsoft.Xna.Framework.Input.Mouse.IsRelativeMouseModeEXT = true;
        }

        protected override void LoadContent()
        {
            base.LoadContent();
        }

        protected override void UnloadContent()
        {
            base.UnloadContent();
        }

        protected override void Initialize()
        {
            base.Initialize();

            var rustyBallModel = Kav.ModelLoader.Load(
                GraphicsDevice,
                Smuggler.Importer.ImportGLB(
                    GraphicsDevice,
                    File.OpenRead("Content/rustysphere.glb")
                )
            );
            rustyBallModel.DisableNormalMaps();
            rustyBallModel.DisableAlbedoMaps();
            rustyBallModel.DisableMetallicRoughnessMaps();
            rustyBallModel.Albedo = Color.DeepSkyBlue;
            rustyBallModel.Metallic = 0.5f;
            rustyBallModel.Roughness = 0.284f;

            var lightBulbModel = Kav.ModelLoader.Load(
                GraphicsDevice,
                Smuggler.Importer.ImportGLB(GraphicsDevice, File.OpenRead("Content/cube.glb"))
            );

            var floorModel = Kav.ModelLoader.Load(
                GraphicsDevice,
                Smuggler.Importer.ImportGLB(GraphicsDevice, File.OpenRead("Content/floor.glb"))
            );
            floorModel.Metallic = 0f;
            floorModel.Roughness = 0f;

            var avocadoModel = Kav.ModelLoader.Load(
                GraphicsDevice,
                Smuggler.Importer.ImportGLB(GraphicsDevice, File.OpenRead("Content/avocado.glb"))
            );

            var redCylinderModel = Kav.ModelLoader.Load(
                GraphicsDevice,
                Smuggler.Importer.ImportGLB(
                    GraphicsDevice,
                    File.OpenRead("Content/redcylinder.glb")
                )
            );

            var pinkConeModel = Kav.ModelLoader.Load(
                GraphicsDevice,
                Smuggler.Importer.ImportGLB(
                    GraphicsDevice,
                    File.OpenRead("Content/pinkcone.glb")
                )
            );

            var blueTorusModel = Kav.ModelLoader.Load(
                GraphicsDevice,
                Smuggler.Importer.ImportGLB(
                    GraphicsDevice,
                    File.OpenRead("Content/bluetorus.glb")
                )
            );

            var cubeModel = Kav.ModelLoader.Load(
                GraphicsDevice,
                Smuggler.Importer.ImportGLB(
                    GraphicsDevice,
                    File.OpenRead("Content/cube.glb")
                )
            );

            var toonShadeRuinsModel = Kav.ModelLoader.Load(
                GraphicsDevice,
                Smuggler.Importer.ImportGLB(
                    GraphicsDevice,
                    File.OpenRead("Content/ToonShadeRuins.glb")
                )
            );

            Texture2D.TextureDataFromStreamEXT(
                File.OpenRead("Content/Skybox/front.jpg"),
                out var skyboxSize,
                out _,
                out byte[] frontPixels
            );

            Texture2D.TextureDataFromStreamEXT(
                File.OpenRead("Content/Skybox/back.jpg"),
                out _,
                out _,
                out byte[] backPixels
            );

            Texture2D.TextureDataFromStreamEXT(
                File.OpenRead("Content/Skybox/right.jpg"),
                out _,
                out _,
                out byte[] rightPixels
            );

            Texture2D.TextureDataFromStreamEXT(
                File.OpenRead("Content/Skybox/left.jpg"),
                out _,
                out _,
                out byte[] leftPixels
            );

            Texture2D.TextureDataFromStreamEXT(
                File.OpenRead("Content/Skybox/top.jpg"),
                out _,
                out _,
                out byte[] topPixels
            );

            Texture2D.TextureDataFromStreamEXT(
                File.OpenRead("Content/Skybox/bottom.jpg"),
                out _,
                out _,
                out byte[] bottomPixels
            );

            var skybox = new TextureCube(GraphicsDevice, skyboxSize, false, SurfaceFormat.Color);
            skybox.SetData(CubeMapFace.PositiveZ, frontPixels);
            skybox.SetData(CubeMapFace.NegativeZ, backPixels);
            skybox.SetData(CubeMapFace.PositiveX, rightPixels);
            skybox.SetData(CubeMapFace.NegativeX, leftPixels);
            skybox.SetData(CubeMapFace.PositiveY, topPixels);
            skybox.SetData(CubeMapFace.NegativeY, bottomPixels);

            var mushroomGuyTexture = Texture2D.FromStream(GraphicsDevice, new FileStream("Content/Sprites/mushroomguy.png", FileMode.Open));

            var mushroomGuyMeshSprite = new Kav.MeshSprite(
                GraphicsDevice,
                mushroomGuyTexture,
                Kav.SpriteBillboardConstraint.None
            );

            WorldBuilder.AddEngine(new InputEngine(this));
            WorldBuilder.AddEngine(new AngularVelocityEngine());
            WorldBuilder.AddEngine(new MoveAlongCurve3DEngine());
            WorldBuilder.AddEngine(new MotionEngine());
            WorldBuilder.AddEngine(new CameraEngine());
            WorldBuilder.AddEngine(new RustyBallSpawner(rustyBallModel));
            WorldBuilder.AddEngine(new LightBulbSpawner(lightBulbModel));
            WorldBuilder.AddEngine(new StaticModelSpawner());
            WorldBuilder.AddEngine(new DirectionalLightSpawner());
            WorldBuilder.AddEngine(new MeshSpriteSpawner());
            WorldBuilder.AddGeneralRenderer(new SceneRenderer(GraphicsDevice), 0);

            // WorldBuilder.SendMessage(new RustyBallSpawnMessage(
            //     new Transform3D(new Vector3(0, 0, 0)),
            //     new Vector3(1, 1, -1)
            // ));

            // WorldBuilder.SendMessage(new RustyBallSpawnMessage(
            //     new Transform3D(new Vector3(-3, 0, 0)),
            //     new Vector3(-1, 1, -1)
            // ));

            // WorldBuilder.SendMessage(new RustyBallSpawnMessage(
            //     new Transform3D(new Vector3(3, 0, 0)),
            //     new Vector3(-1, 1, 1)
            // ));

            // WorldBuilder.SendMessage(new StaticModelSpawnMessage(
            //     new Transform3D(new Vector3(0, 1, 0), Quaternion.Identity, new Vector3(1f, 1f, 1f)),
            //     rustyBallModel
            // ));

            WorldBuilder.SendMessage(new StaticModelSpawnMessage(
                new Transform3D(new Vector3(0, -3, 0), Quaternion.Identity, new Vector3(10f, 1f, 10f)),
                floorModel
            ));

            WorldBuilder.SendMessage(new StaticModelSpawnMessage(
                new Transform3D(
                    new Vector3(0, 1, 0),
                    Quaternion.CreateFromAxisAngle(
                        Vector3.Right,
                        -Microsoft.Xna.Framework.MathHelper.PiOver2
                    ),
                    new Vector3(1f, 1f, 1f)
                ),
                redCylinderModel
            ));

            WorldBuilder.SendMessage(new StaticModelSpawnMessage(
                new Transform3D(
                    new Vector3(-3, 1, 0),
                    Quaternion.CreateFromAxisAngle(
                        Vector3.Right,
                        -Microsoft.Xna.Framework.MathHelper.PiOver2
                    ),
                    new Vector3(1f, 1f, 1f)
                ),
                blueTorusModel
            ));

            WorldBuilder.SendMessage(new StaticModelSpawnMessage(
                new Transform3D(
                    new Vector3(3, 1, 0),
                    Quaternion.CreateFromAxisAngle(
                        Vector3.Right,
                        -Microsoft.Xna.Framework.MathHelper.PiOver2
                    ),
                    new Vector3(1f, 1f, 1f)
                ),
                cubeModel
            ));

            WorldBuilder.SendMessage(new MeshSpriteSpawnMessage(
                new Transform3D(new Vector3(3, 0, 11), Quaternion.CreateFromAxisAngle(Vector3.Up, (float)System.Math.PI), Vector3.One),
                mushroomGuyMeshSprite
            ));

            // WorldBuilder.SendMessage(new StaticModelSpawnMessage(
            //     Transform3D.Identity,
            //     toonShadeRuinsModel
            // ));

            // var lightEntity = WorldBuilder.CreateEntity();
            // WorldBuilder.SetComponent(
            //     lightEntity,
            //     new Transform3DComponent(
            //         new Transform3D(
            //             new Vector3(0, 3, 3),
            //             Quaternion.Identity,
            //             new Vector3(0.1f, 0.1f, 0.1f)
            //         )
            //     )
            // );
            // WorldBuilder.SetComponent(
            //     lightEntity,
            //     new PointLightComponent(Color.Blue, 1000f)
            // );
            // WorldBuilder.SetComponent(
            //     lightEntity,
            //     new ModelComponent(cubeModel)
            // );

            for (var i = 0; i < 1; i++)
            {
                var start = RandomHelper.RandomVector3(2, 5);

                WorldBuilder.SendMessage(new LightBulbSpawnMessage(
                    new Transform3D(start.ToXNAVector(), Quaternion.Identity, new Vector3(0.1f, 0.1f, 0.1f)),
                    RandomHelper.RandomColor(),
                    50f,
                    RandomHelper.RandomLoop(start, 2, 5, 3, 10)
                ));
            }

            var ambientLightEntity = WorldBuilder.CreateEntity();

            WorldBuilder.SetComponent(ambientLightEntity, new AmbientLightComponent(
                new Color(0.4f, 0.4f, 0.4f)
            ));

            WorldBuilder.SendMessage(new DirectionalLightSpawnMessage(
                //Quaternion.CreateFromAxisAngle(Vector3.Right, Microsoft.Xna.Framework.MathHelper.Pi / 3f),
                Quaternion.CreateFromAxisAngle(Vector3.Right, Microsoft.Xna.Framework.MathHelper.PiOver4),
                Color.LightGoldenrodYellow,
                0.7f
            ));

            // WorldBuilder.SendMessage(new DirectionalLightSpawnMessage(
            //     Quaternion.CreateFromAxisAngle(Vector3.Right, Microsoft.Xna.Framework.MathHelper.PiOver4),
            //     Color.GhostWhite,
            //     0.1f
            // ));

            // WorldBuilder.SendMessage(new DirectionalLightSpawnMessage(
            //     Quaternion.CreateFromAxisAngle(Vector3.Right, Microsoft.Xna.Framework.MathHelper.PiOver2 + Microsoft.Xna.Framework.MathHelper.PiOver4),
            //     Color.GhostWhite,
            //     0.1f
            // ));

            var cameraEntity = WorldBuilder.CreateEntity();
            WorldBuilder.SetComponent(cameraEntity, new ArcballTransformComponent(
                new ArcballTransform(
                    new Vector3(0, 0, -10),
                    Microsoft.Xna.Framework.MathHelper.Pi,
                    0
                )
            ));
            WorldBuilder.SetComponent(cameraEntity, new PerspectiveCameraComponent(
                Microsoft.Xna.Framework.MathHelper.PiOver4,
                16f / 9f,
                0.01f,
                100f
            ));

            var skyboxEntity = WorldBuilder.CreateEntity();
            WorldBuilder.SetComponent(skyboxEntity, new SkyboxComponent(
                skybox
            ));

            World = WorldBuilder.Build();
        }

        protected override void Update(GameTime gameTime)
        {
            World.Update(gameTime.ElapsedGameTime.TotalSeconds);

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            World.Draw();

            base.Draw(gameTime);
        }
    }
}
