using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfGraphLabels
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        // Draw a simple graph.
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            const double wxmin = -10;
            const double wxmax = 110;
            const double wymin = -1;
            const double wymax = 11;
            const double xstep = 10;
            const double ystep = 1;
            const double xtic = 5;
            const double ytic = 0.5;

            const double dmargin = 10;
            double dxmin = dmargin;
            double dxmax = graphCanvas.Width - dmargin;
            double dymin = dmargin;
            double dymax = graphCanvas.Height - dmargin;

            // Prepare the transformation matrices.
            PrepareTransformations(
                wxmin, wxmax, wymin, wymax,
                dxmin, dxmax, dymax, dymin);

            // Make the X axis.
            GeometryGroup xaxisGeom = new GeometryGroup();
            Point p0 = new Point(wxmin, 0);
            Point p1 = new Point(wxmax, 0);
            xaxisGeom.Children.Add(new LineGeometry(WtoD(p0), WtoD(p1)));

            for (double x = xstep; x <= wxmax - xstep; x += xstep)
            {
                Point tic0 = WtoD(new Point(x, -ytic));
                Point tic1 = WtoD(new Point(x, ytic));
                xaxisGeom.Children.Add(new LineGeometry(tic0, tic1));

                // Label the tic mark's X coordinate.
                DrawText(graphCanvas, x.ToString(), new Point(tic0.X, tic0.Y - 5), 12,
                    HorizontalAlignment.Center, VerticalAlignment.Top);
            }

            Path xaxisPath = new Path();
            xaxisPath.StrokeThickness = 1;
            xaxisPath.Stroke = Brushes.Black;
            xaxisPath.Data = xaxisGeom;

            graphCanvas.Children.Add(xaxisPath);

            // Make the Y axis.
            GeometryGroup yaxisGeom = new GeometryGroup();
            p0 = new Point(0, wymin);
            p1 = new Point(0, wymax);
            yaxisGeom.Children.Add(new LineGeometry(WtoD(p0), WtoD(p1)));

            for (double y = ystep; y <= wymax - ystep; y += ystep)
            {
                Point tic0 = WtoD(new Point(-xtic, y));
                Point tic1 = WtoD(new Point(xtic, y));
                yaxisGeom.Children.Add(new LineGeometry(tic0, tic1));

                // Label the tic mark's Y coordinate.
                DrawText(graphCanvas, y.ToString(), new Point(tic0.X - 10, tic0.Y), 12,
                    HorizontalAlignment.Center, VerticalAlignment.Center);
            }

            Path yaxisPath = new Path();
            yaxisPath.StrokeThickness = 1;
            yaxisPath.Stroke = Brushes.Black;
            yaxisPath.Data = yaxisGeom;

            graphCanvas.Children.Add(yaxisPath);

            // Make some data sets.
            Brush[] brushes = { Brushes.Red, Brushes.Green, Brushes.Blue };
            Random rand = new Random();
            for (int dataset = 0; dataset < 3; dataset++)
            {
                double lastY = rand.Next(3, 7);

                PointCollection points = new PointCollection();
                for (double x = 0; x <= 100; x += 10)
                {
                    lastY += rand.Next(-10, 10) / 10.0;
                    if (lastY < 0) lastY = 0;
                    if (lastY > 10) lastY = 10;
                    Point p = new Point(x, lastY);
                    points.Add(WtoD(p));
                }

                Polyline polyline = new Polyline();
                polyline.StrokeThickness = 1;
                polyline.Stroke = brushes[dataset];
                polyline.Points = points;

                graphCanvas.Children.Add(polyline);
            }

            // Make a title
            Point titleLocation = new Point((dxmin + dxmax) / 2, 10);
            DrawText(graphCanvas, "Amazing Data", titleLocation, 20,
                HorizontalAlignment.Center, VerticalAlignment.Top);
        }

        // Prepare values for perform transformations.
        private Matrix WtoDMatrix, DtoWMatrix;
        private void PrepareTransformations(
            double wxmin, double wxmax, double wymin, double wymax,
            double dxmin, double dxmax, double dymin, double dymax)
        {
            // Make WtoD.
            WtoDMatrix = Matrix.Identity;
            WtoDMatrix.Translate(-wxmin, -wymin);

            double xscale = (dxmax - dxmin) / (wxmax - wxmin);
            double yscale = (dymax - dymin) / (wymax - wymin);
            WtoDMatrix.Scale(xscale, yscale);

            WtoDMatrix.Translate(dxmin, dymin);

            // Make DtoW.
            DtoWMatrix = WtoDMatrix;
            DtoWMatrix.Invert();
        }

        // Transform a point from world to device coordinates.
        private Point WtoD(Point point)
        {
            return WtoDMatrix.Transform(point);
        }

        // Transform a point from device to world coordinates.
        private Point DtoW(Point point)
        {
            return DtoWMatrix.Transform(point);
        }

        // Position a label at the indicated point in device coordinates.
        private void DrawText(Canvas can, string text, Point location, double font_size,
            HorizontalAlignment halign, VerticalAlignment valign)
        {
            // Make the label.
            Label label = new Label();
            label.Content = text;
            label.FontSize = font_size;
            can.Children.Add(label);

            // Position the label.
            label.Measure(new Size(double.MaxValue, double.MaxValue));

            double x = location.X;
            if (halign == HorizontalAlignment.Center)
                x -= label.DesiredSize.Width / 2;
            else if (halign == HorizontalAlignment.Right)
                x -= label.DesiredSize.Width;
            Canvas.SetLeft(label, x);

            double y = location.Y;
            if (valign == VerticalAlignment.Center)
                y -= label.DesiredSize.Height / 2;
            else if (valign == VerticalAlignment.Bottom)
                y -= label.DesiredSize.Height;
            Canvas.SetTop(label, y);

            // Draw a box around the text for testing purposes.
            //Rectangle rect = new Rectangle();
            //rect.Stroke = Brushes.Red;
            //rect.StrokeThickness = 1;
            //can.Children.Add(rect);
            //rect.Width = label.DesiredSize.Width;
            //rect.Height = label.DesiredSize.Height;
            //Canvas.SetLeft(rect, x);
            //Canvas.SetTop(rect, y);
        }
    }
}
