//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 System.Drawing;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.Samples.DirectX.UtilityToolkit;

namespace DXGfxLib
{
    public class Water2 : SceneObject
    {
        public VertexBuffer waterVBuff = null;
        public IndexBuffer waterIBuff = null;
        private Texture waterTexture = null;
        private Texture bump = null;
        public int numberVertices = 0;
        public int numberTri = 0;
        private Effect waterEffect = null;
        private float movex = 0;
        private float movey = 1;
        private float R0 = 0.02037f;
        private Frustrum frustrum = null;

        public float desiredDimension = 1000.0f;

        public Water2()
        {
            Initialize();
        }

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

        public Water2(float dim)
        {
            LoadFromFile(DXGfxManager.GetGlobalInstance().d3dDevice, "Water.bmp");
        }

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

            movex = 0;
            movey = 1;
            R0 = 0.02037f;

            desiredDimension = 1000.0f;

            Device dxDevice = DXGfxManager.GetGlobalInstance().d3dDevice;

            string currDir = Directory.GetCurrentDirectory();

            if (!File.Exists(fileName))
            {
                throw new Exception("The ressource file does not exist! There is a problem somewhere.... did you delete or moved any file?");
            }
            else
            {
                LoadFromFile(dxDevice, fileName);
            }
        }

        /// <summary>
        /// Use this method to populate Vertex Buffer and Index Buffer to be used to render
        /// water.
        /// </summary>
        /// <param name="d3ddevice"></param>
        public override void LoadFromFile(Device d3ddevice, string fileName)
        {
            int numberOfCollRow = 4;
            int numberOfVerticesPerCollRow = numberOfCollRow + 1;

            numberVertices = numberOfVerticesPerCollRow * numberOfVerticesPerCollRow;
            numberTri = numberOfCollRow * numberOfCollRow * 2;

            float startingXPos = -desiredDimension / 2;
            float currentXPos = startingXPos;

            float startingTU = 0.0f;
            float currentTU = startingTU;
            float stepTU = 1.0f / numberOfCollRow;

            float startingZPos = desiredDimension / 2;
            float currentZPos = startingZPos;

            float startingTV = 1.0f;
            float currentTV = startingTV;
            float stepTV = 1.0f / numberOfCollRow;

            float dimensionOfCollRow = desiredDimension / numberOfCollRow;

            int vertexIndex = 0;

            CustomVertex.PositionNormalTextured[] vertices = new CustomVertex.PositionNormalTextured[numberVertices];

            for (int i = 0; i < numberOfVerticesPerCollRow; i++)
            {
                for (int j = 0; j < numberOfVerticesPerCollRow; j++)
                {
                    vertices[vertexIndex].X = currentXPos;
                    vertices[vertexIndex].Y = 0.0f;
                    vertices[vertexIndex].Z = currentZPos;

                    vertices[vertexIndex].Nx = 0.0f;
                    vertices[vertexIndex].Ny = 1.0f;
                    vertices[vertexIndex].Nz = 0.0f;

                    vertices[vertexIndex].Tu = currentTU;
                    vertices[vertexIndex].Tv = currentTV;

                    vertexIndex++;

                    currentTU += stepTU;
                    currentXPos += dimensionOfCollRow;
                }
                currentXPos = startingXPos;
                currentTU = startingTU;
                currentZPos -= dimensionOfCollRow;
                currentTV -= stepTV;
            }

            waterVBuff = new VertexBuffer(typeof(CustomVertex.PositionNormalTextured), numberVertices,
                d3ddevice,
                Usage.Dynamic, CustomVertex.PositionNormalTextured.Format, Pool.SystemMemory);

            GraphicsStream vbStream = waterVBuff.Lock(0, 0, LockFlags.NoOverwrite);
            vbStream.Write(vertices);
            waterVBuff.Unlock();


            Int16[] indices = new Int16[numberTri * 3];

            int indiceIndex = 0;

            for (int n = 0; n < numberOfCollRow; n++)
            {
                for (int m = 0; m < numberOfCollRow; m++)
                {
                    indices[indiceIndex + 0] = (Int16)(m + n * (numberOfCollRow + 1));
                    indices[indiceIndex + 1] = (Int16)(m + 1 + (n + 1) * (numberOfCollRow + 1));
                    indices[indiceIndex + 2] = (Int16)(m + (n + 1) * (numberOfCollRow + 1));

                    indices[indiceIndex + 3] = (Int16)(m + n * (numberOfCollRow + 1));
                    indices[indiceIndex + 4] = (Int16)(m + 1 + n * (numberOfCollRow + 1));
                    indices[indiceIndex + 5] = (Int16)(m + 1 + (n + 1) * (numberOfCollRow + 1));

                    indiceIndex += 6;
                }
            }

            waterIBuff = new IndexBuffer(d3ddevice,
                sizeof(Int16) * numberTri * 3, Usage.Dynamic, Pool.SystemMemory, true);

            GraphicsStream ibStream = waterIBuff.Lock(0, 0, LockFlags.NoOverwrite);

            ibStream.Write(indices);

            waterIBuff.Unlock();

            // Load the water texture
            // Load our file
            this.fileName = fileName;
            string textureFile = 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(textureFile).Directory.FullName);
            // Now load the mesh hierarchy
            waterTexture = ResourceCache.GetGlobalInstance().CreateTextureFromFile(d3ddevice, textureFile);

            System.IO.Directory.SetCurrentDirectory(currentFolder);

            textureFile = Utility.FindMediaFile("waveBumps.dds");
            // 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
            bump = ResourceCache.GetGlobalInstance().CreateTextureFromFile(d3ddevice, textureFile);

            System.IO.Directory.SetCurrentDirectory(currentFolder);

            worldMat = Matrix.Translation(0.0f, 0.0f, 0.0f);
        }

        public override void Update(Frustrum frustrum, double appTime, float elapsedTime)
        {
            this.frustrum = frustrum;
            movex = 0.03f * (float)Math.Sin(appTime);
            movey = 0.03f * (float)Math.Cos(appTime);
        }

        public void Render(Device device, Texture refractionTex, Texture reflectionTex)
        {
            // Water
            device.Transform.World = worldMat;
            device.SetStreamSource(0, waterVBuff, 0);
            device.Indices = waterIBuff;

            device.VertexFormat = CustomVertex.PositionNormalTextured.Format;

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

            Camera camera = DXGfxManager.GetGlobalInstance().CurrentCamera;
            waterEffect.SetValue("g_WorldViewProj", worldMat * frustrum.matView * frustrum.matProj);
            waterEffect.SetValue("g_WaterTexture", waterTexture);
            waterEffect.SetValue("g_BumpTexture", bump);
            waterEffect.SetValue("g_RefractionTexture", refractionTex);
            waterEffect.SetValue("g_ReflectionTexture", reflectionTex);
            waterEffect.SetValue("g_Movex", movex);
            waterEffect.SetValue("g_Movey", movey);
            waterEffect.SetValue("g_R0", R0);
            Vector3 position = MathUtil.ExtractPosition(Matrix.Invert(frustrum.matView));
            waterEffect.SetValue("g_Eye", new Vector4(position.X, position.Z, position.Y, 1));
            waterEffect.Technique = "RenderWater20";

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

                device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, numberVertices, 0, numberTri);

                waterEffect.EndPass();
            }
            waterEffect.End();
        }
    }
}
