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

namespace WaterTest
{
    class _3DMousePaint
    {
        public _3DMousePaint(GraphicsDevice device, Camera camera, DynamicFlowMap flowMap)
        {
            this.flowMap = flowMap;
            this.camera = camera;
            this.device = device;
        }

        private DynamicFlowMap flowMap;
        private GraphicsDevice device;
        private Camera camera;

        private Vector3? previousWorldPosition;

        public void Update(GameTime gameTime, MouseState previousMouseState, MouseState currentMouseState)
        {
            if (currentMouseState.LeftButton == ButtonState.Pressed)
            {
                Vector3? positionOnGround = Get3DCursorPosition(new Vector2(currentMouseState.X, currentMouseState.Y));

                if (positionOnGround.HasValue)
                {
                    flowMap.PaintOnFlow(previousWorldPosition, positionOnGround.Value);
                    previousWorldPosition = positionOnGround.Value;
                }
            }
            else
            {
                previousWorldPosition = null;
            }
        }

        public Vector3? GetWorldPosition(MouseState mouseState)
        {
            return Get3DCursorPosition(new Vector2(mouseState.X, mouseState.Y));
        }

        // Returns a value if it hits the ground.
        private Vector3? Get3DCursorPosition(Vector2 position)
        {
            Ray ray = MouseCursorRay(position, camera.Projection, camera.View);
            Plane groundPlane = new Plane(Vector3.Up, 0);
            float? distance = ray.Intersects(groundPlane);
            if (distance.HasValue)
            {
                return ray.Position + ray.Direction * distance.Value;
            }
            return null;
        }

        private Ray MouseCursorRay(Vector2 position, Matrix projectionMatrix, Matrix viewMatrix)
        {
            // create 2 positions in screenspace using the cursor position. 0 is as
            // close as possible to the camera, 1 is as far away as possible.
            Vector3 nearSource = new Vector3(position, 0f);
            Vector3 farSource = new Vector3(position, 1f);

            // use Viewport.Unproject to tell what those two screen space positions
            // would be in world space. we'll need the projection matrix and view
            // matrix, which we have saved as member variables. We also need a world
            // matrix, which can just be identity.
            Vector3 nearPoint = device.Viewport.Unproject(nearSource,
                projectionMatrix, viewMatrix, Matrix.Identity);

            Vector3 farPoint = device.Viewport.Unproject(farSource,
                projectionMatrix, viewMatrix, Matrix.Identity);

            // find the direction vector that goes from the nearPoint to the farPoint
            // and normalize it....
            Vector3 direction = farPoint - nearPoint;
            direction.Normalize();

            // and then create a new ray using nearPoint as the source.
            return new Ray(nearPoint, direction);
        }
    }
}
