using System;
using ComponentFramework;
using ComponentFramework.Modules;
using FullScreenShaders.Services;
using MTV3D65;
using System.Collections.Generic;

namespace FullScreenShaders.Components.Hosts
{
    [AutoLoad]
    class WorldHost : Component
    {
        readonly List<TVMesh> meshes = new List<TVMesh>();

        public WorldHost(ICore provider) : base(provider) { }

        public override void Initialize()
        {
            Scene.SetBackgroundColor(0.2f, 0.2f, 0.2f);

            var direction = new TV_3DVECTOR(0.25f, -0.5f, 1);
            MathLibrary.TVVec3Normalize(ref direction, direction);
            LightEngine.CreateDirectionalLight(direction, 1, 1, 1, "Light", 1);
            LightEngine.SetSpecularLighting(true);

            int matId = MaterialFactory.CreateMaterial("Default");
            MaterialFactory.SetEmissive(matId, 0, 0, 0, 1);
            MaterialFactory.SetAmbient(matId, 0.1f, 0.1f, 0.1f, 1);
            MaterialFactory.SetDiffuse(matId, 0.9f, 0.9f, 0.9f, 1);
            MaterialFactory.SetSpecular(matId, 0.5f, 0.5f, 0.5f, 1);
            MaterialFactory.SetPower(matId, 20);

            Scene.SetTextureFilter(CONST_TV_TEXTUREFILTER.TV_FILTER_ANISOTROPIC);
            Scene.SetMaxAnisotropy(DeviceInfo.GetMaxAnisotropy());
            TextureFactory.SetTextureMode(CONST_TV_TEXTUREMODE.TV_TEXTUREMODE_32BITS);

            TextureFactory.LoadTexture("Diffuse.dds", "D");
            TextureFactory.LoadTexture("Flat.dds", "F");
            TextureFactory.LoadTexture("Height.dds", "H");
            TextureFactory.LoadTexture("NormalGloss.dds", "NG");
            TextureFactory.LoadTexture("SkullDiffuse.dds", "SD");
            TextureFactory.LoadTexture("SkullNormal.dds", "SN");
            TextureFactory.LoadTexture("RancorDiffuse.dds", "RD");
            TextureFactory.LoadTexture("RancorNormal.dds", "RN");

            for(int i=0; i<4; i++)
            {
                TVMesh mesh = Scene.CreateMeshBuilder();
                switch(i)
                {
                    case 0:
                        mesh.LoadTVM("Skull.tvm", false, false);
                        mesh.SetTextureEx((int)CONST_TV_LAYER.TV_LAYER_BASETEXTURE, Globals.GetTex("SD"));
                        mesh.SetTextureEx((int)CONST_TV_LAYER.TV_LAYER_NORMALMAP, Globals.GetTex("SN"));
                        mesh.SetTextureEx((int)CONST_TV_LAYER.TV_LAYER_HEIGHTMAP, Globals.GetTex("F"));
                        mesh.RotateY((float)Math.PI);
                        break;
                    case 1:
                        mesh.LoadTVM("Ship.tvm", false, false);
                        mesh.SetTextureEx((int)CONST_TV_LAYER.TV_LAYER_BASETEXTURE, Globals.GetTex("D"));
                        mesh.SetTextureEx((int)CONST_TV_LAYER.TV_LAYER_NORMALMAP, Globals.GetTex("NG"));
                        mesh.SetTextureEx((int)CONST_TV_LAYER.TV_LAYER_HEIGHTMAP, Globals.GetTex("H"));
                        break;
                    case 2:
                        mesh.LoadTVM("Rancor.tvm", false, false);
                        mesh.SetTextureEx((int)CONST_TV_LAYER.TV_LAYER_BASETEXTURE, Globals.GetTex("RD"));
                        mesh.SetTextureEx((int)CONST_TV_LAYER.TV_LAYER_NORMALMAP, Globals.GetTex("RN"));
                        mesh.SetTextureEx((int)CONST_TV_LAYER.TV_LAYER_HEIGHTMAP, Globals.GetTex("F"));
                        break;
                    case 3:
                        mesh.CreateTeapot();
                        mesh.SetTextureEx((int)CONST_TV_LAYER.TV_LAYER_NORMALMAP, Globals.GetTex("F"));
                        mesh.SetTextureEx((int)CONST_TV_LAYER.TV_LAYER_HEIGHTMAP, Globals.GetTex("F"));

                        break;
                }

                TV_3DVECTOR minimum = new TV_3DVECTOR(), maximum = new TV_3DVECTOR();
                mesh.GetBoundingBox(ref minimum, ref maximum);
                TV_3DVECTOR center = (minimum + maximum) / 2;
                mesh.SetMeshCenter(center.x, center.y, center.z);

                TV_3DVECTOR diff = maximum - minimum;
                float factor = 1.0f / Math.Max(Math.Max(diff.x, diff.y), diff.z);
                mesh.SetScale(factor, factor, factor);

                mesh.SetLightingMode(CONST_TV_LIGHTINGMODE.TV_LIGHTING_OFFSETBUMPMAPPING_TANGENTSPACE);
                mesh.SetMaterial(matId);

                switch(i)
                {
                    case 0: mesh.SetPosition(-1, 0, 0); break;
                    case 1: mesh.SetPosition(1, 0, 0); break;
                    case 2: mesh.SetPosition(0, 1, 0); break;
                    case 3: mesh.SetPosition(0, -1, 0); break;
                }

                meshes.Add(mesh);
            }

            base.Initialize();
        }

        public override void Update(TimeSpan elapsedTime)
        {
            if (SampleState.Rotation)
            {
                meshes[0].RotateY((float)elapsedTime.TotalSeconds / 2.0f);
                meshes[0].RotateZ((float)elapsedTime.TotalSeconds / 2.0f);
                meshes[1].RotateY(-(float)elapsedTime.TotalSeconds / 2.0f);
                meshes[2].RotateY((float)elapsedTime.TotalSeconds / 2.0f);
                meshes[3].RotateX((float)elapsedTime.TotalSeconds / 2.0f);
            }
        }

        public override void Draw()
        {
            foreach (TVMesh mesh in meshes)
                mesh.Render();
        }

        [ServiceDependency]
        public ISampleState SampleState { private get; set; }
    }
}