//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
{
    /// <summary>
    /// This class is used by the water class for exemple. It just makes a plane :).
    /// </summary>
    public class TexturedPlane : MeshObject
    {
        //public Mesh texturedPlaneMesh;

        /// <summary>
        /// If this member is set our plane will be rendered with that texture.
        /// </summary>
        public Texture texture;
        /// <summary>
        /// If this is set it will be used if we render the plane.
        /// </summary>
        public Material material;
        /// <summary>
        /// The width of the plane. This class make plane that have eq like a*y = b so the width is in fact
        /// the desired dimension along the X axis.
        /// </summary>
        public int width;
        /// <summary>
        /// See comments to width member. The heigth would then be the desired dimension along the Z axis.
        /// Remember that Z is going away to the infinite, Y is going up and X is going right.
        /// </summary>
        public int heigth;
        /// <summary>
        /// This class builds planes that have eq like y = yPos.
        /// </summary>
        public float yPos;
        /// <summary>
        /// The desired number of vertices along the X axis.
        /// </summary>
        public int numVertX;
        /// <summary>
        /// The desired number of vertices along the Z axis.
        /// </summary>
        public int numVertZ;

        public string textureFileName;

        /// <summary>
        /// This constructor without parameters is needed for serialization purposes.
        /// </summary>
        public TexturedPlane()
        {
        }

        public TexturedPlane(int width, int heigth, int numVertX, int numVertZ, string texturefile)
        {
            if (DXGfxManager.GetGlobalInstance().d3dDevice == null)
            {
                throw new Exception("DXGfxManager.d3dDevice is null, creation of a TexturedPlane is impossible without a valid device!");
            }
            Init(DXGfxManager.GetGlobalInstance().d3dDevice, width, heigth, numVertX, numVertZ, texturefile);
        }

        /// <summary>
        /// This constructor is the one you might use in your own code to get a plane.
        /// </summary>
        /// <param name="width"></param>
        /// <param name="heigth"></param>
        /// <param name="numVertX"></param>
        /// <param name="numVertZ"></param>
        /// <param name="texturefile"></param>
        public TexturedPlane(Device d3ddevice, int width, int heigth, int numVertX, int numVertZ, string texturefile)
        {
            Init(d3ddevice, width, heigth, numVertX, numVertZ, texturefile);
        }
        
        public virtual void Init(Device d3ddevice, int width, int heigth, int numVertX, int numVertZ, string texturefile)
        {
            this.width = width;
            this.heigth = heigth;
            this.numVertX = numVertX;
            this.numVertZ = numVertZ;
            this.yPos = 0.0f;

            // First load our texture.
            if (File.Exists(texturefile))
            {
                texture = ResourceCache.GetGlobalInstance().CreateTextureFromFile(d3ddevice, texturefile);
                textureFileName = texturefile;
            }
            else
            {
                throw new ArgumentException("Texture file:" +texturefile+  "does not exist!");
            }

            // Default material
            material = new Material();
            material.Ambient = Color.Aqua;
            material.Diffuse = Color.Azure;
            material.Emissive = Color.Blue;
            material.Specular = Color.Brown;

            CustomVertex.PositionTextured[] v = new CustomVertex.PositionTextured[numVertX * numVertZ];

            float dX = width / (numVertX - 1);
            float dZ = heigth / (numVertZ - 1);
            float x0 = -width / 2;
            float z0 = -heigth / 2;
            float dU = 1.0f / width;
            float dV = 0.7f / heigth;
            float x = x0;
            float z = z0;
            float tuTmp = 0;
            float tvTmp = heigth;

            //if (localBoundingBox == null)
            //{
            //    localBoundingBox = new BBox();
            //}
            //localBoundingBox.vMin = new Vector3(x0, -0.05f, z0);
            //localBoundingBox.vMax = new Vector3(x0 + width, 0.05f, z0 + heigth);

            for (int j = 0; j <= numVertX - 1; j++)
            {
                for (int i = 0; i <= numVertZ - 1; i++)
                {
                    v[i + j * (numVertX)].X = x;
                    v[i + j * (numVertX)].Z = z;
                    v[i + j * (numVertX)].Y = 0.0f;


                    // set the texture coordinates
                    v[i + j * (numVertX)].Tu = ((float)tuTmp / (float)width);
                    v[i + j * (numVertX)].Tv = ((float)tvTmp / (float)heigth);


                    //					v[i+j*(numVertX)].Nx = 0.0f;
                    //					v[i+j*(numVertX)].Ny = 1.0f;
                    //					v[i+j*(numVertX)].Nz = 0.0f;

                    x += dX;
                    tuTmp += dX;
                }

                z += dZ;
                tvTmp -= dZ;
                x = x0;
                tuTmp = 0;
            }

            originalMesh = new Mesh((numVertX - 1) * (numVertZ - 1) * 2, numVertX * numVertZ, MeshFlags.Managed,
                CustomVertex.PositionTextured.Format, d3ddevice);

            GraphicsStream buffStream = originalMesh.LockVertexBuffer(LockFlags.NoSystemLock);

            // Now let's copy our vertices to it.
            for (int h = 0; h < v.Length; h++)
            {
                buffStream.Write(v[h].X);
                buffStream.Write(v[h].Y);
                buffStream.Write(v[h].Z);
                //				buffStream.Write(v[h].Nx);
                //				buffStream.Write(v[h].Ny);
                //				buffStream.Write(v[h].Nz);
                buffStream.Write(v[h].Tu);
                buffStream.Write(v[h].Tv);
            }
            originalMesh.UnlockVertexBuffer();


            // Now let's build the indices.
            int[] indices = new int[(numVertX - 1) * (numVertZ - 1) * 6];

            for (int b = 0; b < numVertZ - 1; b++)
            {
                for (int a = 0; a < numVertX - 1; a++)
                {
                    indices[((a + b * (numVertX - 1)) * 6)] = a + b * numVertX;
                    indices[((a + b * (numVertX - 1)) * 6) + 1] = (a + 1) + b * numVertX;
                    indices[((a + b * (numVertX - 1)) * 6) + 2] = (a + 1) + (b + 1) * numVertX;

                    indices[((a + b * (numVertX - 1)) * 6) + 3] = a + b * numVertX;
                    indices[((a + b * (numVertX - 1)) * 6) + 4] = (a + 1) + (b + 1) * numVertX;
                    indices[((a + b * (numVertX - 1)) * 6) + 5] = a + (b + 1) * numVertX;
                }
            }

            GraphicsStream iBuffStream = originalMesh.LockIndexBuffer(LockFlags.NoSystemLock);

            for (int t = 0; t < indices.Length; t++)
            {
                iBuffStream.Write((Int16)indices[t]);
            }

            originalMesh.UnlockIndexBuffer();

            localBoundingBox = CalculateLocalBBox(originalMesh);
        }

        public override void Dispose()
        {
        }

        /// <summary>
        /// This method will be called when the device is changed.
        /// </summary>
        /// <param name="device"></param>
        public void ResetDevice(Device device)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// This method will be called from the Graphics.Manager once it has been decided that the plane
        /// is indeed  in the viewing frustrum and once the ISceneObject to be rendered have been sorted
        /// to avoid unecessary state changes or unecessary loading/unloading of geometry from video
        /// memory.
        /// </summary>
        public override void  Draw(Device device)
        {
            device.Material = material;
            device.SetTexture(0, texture);

            device.SetTransform(TransformType.World, worldMat);


            originalMesh.DrawSubset(0);
        }
    }
}
