using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VisiMath.Fractals;

namespace VisiMath.Geometry
{
    
    public class GeoHeightMap
    {
        public HeightMap Height;
        public Vector3 Scale;
        public Vector2 TextureScale;
        public Vector3 Position;

        public GeoHeightMap(int xsize, int ysize) : this(null, xsize, ysize)
        {
        }

        public GeoHeightMap(Random rgn, int xsize, int ysize)
        {
            //Height = new MidDisplacement(xsize, ysize);

            if (rgn == null)
                Height = new PerlinTerrain(xsize, ysize);
            else
                Height = new PerlinTerrain(rgn, xsize, ysize);

            Scale = new Vector3(1, 1, 1);
            Position = new Vector3(0);
            TextureScale = new Vector2(1, 1);
        }


        public IndexedMesh GenerateTriMesh()
        {
            int vertexcount = Height.Height*Height.Width;
            int indexcount = (Height.Height-1) * (Height.Width-1)*6;
            
            
            VertexArray<TexturedVertex> vertices = new VertexArray<TexturedVertex>(vertexcount);
            int[] indices = new int[indexcount];

            //Vertices

            int index = 0;

            float xs = 0;
            float ys = 0;

            Vector3 norm = new Vector3(0,0,1);
            for (int y = 0; y < Height.Height; y++)
            {
                xs = 0;
                for (int x = 0; x < Height.Width; x++)
                {
                    Vector3 pos = new Vector3(Position.X + x*Scale.X, Position.Y + y*Scale.Y, Position.Z + Scale.Z * Height[x, y]);
                    Vector2 tx = new Vector2(xs, ys);
                    vertices[index] = new TexturedVertex(ref pos, ref norm, ref tx);
                    index++;
                    xs += TextureScale.X;
                }
                ys += TextureScale.Y;
            }

            //Indices

            index = 0;
            int wi = Height.Width;

            for (int y = 0; y < Height.Height - 1; y++)
            {
                for (int x = 0; x < Height.Width - 1; x++)
                {
                    indices[index] = (x + y*wi);
                    indices[index + 1] = ((x+1) + y*wi);
                    indices[index + 2] = (x + 1 + (y+1)*wi);

                    indices[index + 3] = (x + 1 + (y + 1)*wi);
                    indices[index + 4] = (x + (y + 1)*wi);
                    indices[index + 5] = (x + y*wi);
                    index += 6;
                }
            }

            IndexedMesh rval = new IndexedMesh(vertices, indices);
            //rval.GenerateNormals();
            return rval;
        }


        /// <summary>
        /// Generates heightmap as list of quads
        /// </summary>
        /// <returns></returns>
        public VertexArray<TexturedVertex> GenerateQuads()
        {
            VertexArray<TexturedVertex> rval = new VertexArray<TexturedVertex>(4*Height.Width*Height.Height);

            int index = 0;

            float xs = 0;
            float ys = 0;

            for (int y = 0; y < Height.Height-1; y++)
            {
                xs = 0;
                for (int x = 0; x < Height.Width-1; x++)
                {

                    float depth1, depth2, depth3, depth4;
                    float scale = Scale.Z;
                    float depth = Position.Z;

                    depth1 = depth + scale * Height[x, y];
                    depth2 = depth + scale * Height[x + 1, y];
                    depth3 = depth + scale * Height[x + 1, y + 1];
                    depth4 = depth + scale * Height[x, y + 1];

                    Vector3 vec1 = new Vector3(1, 0, depth2 - depth1);
                    Vector3 vec2 = new Vector3(1, 1, depth3 - depth1);
                    Vector3 norm = vec1.Cross(ref vec2);
                    norm.Normalize();

                    TexturedVertex []vert = new TexturedVertex[4];

                    float x1, y1, x2, y2;
                    x1 = xs;
                    x2 = xs + TextureScale.X;
                    y1 = ys;
                    y2 = ys + TextureScale.Y;

                    vert[0].texCoord = new Vector2(x1, y1);
                    vert[0].position = new Vector3(x * 1.0f, y * 1.0f, depth1);
                    vert[1].texCoord = new Vector2(x2, y1);
                    vert[1].position = new Vector3(x * 1.0f + 1.0f, y * 1.0f, depth2);
                    vert[2].texCoord = new Vector2(x2, y2);
                    vert[2].position = new Vector3(x * 1.0f + 1.0f, y * 1.0f + 1.0f, depth3);
                    vert[3].texCoord = new Vector2(x1, y2);
                    vert[3].position = new Vector3(x * 1.0f, y * 1.0f + 1.0f, depth4);

                    for (int i = 0; i < 4; i++)
                    {
                        vert[i].normal = norm;
                        vert[i].position.X = vert[i].position.X * Scale.X;
                        vert[i].position.Y = vert[i].position.Y * Scale.Y;
                        vert[i].position.Add(ref Position);
                        rval[index + i] = vert[i];
                    }
                    index += 4;
                    xs += TextureScale.X;
                }
                ys += TextureScale.Y;
            }



            return rval;
        }

    }
}
