using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Microsoft.Win32;

namespace GraphDrawer
{
    /// <summary>
    /// Interaction logic for GraphByClick.xaml
    /// </summary>
    public partial class GraphByClick
    {
        public GraphByClick()
        {
            InitializeComponent();

            VertexArray.vertexCount = 0;
        }

        readonly Random _rand = new Random();
        public GraphByClick(int[,] matrix, bool state)
        {
            InitializeComponent();
            VertexArray.vertexCount = 0;

            ClearCanvas();
            if (state)
            {
                int length = matrix.GetLength(0);
                for(int i = 0; i < length; i++)
                    VertexArray.AddVertex(canvas, _rand.Next(1, 600), _rand.Next(1, 300));

                for(int i = 0; i < length; i++)
                {
                    var vertex1 = VertexArray.vertex[i];
                    for(int j = 0; j < length; j++)
                    {
                        var vertex2 = VertexArray.vertex[j];
                        if (matrix[i, j] >= 1)
                        {
                            EdgeArray.edgeCount++;
                            Array.Resize(ref EdgeArray.edge, EdgeArray.edgeCount);
                            Array.Resize(ref EdgeArray.line, EdgeArray.edgeCount);
                            EdgeArray.line[EdgeArray.edgeCount - 1] = new Line
                            {
                                Stroke = new SolidColorBrush(Color.FromRgb(0, 0, 0)),
                                StrokeThickness = 2,
                                X1 = vertex1.X + 5,
                                Y1 = vertex1.Y + 5,
                                X2 = vertex2.X + 5,
                                Y2 = vertex2.Y + 5
                            };

                            canvas.Children.Add(EdgeArray.line[EdgeArray.edgeCount - 1]);
                            if(matrix[i,j]>1)
                                EdgeArray.edge[EdgeArray.edgeCount - 1] = new Edge(EdgeArray.line[EdgeArray.edgeCount - 1], vertex1, vertex2, matrix[i, j]);
                            else
                                EdgeArray.edge[EdgeArray.edgeCount - 1] = new Edge(EdgeArray.line[EdgeArray.edgeCount - 1], vertex1, vertex2);
                        }
                    }
                }
            }
            else
            {
                int rows = matrix.GetLength(0);
                int cols = matrix.GetLength(1);
                Vertex buffer = null;

                for(int i = 0; i < rows; i++)
                {
                    VertexArray.AddVertex(canvas, _rand.Next(1, 600), _rand.Next(1, 300));
                }

                for(int j = 0; j < cols; j++)
                {
                    for(int i = 0; i < rows; i++)
                    {
                        if (matrix[i, j] == 1)
                        {
                            if (buffer == null)
                                buffer = VertexArray.vertex[i];
                            else
                            {
                                EdgeArray.AddEdge(canvas, buffer, VertexArray.vertex[i]);
                                buffer = null;
                            }
                        }
                    }
                }
            }
        }
        private void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
        {
            
            var point = e.GetPosition(canvas); // Получение позиции курсора
            int x = Convert.ToInt32(point.X); // Перевод в координаты
            int y = Convert.ToInt32(point.Y);
            if (vertexRb.IsChecked == true)
            {
                if (GraphState.named)
                {
                    VertexArray.AddNamedVertex(canvas, x, y);
                }
                else
                {
                    VertexArray.AddVertex(canvas, x, y);
                }
            }
            if(edgeRb.IsChecked == true)
            {
                if (GraphState.focused)
                {
                    var point1 = e.GetPosition(canvas);
                    int X = Convert.ToInt32(point1.X);
                    int Y = Convert.ToInt32(point1.Y);
                    for (int i = 0; i <= VertexArray.vertexCount - 1; i++)// Проверка на принадлежность точки к какой-либо вершине
                    {
                        if (((X - VertexArray.vertex[i].X <= 15) && (X - VertexArray.vertex[i].X >= 0)) ||
                            ((VertexArray.vertex[i].X - X <= 15) && (VertexArray.vertex[i].X - X >= 0)))
                        {
                            if (((Y - VertexArray.vertex[i].Y <= 15) && (Y - VertexArray.vertex[i].Y >= 0)) ||
                            ((VertexArray.vertex[i].Y - Y <= 15) && (VertexArray.vertex[i].Y - Y >= 0)))
                            {
                                if (VertexArray.buffer[0] == null)
                                {
                                    VertexArray.buffer[0] = VertexArray.vertex[i];
                                }
                                else
                                {
                                    VertexArray.buffer[1] = VertexArray.vertex[i];
                                    EdgeArray.AddArrowedEdge(canvas, VertexArray.buffer[0], VertexArray.buffer[1]);
                                    VertexArray.buffer[0] = null;
                                    VertexArray.buffer[1] = null;
                                }
                            }
                        }
                    } // Конец проверки
                }
                else
                {
                    var point1 = e.GetPosition(canvas);
                    int X = Convert.ToInt32(point1.X);
                    int Y = Convert.ToInt32(point1.Y);
                    for (int i = 0; i <= VertexArray.vertexCount - 1; i++)// Проверка на принадлежность точки к какой-либо вершине
                    {
                        if ((((X - VertexArray.vertex[i].X <= 8) && (X - VertexArray.vertex[i].X >= 0)) ||
                             ((VertexArray.vertex[i].X - X <= 8) && (VertexArray.vertex[i].X - X >= 0))) && (((Y - VertexArray.vertex[i].Y <= 8) && (Y - VertexArray.vertex[i].Y >= 0)) ||
                                                                                                              ((VertexArray.vertex[i].Y - Y <= 8) && (VertexArray.vertex[i].Y - Y >= 0))))
                        {
                            if (VertexArray.buffer[0] == null)
                            {
                                VertexArray.buffer[0] = VertexArray.vertex[i];
                            }
                            else
                            {
                                if (!Vertex.Compare(VertexArray.buffer[0], VertexArray.vertex[i]))
                                {
                                    VertexArray.buffer[1] = VertexArray.vertex[i];
                                    EdgeCreateWindow edgeCreate = new EdgeCreateWindow();
                                    if (edgeCreate.ShowDialog() == true)
                                    {
                                        if (edgeCreate.State)
                                        {
                                            EdgeArray.AddArrowedEdge(canvas, VertexArray.buffer[0], VertexArray.buffer[1], edgeCreate.Color, edgeCreate.Weight);
                                        }
                                        else
                                        {
                                            EdgeArray.AddEdge(canvas, VertexArray.buffer[0], VertexArray.buffer[1], edgeCreate.Color, edgeCreate.Weight);
                                        }
                                    }
                                    VertexArray.buffer[0] = null;
                                    VertexArray.buffer[1] = null;
                                }
                            }
                        }
                    } // Конец проверки
                }
            }
        }

        // Очистка канваса
        private void ClearBtn_Click(object sender, RoutedEventArgs e)
        {
            ClearCanvas();
        }

        public void ClearCanvas()
        {
            canvas.Children.Clear();
            VertexArray.vertexCount = 0;
            EdgeArray.edgeCount = 0;
            Array.Clear(VertexArray.edge, 0, VertexArray.edge.Length);
            Array.Clear(VertexArray.ellipse, 0, VertexArray.ellipse.Length);
            Array.Clear(EdgeArray.edge, 0, EdgeArray.edge.Length);
            Array.Clear(EdgeArray.line, 0, EdgeArray.line.Length);
        }


        // Кнопки меню
        private void UnfocusedGraphBtn_Click(object sender, RoutedEventArgs e)
        {
            GraphState.focused = false;
        }

        private void FocusedGraphBtn_Click(object sender, RoutedEventArgs e)
        {
            GraphState.focused = true;
        }

        private void ExportPngBtn_Click(object sender, RoutedEventArgs e)
        {
            SaveFileDialog sfg = new SaveFileDialog
            {
                Filter = "PNG (*.png) | *.png"
            };
            sfg.ShowDialog();
            string filename = sfg.FileName;
            Rect rect = new Rect(canvas.Margin.Left, canvas.Margin.Top, canvas.ActualWidth, canvas.ActualHeight);

            double dpi = 96d;

            RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right, (int)rect.Bottom, dpi, dpi, PixelFormats.Default);

            rtb.Render(canvas);

            BitmapEncoder pngEncoder = new PngBitmapEncoder();
            pngEncoder.Frames.Add(BitmapFrame.Create(rtb));

            try
            {
                MemoryStream ms = new MemoryStream();

                pngEncoder.Save(ms);
                ms.Close();

                File.WriteAllBytes(filename, ms.ToArray());
            }
            catch (Exception err)
            {
                MessageBox.Show(err.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void DelEdgeBtn_Click(object sender, RoutedEventArgs e)
        {
            for(int i = 0; i < EdgeArray.edgeCount; i++)
            {
                canvas.Children.Remove(EdgeArray.line[i]);
            }
            EdgeArray.edgeCount = 0;
            Array.Clear(EdgeArray.edge, 0, EdgeArray.edge.Length);
        }

        private void PrintBtn_Click(object sender, RoutedEventArgs e)
        {
            PrintDialog printDialog = new PrintDialog();
            printDialog.PrintVisual(canvas, "Your graph");
        }

        private void NamedGraphBtn_Click(object sender, RoutedEventArgs e)
        {
            GraphState.named = true;
        }

        private void UnnamedGraphBtn_Click(object sender, RoutedEventArgs e)
        {
            GraphState.named = false;
        }

        private void AdjacencyMatrix_Click(object sender, RoutedEventArgs e)
        {
            int length = VertexArray.vertex.Length;
            int[,] matrix = new int[length, length];
            for(int i = 0; i < VertexArray.vertex.Length; i++)
            {
                var vertex1 = VertexArray.vertex[i];
                for(int j = 0; j < VertexArray.vertex.Length; j++)
                {
                    if (i != j)
                    {
                        var vertex2 = VertexArray.vertex[j];
                        for (int k = 0; k < EdgeArray.edge.Length; k++)
                        {
                            var edge = EdgeArray.edge[k];
                            if (Vertex.Compare(edge.FinishVertex, vertex1) && Vertex.Compare(edge.StartVertex, vertex2))
                                if (edge.Weight > 0)
                                    matrix[i, j] = (int)edge.Weight;
                                else
                                    matrix[i, j] = 1;
                            else if (Vertex.Compare(edge.StartVertex, vertex1) && Vertex.Compare(edge.FinishVertex, vertex2))
                                if (edge.Weight > 0)
                                    matrix[i, j] = (int)edge.Weight;
                        }
                    }
                    else
                    {
                        matrix[i, j] = 0;
                    }
                }
            }

            for (int i = 0; i < length; i++)
                for (int j = 0; j < length; j++)
                    matrix[i, j] = matrix[j, i];

            MatrixOutput output = new MatrixOutput(matrix);
            output.Show();
        }

        private void IncidenceMatrix_Click(object sender, RoutedEventArgs e)
        {
            var rows = VertexArray.vertex.Length;
            var cols = EdgeArray.edge.Length;
            var matrix = new int[rows, cols];
            for(var i = 0; i < rows; i++)
            {
                var vertex = VertexArray.vertex[i];

                for (var j = 0; j < cols; j++)
                {
                    var edge = EdgeArray.edge[j];
                    

                    if (Vertex.Compare(vertex, edge.FinishVertex) && edge.IsFocused)
                        matrix[i, j] = -1;
                    else
                    {
                        if (Vertex.Compare(vertex, edge.FinishVertex) || Vertex.Compare(vertex, edge.StartVertex))
                            if (edge.Weight > 0)
                                matrix[i, j] = (int)edge.Weight;
                            else
                                matrix[i, j] = 1;
                        else
                            matrix[i, j] = 0;
                    }
                }
            }
            var output = new MatrixOutput(matrix);
            output.Show();

        }

        private void CreateGraphByMatrixBtn_Click(object sender, RoutedEventArgs e)
        {
            GraphByMatrix matrix = new GraphByMatrix();
            matrix.Show();
            Close();
        }
    }

    public static class VertexArray
    {
        public static int vertexCount;
        public static Vertex[] vertex = new Vertex[vertexCount];
        public static Ellipse[] ellipse = new Ellipse[vertexCount];
        public static Edge[] edge = new Edge[vertexCount];
        public static Vertex[] buffer = new Vertex[2];
        public static Label[] label = new Label[vertexCount];

        public static int AddVertex(Canvas canvas, int x, int y)
        {

            vertexCount++;
            Array.Resize(ref vertex, vertexCount);
            Array.Resize(ref ellipse, vertexCount);
            vertex[vertexCount-1] = new Vertex(x, y);
            ellipse[vertexCount - 1] = new Ellipse
            {
                Height = 10,
                Width = 10,
                Fill = new SolidColorBrush(Color.FromRgb(0, 0, 0))
            };
            Canvas.SetTop(ellipse[vertexCount-1], vertex[vertexCount-1].Y);
            Canvas.SetLeft(ellipse[vertexCount-1], vertex[vertexCount-1].X);
            Panel.SetZIndex(ellipse[vertexCount - 1], 10);
            canvas.Children.Add(ellipse[vertexCount-1]);
            return vertexCount-1;
        }

        public static int AddNamedVertex(Canvas canvas, int x, int y)
        {
            vertexCount++;
            Array.Resize(ref vertex, vertexCount);
            Array.Resize(ref ellipse, vertexCount);
            Array.Resize(ref label, vertexCount);
            vertex[vertexCount - 1] = new Vertex(x, y);
            label[vertexCount - 1] = new Label
            {
                Foreground = new SolidColorBrush(Color.FromRgb(0, 0, 0)),
                FontSize = 18
            };
            ellipse[vertexCount - 1] = new Ellipse
            {
                Height = 10,
                Width = 10,
                StrokeThickness = 2,
                Stroke = new SolidColorBrush(Color.FromRgb(0, 0, 0))
            };


            ellipse[vertexCount - 1].Fill = ellipse[vertexCount - 1].Stroke;
            label[vertexCount - 1].Content = vertexCount.ToString();
            Canvas.SetTop(ellipse[vertexCount - 1], vertex[vertexCount - 1].Y);
            Canvas.SetLeft(ellipse[vertexCount - 1], vertex[vertexCount - 1].X);
            canvas.Children.Add(ellipse[vertexCount - 1]);
            Canvas.SetTop(label[vertexCount - 1], vertex[vertexCount - 1].Y+5);
            Canvas.SetLeft(label[vertexCount - 1], vertex[vertexCount - 1].X-15);
            canvas.Children.Add(label[vertexCount-1]);
            return vertexCount - 1;
        }
    }

    public class EdgeArray
    {
        public static int edgeCount;
        public static Edge[] edge = new Edge[edgeCount];
        public static Line[] line = new Line[edgeCount];
        public static Label[] label = new Label[edgeCount];
        public static ArrowLine[] ArrowLine = new ArrowLine[edgeCount];

        public static void AddEdge(Canvas canvas, Vertex v1, Vertex v2)
        {
            edgeCount++;
            Array.Resize(ref edge, edgeCount);
            Array.Resize(ref line, edgeCount);
            line[edgeCount-1] = new Line();
            edge[edgeCount-1] = new Edge(line[edgeCount-1], v1, v2);
            line[edgeCount - 1].Stroke = new SolidColorBrush(Color.FromRgb(0, 0, 0));
            line[edgeCount - 1].StrokeThickness = 4;
            line[edgeCount - 1].X1 = v1.X;
            line[edgeCount - 1].Y1 = v1.Y;
            line[edgeCount - 1].X2 = v2.X;
            line[edgeCount - 1].Y2 = v2.Y;
            Panel.SetZIndex(line[edgeCount-1], -5);
            canvas.Children.Add(line[edgeCount - 1]);
        }

        public static void AddEdge(Canvas canvas, Vertex v1, Vertex v2, Color lineColor, int weight)
        {
            edgeCount++;
            Array.Resize(ref edge, edgeCount);
            Array.Resize(ref line, edgeCount);
            Array.Resize(ref label, edgeCount);
            line[edgeCount - 1] = new Line
            {
                StrokeThickness = 2,
                X1 = v1.X,
                Y1 = v1.Y,
                Stroke = new SolidColorBrush(lineColor),
                Y2 = v2.Y,
                X2 = v2.X
            };
            edge[edgeCount - 1] = new Edge(line[edgeCount - 1], v1, v2) {Weight = weight};

            if (weight > 0)
            {
                Brush brush = Brushes.Black;
                label[edgeCount - 1] = new Label
                {
                    FontSize = 16,
                    Foreground = brush,
                    Background = Brushes.White,
                    BorderBrush = new SolidColorBrush(lineColor),
                    BorderThickness = new Thickness(2),
                    Content = weight.ToString()
                };
                double centerX = Edge.GetCenterByX(edge[edgeCount - 1]);
                double centerY = Edge.GetCenterByY(edge[edgeCount - 1]);
                Panel.SetZIndex(label[edgeCount - 1], 3);
                Canvas.SetLeft(label[edgeCount - 1], centerX);
                Canvas.SetTop(label[edgeCount - 1], centerY);
                canvas.Children.Add(label[edgeCount - 1]);
            }
            canvas.Children.Add(line[edgeCount - 1]);
        }

        public static void AddArrowedEdge(Canvas canvas, Vertex v1, Vertex v2)
        {
            edgeCount++;
            Array.Resize(ref ArrowLine, edgeCount);
            Array.Resize(ref line, edgeCount);
            ArrowLine[edgeCount - 1] = new ArrowLine();
            edge[edgeCount - 1] = new Edge(ArrowLine[edgeCount - 1], v1, v2);
            ArrowLine[edgeCount - 1].Stroke = new SolidColorBrush(Color.FromRgb(0, 0, 0));
            ArrowLine[edgeCount - 1].StrokeThickness = 2;
            ArrowLine[edgeCount - 1].X1 = v1.CenterByX;
            ArrowLine[edgeCount - 1].Y1 = v1.CenterByY;
            ArrowLine[edgeCount - 1].X2 = v2.CenterByX;
            ArrowLine[edgeCount - 1].Y2 = v2.CenterByY;
            canvas.Children.Add(ArrowLine[edgeCount - 1]);
        }

        public static void AddArrowedEdge(Canvas canvas, Vertex v1, Vertex v2, Color lineColor, int Weight)
        {
            edgeCount++;
            Array.Resize(ref edge, edgeCount);
            Array.Resize(ref line, edgeCount);
            Array.Resize(ref label, edgeCount);
            Array.Resize(ref ArrowLine, edgeCount);
            ArrowLine[edgeCount - 1] = new ArrowLine();
            edge[edgeCount - 1] = new Edge(ArrowLine[edgeCount - 1], v1, v2);
            ArrowLine[edgeCount - 1].Stroke = new SolidColorBrush(lineColor);
            ArrowLine[edgeCount - 1].StrokeThickness = 2;
            ArrowLine[edgeCount - 1].X1 = v1.CenterByX - 5;
            ArrowLine[edgeCount - 1].Y1 = v1.CenterByY - 5;
            ArrowLine[edgeCount - 1].X2 = v2.CenterByX - 2;
            ArrowLine[edgeCount - 1].Y2 = v2.CenterByY - 3;
            edge[edgeCount - 1].Weight = Weight;

            if (Weight > 0)
            {
                Brush brush = Brushes.Black;
                label[edgeCount - 1] = new Label
                {
                    FontSize = 16,
                    Foreground = brush,
                    Background = Brushes.White,
                    BorderBrush = new SolidColorBrush(lineColor),
                    BorderThickness = new Thickness(2),
                    Content = Weight.ToString()
                };
                var centerX = Edge.GetCenterByX(edge[edgeCount - 1]);
                var centerY = Edge.GetCenterByY(edge[edgeCount - 1]);
                Panel.SetZIndex(label[edgeCount - 1], 3);
                Canvas.SetLeft(label[edgeCount - 1], centerX);
                Canvas.SetTop(label[edgeCount - 1], centerY);
                canvas.Children.Add(label[edgeCount - 1]);
            }
            canvas.Children.Add(ArrowLine[edgeCount - 1]);
        }
    }
}
