using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Media3D;
using System.Drawing;

namespace Dialect
{
    /// <summary>
    /// The graphical representation of the plugin in the
    /// display window.
    /// </summary>
    public partial class PluginControl
    {
        public PluginControl(int id, Plugin parentInstance, DisplayPanel container)
        {
            Parent = parentInstance;
            Container = container;

            ID = id;

            // Create the model.
            Model = new Model();

            // Set various default attributes.
            Position = new Vector3D(0.0, 0.0, 0.0);
            Rotation = new Vector3D(0.0, 0.0, 0.0);

            Scale = new Vector3D(15.0, 15.0, 15.0);

            Spin = new Vector3D(0.0, 0.0, 0.0);
            Resolution = Resolution.Medium;

            Color = Color.Red;
            Clock = 0;
        }

        /// <summary>
        /// The control model.
        /// </summary>
        Model Model;

        /// <summary>
        ///  The update clock.
        /// </summary>
        float Clock;

        /// <summary>
        /// The plugin instance ID.
        /// </summary>
        public int ID { get; set; }

        /// <summary>
        /// The type of plugin that this control represents.
        /// </summary>
        public Plugin Parent { get; set; }
        
        /// <summary>
        /// The offset from the mouse position.
        /// </summary>
        public PointF Offset { get; set; }

        /// <summary>
        /// The drag offset from the mouse position.
        /// </summary>
        public Point DragOffset { get; set; }

        /// <summary>
        /// The control name.
        /// </summary>
        public String Name { get { return Parent.Name; } set { Parent.Name = value; } }

        /// <summary>
        /// The control position in 3D space.
        /// </summary>
        public Vector3D Position { get { return Model.Position; } set { Model.Position = value; } }

        /// <summary>
        /// The control rotation in 3D space.
        /// </summary>
        public Vector3D Rotation { get { return Model.Rotation; } set { Model.Rotation = value; } }

        /// <summary>
        /// The control scale factor.
        /// </summary>
        public Vector3D Scale { get { return Model.Scale; } set { Model.Scale = value; } }

        /// <summary>
        /// The display resolution.
        /// </summary>
        public Resolution Resolution { get; set; }

        /// <summary>
        /// The parent display container.
        /// </summary>
        public DisplayPanel Container { get; set; }

        /// <summary>
        /// If the control is selected.
        /// </summary>
        public bool IsSelected { get { return Model.IsSelected; } set { Model.IsSelected = value; } }

        /// <summary>
        /// The spin factor.
        /// </summary>
        public Vector3D Spin { get; set; }

        /// <summary>
        /// If the plugin is spinning.
        /// </summary>
        public bool IsSpinning { get { return (Spin != new Vector3D(0.0, 0.0, 0.0)); } }

        /// <summary>
        /// If the plugin is cycling its color.
        /// </summary>
        public bool CycleColor { get; set; }
        
        /// <summary>
        /// The color of the plugin.
        /// </summary>
        public Color Color { get { return Model.Color; } set { Model.Color = value; } }

        /// <summary>
        /// Computes if a 2D point (usually the mouse position)
        /// intersects the model in 3D space.
        /// </summary>
        /// <param name="location">The position to check.</param>
        /// <returns>If the position is inside the boundary.</returns>
        public bool InBounds(Point location)
        {
            Point3D centerPoint = Model.GetCenter();

            // Compute the mouse position in 3D space.
            Point3D mousePoint = new Point3D(location.X / (DisplayPanel.CameraPerspective / (Position.Z + DisplayPanel.CameraZPosition)),
                location.Y / (DisplayPanel.CameraPerspective / (Position.Z + DisplayPanel.CameraZPosition)),
                Position.Z);

            // Compute the model position in 3D space.
            Point3D modelPoint = new Point3D(Position.X + centerPoint.X, 
                Position.Y + centerPoint.Y, 
                Position.Z + centerPoint.Z);

            // If the distance is in the boundary.
            return (Utils.Distance(modelPoint, mousePoint) < 40.0f) ? true : false;
        }

        /// <summary>
        /// Computes a reverse projection to set the model to a 2D
        /// point in 3D space.
        /// </summary>
        /// <param name="location">The 2D location to place the model at.</param>
        public void SetLocation(Point location)
        {
            Position = new Vector3D(location.X / (DisplayPanel.CameraPerspective / (Position.Z + DisplayPanel.CameraZPosition)),
                location.Y / (DisplayPanel.CameraPerspective / (Position.Z + DisplayPanel.CameraZPosition)),
                Position.Z);
        }

        /// <summary>
        /// Draws the control to the display.
        /// </summary>
        /// <param name="graphics">The graphics device.</param>
        public void Draw(Graphics graphics)
        {
            // Increment the clock.
            Clock++;

            Model.Offset = Offset;

            if (Properties.Settings.Default.ShowAnimations)
            {
                // Set the rotation from a spin factor.
                Rotation += Spin;

                // Get the color cycle value.
                if (CycleColor)
                    Color = Utils.Cycle(Color, Clock, 100);
            }

            // Clamp the rotation between 0 and 360.
            Rotation = Utils.Clamp(Rotation, 0.0, 360.0);

            // Snap the position to the helper grid.
            Position = Utils.Snap(Position, 0.0, 3.0);

            // Draw the model using the display backgroun color.
            if (Utils.IsDark(Container.BackColor))
                Model.Draw(graphics, Color.White);
            else
                Model.Draw(graphics, Color.Black);

            if (IsSelected)
            {
                // Draw the text info overlay.
                PointF modelPosition = Utils.To2DF(Utils.PositionWithCameraZ(Position));
                PointF textPosition = new PointF(modelPosition.X + Offset.X + 10.0f, modelPosition.Y + Offset.Y + 10.0f);

                Pen textPen = new Pen(Brushes.Black);

                if (Utils.IsDark(Container.BackColor))
                    textPen.Brush = Brushes.White;

                graphics.FillRectangle(new SolidBrush(Color.FromArgb(80, textPen.Color)),
                    textPosition.X - 3.0f,
                    textPosition.Y - 3.0f,
                    160, 50);

                graphics.DrawString(Name +
                    "\nPosition: " + (int)Position.X + ", " + (int)Position.Y + ", " + (int)Position.Z +
                    "\nRotation: " + (int)Rotation.X + ", " + (int)Rotation.Y + ", " + (int)Rotation.Z,
                    new Font(FontFamily.GenericSansSerif, 10f, FontStyle.Bold),
                    textPen.Brush,
                    textPosition);
            }
        }
    }
}
