//BSD License  
//DXGfx® - http://www.eteractions.com
//Copyright (c) 2005
//by Guillaume Randon
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software
//and associated documentation files (the "Software"), to deal in the Software without restriction,
//including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
//and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
//subject to the following conditions:
//The above copyright notice and this permission notice shall be included in all copies or substantial
//portions of the Software.
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
//INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
//AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
//DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.Samples.DirectX.UtilityToolkit;

namespace DXGfxLib
{
    public class SkyDome : MeshObject
    {
        private Texture[] skyboxTextures = null;
        private Material[] skyBoxMaterials = null;
        private Effect skyEffect = null;
        private Texture baseTexture = null;
        private Texture layerTexture = null;
        private Texture gradientTexture = null;
        private Texture noiseTexture = null;
        private float movex = 0;
        private float movey = 0;
        private Frustrum frustrum = null;

        public SkyDome()
        {
            Initialize();
        }

        public override void Initialize()
        {
            base.Initialize();
        }

        public override void GetPrepared()
        {
            base.GetPrepared();
        }

        /// <summary>
        /// We use the Update method to ensure that the Skybox is always centered on the camera position.
        /// </summary>
        /// <param name="frustrum"></param>
        /// <param name="appTime"></param>
        /// <param name="elapsedTime"></param>
        public override void Update(Frustrum frustrum, double appTime, float elapsedTime)
        {
            base.Update(frustrum, appTime, elapsedTime);

            // We are not going to change the orientation of the skybox but we want its center to
            // be always at the camera position.
            Position = MathUtil.ExtractPosition(Matrix.Invert(frustrum.matView));

            movex = 0.03f * (float)Math.Sin(appTime);
            movey = 0.03f * (float)Math.Cos(appTime);

            this.frustrum = frustrum;
        }

        public void RenderSky(Device device)
        {
            device.Transform.World = worldMat;

            if (skyEffect == null)
            {
                DXGfxManager.GetGlobalInstance().LoadEffect(device, "SkyHLSL.fx", ref skyEffect);
            }

            skyEffect.SetValue("g_WorldViewProj", worldMat * frustrum.matView * frustrum.matProj);
            skyEffect.SetValue("g_LayerTexture", layerTexture);
            skyEffect.SetValue("g_GradientTexture", gradientTexture);
            skyEffect.SetValue("g_NoiseTexture", noiseTexture);
            skyEffect.SetValue("g_Movex", movex);
            skyEffect.SetValue("g_Movey", movey);
            skyEffect.SetValue("g_Movex2", movex);
            skyEffect.SetValue("g_Movey2", movey);
            skyEffect.Technique = "RenderSky";

            device.VertexFormat = CustomVertex.PositionNormalTextured.Format;

            device.RenderState.CullMode = Cull.None;

            for (int i = 0; i < skyBoxMaterials.Length; i++)
            {
                device.Material = skyBoxMaterials[i];
                skyEffect.SetValue("g_BaseTexture", skyboxTextures[i]);

                // Apply the technique contained in the effect 
                int passes = skyEffect.Begin(0);
                for (int pass = 0; pass < passes; pass++)
                {
                    skyEffect.BeginPass(pass);

                    // Draw the mesh subset
                    originalMesh.DrawSubset(i);

                    skyEffect.EndPass();
                }
                skyEffect.End();
            }
        }

        public override void LoadFromFile(Device d3ddevice, string fileName)
        {
            ExtendedMaterial[] materials = null;

            string meshFile = Utility.FindMediaFile(fileName);
            // Store the current folder, then switch to the folder where the media was found
            string currentFolder = System.IO.Directory.GetCurrentDirectory();
            System.IO.Directory.SetCurrentDirectory(new System.IO.FileInfo(fileName).Directory.FullName);

            originalMesh = Mesh.FromFile(fileName, MeshFlags.Managed, d3ddevice, out materials);

            if (skyboxTextures == null)
            {
                // We need to extract the material properties and texture names 
                skyboxTextures = new Texture[materials.Length];
                skyBoxMaterials = new Material[materials.Length];
                for (int i = 0; i < materials.Length; i++)
                {
                    skyBoxMaterials[i] = materials[i].Material3D;
                    // Set the ambient color for the material (D3DX does not do this)
                    skyBoxMaterials[i].Ambient = skyBoxMaterials[i].Diffuse;

                    // Create the texture
                    if (System.IO.File.Exists(materials[i].TextureFilename))
                    {
                        skyboxTextures[i] = ResourceCache.GetGlobalInstance().CreateTextureFromFile(d3ddevice, materials[i].TextureFilename);
                    }
                    else
                    {
                        skyboxTextures[i] = ResourceCache.GetGlobalInstance().CreateTextureFromFile(d3ddevice, "skybox_front.jpg");
                    }
                }
            }

            string textureFile = Utility.FindMediaFile("cloudBase.bmp");
            // Store the current folder, then switch to the folder where the media was found
            currentFolder = System.IO.Directory.GetCurrentDirectory();
            System.IO.Directory.SetCurrentDirectory(new System.IO.FileInfo(textureFile).Directory.FullName);
            // Now load the mesh hierarchy
            baseTexture = ResourceCache.GetGlobalInstance().CreateTextureFromFile(d3ddevice, textureFile);


            textureFile = Utility.FindMediaFile("cloudlayer.bmp");
            // Store the current folder, then switch to the folder where the media was found
            currentFolder = System.IO.Directory.GetCurrentDirectory();
            System.IO.Directory.SetCurrentDirectory(new System.IO.FileInfo(textureFile).Directory.FullName);
            // Now load the mesh hierarchy
            layerTexture = ResourceCache.GetGlobalInstance().CreateTextureFromFile(d3ddevice, textureFile);


            textureFile = Utility.FindMediaFile("gradient.bmp");
            // Store the current folder, then switch to the folder where the media was found
            currentFolder = System.IO.Directory.GetCurrentDirectory();
            System.IO.Directory.SetCurrentDirectory(new System.IO.FileInfo(textureFile).Directory.FullName);
            // Now load the mesh hierarchy
            gradientTexture = ResourceCache.GetGlobalInstance().CreateTextureFromFile(d3ddevice, textureFile);


            textureFile = Utility.FindMediaFile("noise.bmp");
            // Store the current folder, then switch to the folder where the media was found
            currentFolder = System.IO.Directory.GetCurrentDirectory();
            System.IO.Directory.SetCurrentDirectory(new System.IO.FileInfo(textureFile).Directory.FullName);
            // Now load the mesh hierarchy
            noiseTexture = ResourceCache.GetGlobalInstance().CreateTextureFromFile(d3ddevice, textureFile);

            System.IO.Directory.SetCurrentDirectory(currentFolder);

            worldMat = Matrix.Scaling(10.0f, 6.0f, 10.0f);
        }
    }
}
