using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace WaterTest
{
    class FlowLines : IDisposable
    {
        public FlowLines(Game game, Camera camera, DynamicFlowMap flowMap, Vector2 worldTopLeft, Vector2 worldBottomRight)
        {
            this.device = game.GraphicsDevice;
            this.camera = camera;
            this.flowMap = flowMap;
            lineEffect = game.Content.Load<Effect>(@"Fx\FlowLines");

            width = flowMap.Width;
            height = flowMap.Height;

            Vector2 worldSize = worldBottomRight - worldTopLeft;

            worldToFlowMapTexCoord = new Vector4(
                -worldTopLeft.X,
                -worldTopLeft.Y,
                1f / worldSize.X,
                1f / worldSize.Y
                );

            squareSize = new Vector2(worldSize.X / width, worldSize.Y / height);

            VertexPositionColor[] vertices = new VertexPositionColor[width * height * 2];
            for (int y = 0; y < height; y++)
            {
                float zPosition = squareSize.Y * y + worldTopLeft.Y;
                for (int x = 0; x < width; x++)
                {
                    float xPosition = squareSize.X * x + worldTopLeft.X;
                    int index = (x + y * width) * 2;
                    vertices[index] = new VertexPositionColor(
                        new Vector3(xPosition, 0f, zPosition),
                        Color.Pink
                        );
                    vertices[index + 1] = new VertexPositionColor(
                        new Vector3(xPosition, 1f, zPosition),   // overriding usage of Y to indicate which vertex of the line this is.
                        Color.Aquamarine
                        );
                }
            }
            vb = new VertexBuffer(game.GraphicsDevice, typeof(VertexPositionColor), vertices.Length, BufferUsage.WriteOnly);
            vb.SetData(vertices);
        }

        public void Dispose()
        {
            vb.Dispose();
        }

        RenderTarget2D vertexFlowMapTexture;
        Vector4 worldToFlowMapTexCoord;
        Vector2 squareSize;
        Camera camera;
        VertexBuffer vb;
        int width, height;
        GraphicsDevice device;
        DynamicFlowMap flowMap;
        Effect lineEffect;

        public void PreRender(SpriteBatch spriteBatch)
        {
            // Delay it's creation.
            if (vertexFlowMapTexture == null)
            {
                // We need a floating point one, since we sample in the vertex shader
                vertexFlowMapTexture = new RenderTarget2D(device, width, height, false, SurfaceFormat.HalfVector2, DepthFormat.None);
            }

            device.SetRenderTarget(vertexFlowMapTexture);
            spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque);
            spriteBatch.Draw(flowMap.CurrentFlowMap, Vector2.Zero, Color.White);
            spriteBatch.End();
        }

        public void Render()
        {
            device.RasterizerState = wireFrame;
            device.BlendState = BlendState.Opaque;
            device.DepthStencilState = DepthStencilState.DepthRead;

            lineEffect.Parameters["World"].SetValue(Matrix.Identity);
            lineEffect.Parameters["View"].SetValue(camera.View);
            lineEffect.Parameters["Projection"].SetValue(camera.Projection);
            lineEffect.Parameters["WorldToFlowMapTexCoord"].SetValue(worldToFlowMapTexCoord);
            lineEffect.Parameters["FlowMapTexture"].SetValue(vertexFlowMapTexture);
            lineEffect.Parameters["SquareSize"].SetValue(squareSize);
            lineEffect.CurrentTechnique.Passes[0].Apply();

            device.VertexSamplerStates[0] = SamplerState.PointClamp;

            device.SetVertexBuffer(vb);
            device.Indices = null;
            device.DrawPrimitives(PrimitiveType.LineList, 0, vb.VertexCount / 2);

            // Restore unusual state
            device.RasterizerState = RasterizerState.CullCounterClockwise;
        }

        private static RasterizerState wireFrame = new RasterizerState()
        {
            FillMode = FillMode.WireFrame,
            CullMode = CullMode.CullCounterClockwiseFace,
        };
    }
}
