using System;
using Microsoft.Xna.Framework;

namespace ECOT.Engine.Graphics
{
    public delegate void CameraEventHandler(Camera camera);

    public class Camera
    {
        #region Initialization

        public Camera()
        {
            this.minLimit = this.maxLimit = Vector2.Zero;
        }

        #endregion

        #region Evenements

        public event CameraEventHandler OnMove;
        public event CameraEventHandler OnZoom;
        public event CameraEventHandler OnMoveToFinished;
        public event CameraEventHandler OnLimitChanged;

        #endregion

        #region Properties

        public Vector2 Position
        {
            get { return this.position; }
            set
            {
                this.moving = false;
                this.position = value;
                // Checks the position is inside the limits
                if (this.minLimit != this.maxLimit)
                    this.position = Vector2.Clamp(
                        this.position * Kernel.Instance.Camera.Zoom,
                        this.minLimit * Kernel.Instance.Camera.Zoom,
                        this.maxLimit * Kernel.Instance.Camera.Zoom - new Vector2(Kernel.Instance.Width, Kernel.Instance.Height)) / Kernel.Instance.Camera.Zoom;
                this.updated = false;

                if (this.OnMove != null)
                    this.OnMove(this);
            }
        }
        public float Zoom
        {
            get { return this.zoom; }
            set
            {
                // TODO: ajuster la caméra pour zoomer par rapport au centre de l'écran
                /*Vector2 offset = new Vector2(
                    (value - this.zoom) * Kernel.Instance.Width  * 0.5f,
                    (value - this.zoom) * Kernel.Instance.Height * 0.5f) / this.zoom;*/

                this.zoom = value;
                this.updated = false;

                // Réajuste la caméra
                //this.Move(offset);
                this.Move(Vector2.Zero);

                if (this.OnZoom != null)
                    this.OnZoom(this);
            }
        }

        public Matrix TransformMatrix
        {
            get { return this.transform; }
        }

        public Vector2 MinLimit
        {
            get { return this.minLimit; }
            set
            {
                this.minLimit = value;
                this.limitChanged = true;
            }
        }

        public Vector2 MaxLimit
        {
            get { return this.maxLimit; }
            set
            {
                this.maxLimit = value;
                this.limitChanged = true;
            }
        }

        #endregion

        #region Methods

        public void Move(Vector2 translation)
        {
            this.Position += translation;
        }

        public void MoveTo(Vector2 position, float accel)
        {
            if (this.position.Equals(position))
            {
                this.moving = false;
                if (this.OnMoveToFinished != null)
                    this.OnMoveToFinished(this);
                return;
            }

            this.moveTo = position;
            this.direction = position - this.position;
            this.length = this.direction.Length();
            this.length_2 = this.length * 0.5f;
            this.direction.Normalize();
            this.accel = accel;
            this.currentSpeed = 0.0f;
            this.moving = true;
        }

        public void ZoomTo(float zoom, float speed)
        {
            this.zoomTo = zoom;
            this.zoomSpeed = speed;
            if (this.zoomTo < this.zoom)
                this.zoomDirection = -1;
            else
                this.zoomDirection = +1;
            this.zooming = true;
        }

        public void Update(GameTime gameTime)
        {
            if (this.moving)
            {
                float dt = gameTime.ElapsedGameTime.Milliseconds / 1000.0f;
                float mod = (this.length >= this.length_2 ? +1 : -1);
                this.currentSpeed += mod * accel * dt;
                if (this.currentSpeed < 0.0f)
                    this.length = -1.0f;
                Vector2 newPos = this.position + this.direction * this.currentSpeed * dt;
                this.length -= (newPos - this.position).Length();

                if (this.length > float.Epsilon)
                    this.position = newPos;
                else
                {
                    this.position = this.moveTo;
                    this.moving = false;

                    if (this.OnMoveToFinished != null)
                        this.OnMoveToFinished(this);
                }

                if (this.OnMove != null)
                    this.OnMove(this);

                this.updated = false;
            }

            if (this.zooming)
            {
                float dt = gameTime.ElapsedGameTime.Milliseconds / 1000.0f;
                this.zoom += this.zoomDirection * this.zoomSpeed * dt;

                if (this.zoomDirection == -1 && this.zoom <= this.zoomTo ||
                    this.zoomDirection == +1 && this.zoom >= this.zoomTo)
                {
                    this.zoom = this.zoomTo;
                    this.zooming = false;
                }

                if (this.OnZoom != null)
                    this.OnZoom(this);

                this.updated = false;
            }

            if (!this.updated)
            {
                this.transform = Matrix.CreateTranslation(-this.position.X, -this.position.Y, 0.0f);
                this.transform*= Matrix.CreateScale(this.zoom);

                this.updated = true;
            }

            if (this.limitChanged)
            {
                if (this.OnLimitChanged != null)
                    this.OnLimitChanged(this);
                this.limitChanged = false;
            }
        }

        #endregion

        #region Fields

        private Vector2 position;
        private float zoom = 0.8f;
        private Vector2 minLimit;
        private Vector2 maxLimit;
        private bool updated = false;
        private bool limitChanged = false;

        private bool moving = false;
        private Vector2 moveTo;
        private Vector2 direction;
        private float length, length_2;
        private float accel;
        private float currentSpeed;

        private bool zooming = false;
        private float zoomTo;
        private float zoomSpeed;
        private float zoomDirection;

        private Matrix transform;

        #endregion
    }
}
