using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Drawing.Drawing2D;

namespace LeastSquares
{
    public partial class frm84LeastSquares : Form
    {
        public frm84LeastSquares()
        {
            InitializeComponent();
        }

        // Drawing constants.
        private const float Xmin = -10.0f;
        private const float Xmax = 10.0f;
        private const float Ymin = -10.0f;
        private const float Ymax = 10.0f;
        private Matrix DrawingTransform;
        private Matrix InverseTransform;

        private bool HasSolution = false;
        private List<double> BestCoeffs;
        private List<PointF> Points = new List<PointF>();

        // Make a drawing transformation.
        private void frm84LeastSquares_Load(object sender, EventArgs e)
        {
            RectangleF worldRect = new RectangleF(Xmin, Ymin, Xmax - Xmin, Ymax - Ymin);
            PointF[] pts =
            {
                new PointF(0, graphPictureBox.ClientSize.Height),
                new PointF(graphPictureBox.ClientSize.Width, graphPictureBox.ClientSize.Height),
                new PointF(0, 0),
            };
            DrawingTransform = new Matrix(worldRect, pts);
            InverseTransform = DrawingTransform.Clone();
            InverseTransform.Invert();

            // Pretty up the sample equation.
            equationRichTextBox.Select(5, 1);
            equationRichTextBox.SelectionCharOffset = -7;
            equationRichTextBox.Select(7, 1);
            equationRichTextBox.SelectionCharOffset = 7;
            equationRichTextBox.Select(12, 1);
            equationRichTextBox.SelectionCharOffset = -7;
            equationRichTextBox.Select(14, 1);
            equationRichTextBox.SelectionCharOffset = 7;
            equationRichTextBox.Select(19, 1);
            equationRichTextBox.SelectionCharOffset = -7;
            equationRichTextBox.Select(21, 1);
            equationRichTextBox.SelectionCharOffset = 7;
            equationRichTextBox.Select(26, 1);
            equationRichTextBox.SelectionCharOffset = -7;
            equationRichTextBox.Select(28, 1);
            equationRichTextBox.SelectionCharOffset = 7;
        }

        // Save a new point.
        private void graphPictureBox_MouseClick(object sender, MouseEventArgs e)
        {
            // Transform the point to world coordinates.
            PointF[] pts = { new PointF(e.X, e.Y) };
            InverseTransform.TransformPoints(pts);

            // Save the point.
            Points.Add(pts[0]);
            graphPictureBox.Refresh();
        }

        // Draw the points and best fit curve.
        private void graphPictureBox_Paint(object sender, PaintEventArgs e)
        {
            // Use the drawing transformation.
            e.Graphics.Transform = DrawingTransform;
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

            // Draw the axes.
            DrawAxes(e.Graphics);

            // Draw the curve.
            if (HasSolution)
            {
                using (Pen pen = new Pen(Color.Blue, 0))
                {
                    const double xStep = 0.1;
                    double y0 = CurveFunctions.F(BestCoeffs, Xmin);
                    for (double x = Xmin + xStep; x <= Xmax; x += xStep)
                    {
                        double y1 = CurveFunctions.F(BestCoeffs, x);
                        e.Graphics.DrawLine(pen,
                            (float)(x - xStep), (float)y0, (float)x, (float)y1);
                        y0 = y1;
                    }
                }
            }

            // Draw the points.
            const float dx = (Xmax - Xmin) / 100;
            const float dy = (Ymax - Ymin) / 100;
            using (Pen pen = new Pen(Color.Black, 0))
            {
                foreach (PointF pt in Points)
                {
                    e.Graphics.FillRectangle(Brushes.White,
                        pt.X - dx, pt.Y - dy, 2 * dx, 2 * dy);
                    e.Graphics.DrawRectangle(pen,
                        pt.X - dx, pt.Y - dy, 2 * dx, 2 * dy);
                }
            }
        }

        // Draw the axes.
        private void DrawAxes(Graphics gr)
        {
            using (Pen pen = new Pen(Color.Black, 0))
            {
                const float xthick = 0.2f;
                const float ythick = 0.2f;
                gr.DrawLine(pen, Xmin, 0, Xmax, 0);
                for (float x = Xmin; x <= Xmax; x += 1.0f)
                {
                    gr.DrawLine(pen, x, -ythick, x, ythick);
                }
                gr.DrawLine(pen, 0, Ymin, 0, Ymax);
                for (float y = Ymin; y <= Ymax; y += 1.0f)
                {
                    gr.DrawLine(pen, -xthick, y, xthick, y);
                }
            }
        }

        // Clear the points.
        private void clearButton_Click(object sender, EventArgs e)
        {
            Points = new List<PointF>();
            HasSolution = false;
            graphPictureBox.Refresh();

            coeffsTextBox.Clear();
            errorTextBox.Clear();
        }

        // Find parameters for a curve fit.
        private void fitButton_Click(object sender, EventArgs e)
        {
            Cursor = Cursors.WaitCursor;
            coeffsTextBox.Clear();
            errorTextBox.Clear();
            Application.DoEvents();

            // Find a good fit.
            int degree = (int)degreeUpDown.Value;
            BestCoeffs = CurveFunctions.FindPolynomialLeastSquaresFit(Points, degree);
            HasSolution = true;

            string text = "";
            foreach (double coeff in BestCoeffs)
            {
                text += " " + coeff.ToString();
            }
            coeffsTextBox.Text = text.Substring(1);

            // Display the error.
            ShowError();

            // We have a solution.
            HasSolution = true;
            graphPictureBox.Refresh();

            Cursor = Cursors.Default;
        }

        // Regraph with the given parameters.
        private void graphButton_Click(object sender, EventArgs e)
        {
            // Get the coefficients.
            string[] coeffs = coeffsTextBox.Text.Split();
            BestCoeffs = new List<double>();
            foreach (string coeff in coeffs)
            {
                BestCoeffs.Add(double.Parse(coeff));
            }
            ShowError();
            graphPictureBox.Refresh();
        }

        // Display the error.
        private void ShowError()
        {
            // Get the error.
            double error = Math.Sqrt(
                CurveFunctions.ErrorSquared(Points, BestCoeffs));
            errorTextBox.Text = error.ToString();
        }


        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.degreeUpDown = new System.Windows.Forms.NumericUpDown();
            this.graphButton = new System.Windows.Forms.Button();
            this.equationRichTextBox = new System.Windows.Forms.RichTextBox();
            this.label3 = new System.Windows.Forms.Label();
            this.errorTextBox = new System.Windows.Forms.TextBox();
            this.label2 = new System.Windows.Forms.Label();
            this.coeffsTextBox = new System.Windows.Forms.TextBox();
            this.label1 = new System.Windows.Forms.Label();
            this.clearButton = new System.Windows.Forms.Button();
            this.fitButton = new System.Windows.Forms.Button();
            this.graphPictureBox = new System.Windows.Forms.PictureBox();
            ((System.ComponentModel.ISupportInitialize)(this.degreeUpDown)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.graphPictureBox)).BeginInit();
            this.SuspendLayout();
            // 
            // degreeUpDown
            // 
            this.degreeUpDown.Location = new System.Drawing.Point(297, 90);
            this.degreeUpDown.Name = "degreeUpDown";
            this.degreeUpDown.Size = new System.Drawing.Size(41, 20);
            this.degreeUpDown.TabIndex = 62;
            this.degreeUpDown.Value = new decimal(new int[] {
            2,
            0,
            0,
            0});
            // 
            // graphButton
            // 
            this.graphButton.Location = new System.Drawing.Point(284, 219);
            this.graphButton.Name = "graphButton";
            this.graphButton.Size = new System.Drawing.Size(75, 23);
            this.graphButton.TabIndex = 56;
            this.graphButton.Text = "Graph";
            this.graphButton.UseVisualStyleBackColor = true;
            this.graphButton.Click += new System.EventHandler(this.graphButton_Click);
            // 
            // equationRichTextBox
            // 
            this.equationRichTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.equationRichTextBox.BackColor = System.Drawing.SystemColors.Control;
            this.equationRichTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None;
            this.equationRichTextBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.equationRichTextBox.Location = new System.Drawing.Point(249, 12);
            this.equationRichTextBox.Multiline = false;
            this.equationRichTextBox.Name = "equationRichTextBox";
            this.equationRichTextBox.Size = new System.Drawing.Size(210, 46);
            this.equationRichTextBox.TabIndex = 61;
            this.equationRichTextBox.Text = "y = A0X0 + A1X1 + A2X2 + A3X3 + ...";
            // 
            // label3
            // 
            this.label3.AutoSize = true;
            this.label3.Location = new System.Drawing.Point(246, 144);
            this.label3.Name = "label3";
            this.label3.Size = new System.Drawing.Size(32, 13);
            this.label3.TabIndex = 60;
            this.label3.Text = "Error:";
            // 
            // errorTextBox
            // 
            this.errorTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.errorTextBox.Location = new System.Drawing.Point(297, 141);
            this.errorTextBox.Name = "errorTextBox";
            this.errorTextBox.Size = new System.Drawing.Size(162, 20);
            this.errorTextBox.TabIndex = 55;
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(246, 118);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(22, 13);
            this.label2.TabIndex = 59;
            this.label2.Text = "As:";
            // 
            // coeffsTextBox
            // 
            this.coeffsTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.coeffsTextBox.Location = new System.Drawing.Point(297, 115);
            this.coeffsTextBox.Name = "coeffsTextBox";
            this.coeffsTextBox.Size = new System.Drawing.Size(162, 20);
            this.coeffsTextBox.TabIndex = 54;
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(246, 92);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(45, 13);
            this.label1.TabIndex = 58;
            this.label1.Text = "Degree:";
            // 
            // clearButton
            // 
            this.clearButton.Location = new System.Drawing.Point(326, 60);
            this.clearButton.Name = "clearButton";
            this.clearButton.Size = new System.Drawing.Size(75, 23);
            this.clearButton.TabIndex = 53;
            this.clearButton.Text = "Clear";
            this.clearButton.UseVisualStyleBackColor = true;
            this.clearButton.Click += new System.EventHandler(this.clearButton_Click);
            // 
            // fitButton
            // 
            this.fitButton.Location = new System.Drawing.Point(245, 60);
            this.fitButton.Name = "fitButton";
            this.fitButton.Size = new System.Drawing.Size(75, 23);
            this.fitButton.TabIndex = 52;
            this.fitButton.Text = "Fit";
            this.fitButton.UseVisualStyleBackColor = true;
            this.fitButton.Click += new System.EventHandler(this.fitButton_Click);
            // 
            // graphPictureBox
            // 
            this.graphPictureBox.BackColor = System.Drawing.Color.White;
            this.graphPictureBox.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
            this.graphPictureBox.Location = new System.Drawing.Point(12, 12);
            this.graphPictureBox.Name = "graphPictureBox";
            this.graphPictureBox.Size = new System.Drawing.Size(228, 228);
            this.graphPictureBox.TabIndex = 57;
            this.graphPictureBox.TabStop = false;
            this.graphPictureBox.Paint += new System.Windows.Forms.PaintEventHandler(this.graphPictureBox_Paint);
            this.graphPictureBox.MouseClick += new System.Windows.Forms.MouseEventHandler(this.graphPictureBox_MouseClick);
            // 
            // frm84LeastSquares
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(471, 254);
            this.Controls.Add(this.degreeUpDown);
            this.Controls.Add(this.graphButton);
            this.Controls.Add(this.equationRichTextBox);
            this.Controls.Add(this.label3);
            this.Controls.Add(this.errorTextBox);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.coeffsTextBox);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.clearButton);
            this.Controls.Add(this.fitButton);
            this.Controls.Add(this.graphPictureBox);
            this.Name = "frm84LeastSquares";
            this.Text = "LeastSquares";
            this.Load += new System.EventHandler(this.frm84LeastSquares_Load);
            ((System.ComponentModel.ISupportInitialize)(this.degreeUpDown)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.graphPictureBox)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.NumericUpDown degreeUpDown;
        private System.Windows.Forms.Button graphButton;
        private System.Windows.Forms.RichTextBox equationRichTextBox;
        private System.Windows.Forms.Label label3;
        private System.Windows.Forms.TextBox errorTextBox;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.TextBox coeffsTextBox;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Button clearButton;
        private System.Windows.Forms.Button fitButton;
        private System.Windows.Forms.PictureBox graphPictureBox;
    }
}