using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
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;
using zocWpfCharts;

namespace OTDR
{
    // http://wpftoolkit.codeplex.com/
    // http://www.c-sharpcorner.com/uploadfile/mahesh/charting-in-wpf/
    // https://lvcharts.net/

    // http://stackoverflow.com/questions/741956/pan-zoom-image

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public Charts charts;
        private List<int> evntlocindexes = null;

        public MainWindow()
        {
            InitializeComponent();
            winCharts.WindowState = WindowState.Maximized;
            // vytvoření objektu pro grafy
            charts = new Charts(spnMain);
            // nastavení popisky osy X
            charts.axis.axeXdesc = "km";
            // nastavení popisky osy Y
            charts.axis.axeYdesc = "dB";
            // provádět zoom v ose X
            charts.bZoomByX = true;
            // provádět zoom v ose Y
            charts.bZoomByY = true;
            // krok o který se má zoomovat v ose X
            charts.dZoomStepX = 0.5;
            // krok o který se má zoomovat v ose Y
            charts.dZoomStepY = 0.5;

            tbcResults.SelectedIndex = 1;
            // napojení události vložení markeru do grafu
            charts.MarkerAdd += MarkerAddedEvent;
            // napojení události posunu markeru - obecného
            charts.MarkerMoved += MarkerMovedEvent;
            // napojení události posunu markeru A
            charts.MarkerAMoved += MarkerAMovedEvent;
            // napojení události posunu markeru B
            charts.MarkerBMoved += MarkerBMovedEvent;

            // povolit nástroj ruka jako výchozí
            MoveEnable();
        }

        private void btnOpenFile_Click(object sender, RoutedEventArgs e)
        {
            OpenFileAndDrawChart(1);
        }

        private void btnOpenFile2_Click(object sender, RoutedEventArgs e)
        {
            OpenFileAndDrawChart(2);
        }

        private void btnOpenFile3_Click(object sender, RoutedEventArgs e)
        {
            OpenFileAndDrawChart(3);
        }

        /// <summary>
        /// Otevře dialog pro načtení souboru s grafem a následně graf vykreslí
        /// </summary>
        /// <param name="nChartNum"></param>
        private void OpenFileAndDrawChart(int nChartNum)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "OTDR file types|*.sor;*.asc|SOR Files (*.sor)|*.sor|ASC Files (*.asc)|*.asc";
            if (openFileDialog.ShowDialog() == true)
            {
                // vytvoření nového grafu
                Chart chart = new Chart()
                {
                    num = charts.lstCharts.Count + 1, // pořadové číslo grafu
                    filePath = openFileDialog.FileName, // cesta k souboru s daty
                    fileName = openFileDialog.FileName.Substring(openFileDialog.FileName.LastIndexOf('\\') + 1), // název souboru s daty
                    color = rctColor.Fill, // barva, kterou se má daný graf vykreslit
                    parent = charts, // propojení grafu s hlavním objektem
                };
                // vyplnění textblocku odpovídajího souboru jeho názvem a nastavení barvy grafu
                TextBox txb = txbFile;
                switch (chart.num)
                {
                    case 2:
                        txb = txbFile2;
                        chart.color = rctColor2.Fill;
                        break;
                    case 3:
                        txb = txbFile3;
                        chart.color = rctColor3.Fill;
                        break;
                    default:
                        txb = txbFile;
                        chart.color = rctColor.Fill;
                        break;
                }
                txb.Text = chart.fileName;

                // podle typu souboru se volá metoda pro načtení dat
                switch (openFileDialog.FileName.Substring(openFileDialog.FileName.Length - 4))
                {
                    case ".sor":
                        SorParserFinal.SorParser sorparser = new SorParserFinal.SorParser(openFileDialog.FileName);
                        // provázání pomocných objektů s grafem
                        chart.externalObjects.Add("sorparser", sorparser);
                        // naplnění Listu bodů grafu
                        chart.lstPoints = GetChartPoints(sorparser, "sor");
                        // naplnění pole bodů zbavených šumu
                        chart.smootheddata = EdgeDetector1.EdgeDetector.smooth(sorparser.getArrDataPoints());
                        break;
                    case ".asc":
                        AscParser ascparser = new AscParser(openFileDialog.FileName);
                        chart.externalObjects.Add("ascparser", ascparser);
                        chart.lstPoints = GetChartPoints(ascparser, "asc");
                        break;
                    default:
                        return;
                }
                // přidání grafu do hlavního objektu
                charts.AddChart(chart);
                // vykreslení všech elementů hlavního objektu (grafy, osy, markery, ...)
                charts.Draw();
            }
        }

        /// <summary>
        /// Načte body pro graf ze souboru
        /// </summary>
        /// <param name="sFilePath">soubor odkud se načítají data grafu</param>
        public List<Tuple<double,double>> GetChartPoints(string sFilePath)
        {
            if (sFilePath.Length == 0)
            {
                return null;
            }
            List<Tuple<double, double>> lstResult = new List<Tuple<double, double>>();
            switch (sFilePath.Substring(sFilePath.Length - 4))
            {
                case ".sor":
                    SorParserFinal.SorParser myparser = new SorParserFinal.SorParser(sFilePath);
                    myparser.parse();
                    List<DataPoint> points = myparser.getDataPoints();
                    evntlocindexes = myparser.getEventLocIndex();
                    foreach (DataPoint dtp in points)
                        lstResult.Add(new Tuple<double, double>(dtp.x, dtp.y));
                    points.Clear();
                    break;
                case ".asc":
                    StreamReader sr = new StreamReader(sFilePath);
                    string sLine = sr.ReadLine();
                    bool bRead = false;
                    string[] arrPoint;
                    while (sLine != null)
                    {
                        // skip empty rows
                        if (sLine.Trim().Length == 0)
                        {
                            sLine = sr.ReadLine();
                            continue;
                        }
                        // starting area of points
                        if (sLine == "POS(km)  VAL(dB)")
                            bRead = true;
                        else
                        {
                            if (bRead)
                            {
                                arrPoint = sLine.Split(' ');
                                lstResult.Add(new Tuple<double, double>(double.Parse(arrPoint[0], CultureInfo.InvariantCulture), double.Parse(arrPoint[1], CultureInfo.InvariantCulture)));
                            }
                        }
                        sLine = sr.ReadLine();
                    }
                    break;
                    default:
                        return null;
            }
            return lstResult;
        }

        /// <summary>
        /// Načte body pro graf z parseru
        /// </summary>
        /// <param name="parser">parser</param>
        /// <param name="sObjType">typ parseru</param>
        /// <returns></returns>
        public List<Tuple<double, double>> GetChartPoints(object parser, string sObjType)
        {
            List<Tuple<double, double>> lstResult = new List<Tuple<double, double>>();
            switch (sObjType)
            {
                case "sor":
                    SorParserFinal.SorParser sorparser = (SorParserFinal.SorParser)parser;
                    sorparser.parse();
                    List<DataPoint> points = sorparser.getDataPoints();
                    if (ckbEvntsFromSor.IsChecked ?? false)
                    {
                        evntlocindexes = sorparser.getEventLocIndex();
                    }
                    else
                    {
                        evntlocindexes = EdgeDetector1.EdgeDetector.getEventLocIndex(sorparser.getArrDataPoints());
                    }

                    foreach (DataPoint dtp in points)
                        lstResult.Add(new Tuple<double, double>(dtp.x, dtp.y));
                    points.Clear();
                    break;
                case ".asc":
                    AscParser ascparser = (AscParser)parser;
                    ascparser.parse();
                    lstResult = ascparser.getEventLocIndex();
                    break;
                default:
                    return null;
            }
            return lstResult;
        }

        /// <summary>
        /// Metoda odstraní veškerá načtená data
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnClearCanvas_Click(object sender, RoutedEventArgs e)
        {
            txbFile.Text = "";
            txbFile2.Text = "";
            txbFile3.Text = "";
            // vymaže všechny objekty z třídy Charts
            charts.Clear();
            // odstraní informace o eventech ze záložky Events
            grdEvents.Children.Clear();
            grdMarkers.Visibility = Visibility.Hidden;
        }

        /// <summary>
        /// Metoda zruší zoom všech grafů
        /// nastaví výchozí scale a grafy znovu vykreslí
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnUnzoom_Click(object sender, RoutedEventArgs e)
        {
            // vyresetuje zoom pro všechny objekty ve třídě Charts
            charts.ZoomClear();
        }

        /// <summary>
        /// Akce vyvolaná stisknutím tlačítko "-" pro Zoom
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnZoomMinus_Click(object sender, RoutedEventArgs e)
        {
            // provede zoom všech objektů podle parametru dZoomStepX a dZoomStepY
            // vstupní parametr udává zda se má zmenšovat -1, nebo zvětšovat +1
            charts.ZoomUnzoom(-1);
        }

        /// <summary>
        /// Akce vyvolaná stisknutím tlačítko "+" pro Zoom
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnZoomPlus_Click(object sender, RoutedEventArgs e)
        {
            // provede zoom všech objektů podle parametru dZoomStepX a dZoomStepY
            // vstupní parametr udává zda se má zmenšovat -1, nebo zvětšovat +1
            charts.ZoomUnzoom(1);
        }


        /// <summary>
        /// překreslení grafů při změně rozměru okna
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            // překreslení objektů ve třídě Charts při změně rozměru okna
            charts.Resize();
        }


        private void btnEdges_Click(object sender, RoutedEventArgs e)
        {
            if (evntlocindexes != null)
                // vykreslení markerů podle indexů v seznamu bodů grafu pro zvolený graf
                charts.DrawEdgesByIndexes(0, evntlocindexes);
            else
                return;
                // vykreslení markerů podle pozic
//                charts.lstCharts[0].DrawEdges(charts.lstCharts[0].GetEdges());
            btnPdfExport.IsEnabled = true;
            GenEventsTable();
        }

        private void btnMoveEnable_Click(object sender, RoutedEventArgs e)
        {
            MoveEnable();
        }

        private void MoveEnable()
        {
            // povolení/zakázání posunu grafů nástrojem ruka
            charts.EnableMovement();
            // zjištění zda je objekt v módu posunu nástrojem ruka
            if (charts.MovementEnabled)
            {
                btnMoveEnable.Background = Brushes.LightGreen;
                btnMoveEnable.ToolTip = "Move on";
            }
            else
            {
                btnMoveEnable.Background = Brushes.LightGray;
                btnMoveEnable.ToolTip = "Move off";
            }
        }

        private void btnAddMarker_Click(object sender, RoutedEventArgs e)
        {
            // zjištění, zda je graf v módu vkládání markeru
            if (charts.bAddMarker)
                btnAddMarker.Background = Brushes.LightGray;
            else
                btnAddMarker.Background = Brushes.LightGreen;
            // zapnutí nebo vypnutí módu pro vkládání markeru
            charts.AddMarker();
        }

        private void GenEventsTable()
        {
            // jsou nějaké markery ?
            if (charts.lstCharts[0].lstEdgeLines.Count > 0)
            {
                grdEvents.ColumnDefinitions.Clear();
                grdEvents.RowDefinitions.Clear();
                // odstraním předchozí data v tabulce events
                grdEvents.Children.Clear();
                // vygeneruji počet řádků v tabulce, je napevno 7
                for (int i = 0; i < 7; i++) 
                    grdEvents.RowDefinitions.Add(new RowDefinition());
                // generuji počet sloupců, je závislý na počtu eventů a násobí se 2x, protože ke každému eventu je i sloupec section
                for (int i = 0; i < (charts.lstCharts[0].lstEdgeLines.Count*2); i++)
                    grdEvents.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(100), });
                double dLength, dLoss;
                Button btn;
                for (int i=0;i< charts.lstCharts[0].lstEdgeLines.Count;i++)
                {
                    // nastavení barvy pozadí sloupce Event
                    SetGridColBackground(grdEvents, i * 2, 7, Brushes.LightBlue);
                    GenTextToGridCell(grdEvents, string.Format("Event {0}", i+1), 0, i * 2);
                    btn = new Button();
                    btn.Content = "X";
                    btn.Foreground = Brushes.Red;
                    btn.ToolTip = "Remove Event";
                    btn.Tag = charts.lstCharts[0].lstEdgeLines[i].index;
                    btn.Width = 20;
                    btn.Height = 20;
                    btn.Background = Brushes.Transparent;
                    btn.BorderBrush = Brushes.Transparent;
                    btn.HorizontalAlignment = HorizontalAlignment.Right;
                    btn.Click += btnRemoveEvent_Click;
                    Grid.SetRow(btn, 0);
                    Grid.SetColumn(btn, i * 2);
                    grdEvents.Children.Add(btn);
                    GenTextToGridCell(grdEvents, "Distance (Km)", 1, i * 2);
                    GenTextToGridCell(grdEvents, charts.lstCharts[0].lstPoints[charts.lstCharts[0].lstEdgeLines[i].index].Item1.ToString("F4"), 2, i * 2, HorizontalAlignment.Right);
                    GenTextToGridCell(grdEvents, "Value (dB)", 3, i * 2);
                    GenTextToGridCell(grdEvents, charts.lstCharts[0].lstPoints[charts.lstCharts[0].lstEdgeLines[i].index].Item2.ToString("F4"), 4, i * 2, HorizontalAlignment.Right);

                    // pro poslední event už nebude section
                    if (charts.lstCharts[0].lstEdgeLines.Count - 1 > i)
                    {
                        // nastavení barvy pozadí sloupce Section
                        SetGridColBackground(grdEvents, i * 2 + 1, 7, Brushes.Beige);
                        GenTextToGridCell(grdEvents, "Section", 0, i * 2 + 1);
                        GenTextToGridCell(grdEvents, "Length (Km)", 1, i * 2 + 1);
                        dLength = (charts.lstCharts[0].lstPoints[charts.lstCharts[0].lstEdgeLines[i + 1].index].Item1 - charts.lstCharts[0].lstPoints[charts.lstCharts[0].lstEdgeLines[i].index].Item1);
                        GenTextToGridCell(grdEvents, dLength.ToString("F4"), 2, i * 2 + 1, HorizontalAlignment.Right);
                        GenTextToGridCell(grdEvents, "Loss (dB)", 3, i * 2 + 1);
                        dLoss = (charts.lstCharts[0].lstPoints[charts.lstCharts[0].lstEdgeLines[i].index].Item2 - charts.lstCharts[0].lstPoints[charts.lstCharts[0].lstEdgeLines[i + 1].index].Item2);
                        GenTextToGridCell(grdEvents, dLoss.ToString("F4"), 4, i * 2 + 1, HorizontalAlignment.Right);
                        GenTextToGridCell(grdEvents, "Att (dB/Km)", 5, i * 2 + 1);
                        GenTextToGridCell(grdEvents, (dLoss / dLength).ToString("F4"), 6, i * 2 + 1, HorizontalAlignment.Right);
                    }

                }
            }
        }

        /// <summary>
        /// Metoda pro aktualizaci tabulky s uživatelskými markery
        /// </summary>
        private void GenMarkersTable()
        {
            // povolit zobrazení
            if (grdMarkers.Visibility != Visibility.Visible)
                grdMarkers.Visibility = Visibility.Visible;
            // načíst hodnoty jednotlivých markerů
            // zjištění indexu daného markeru v daném grafu
            // charts.lstCharts[0].MarkerA.index
            double dMarkerAX = charts.lstCharts[0].lstPoints[charts.lstCharts[0].MarkerA.index].Item1;
            double dMarkerAY = charts.lstCharts[0].lstPoints[charts.lstCharts[0].MarkerA.index].Item2;
            double dMarkerBX = charts.lstCharts[0].lstPoints[charts.lstCharts[0].MarkerB.index].Item1;
            double dMarkerBY = charts.lstCharts[0].lstPoints[charts.lstCharts[0].MarkerB.index].Item2;
            // vypsat načtené hodnoty do tabulky
            txblMarkerAPos.Text = string.Format("{0:F2} km", dMarkerAX);
            txblMarkerAVal.Text = string.Format("{0:F2} dB", dMarkerAY);
            txblMarkerBPos.Text = string.Format("{0:F2} km", dMarkerBX);
            txblMarkerBVal.Text = string.Format("{0:F2} dB", dMarkerBY);
            txblMarkerABLoss.Text = string.Format("{0:F2} dB", dMarkerBY - dMarkerAY);
            txblMarkerABDist.Text = string.Format("{0:F2} km", dMarkerBX - dMarkerAX);
            txblMarkerABAtt.Text = string.Format("{0:F2} dB / km", (dMarkerBY - dMarkerAY) / (dMarkerBX - dMarkerAX));

        }

        /// <summary>
        /// Metoda pro odstranění markeru dané eventy
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnRemoveEvent_Click(object sender, RoutedEventArgs e)
        {
            // odstranění markeru s daným pořadovým číslem
            charts.RemoveMarker((int)((Button)sender).Tag);
            GenEventsTable();
        }

        private void MarkerAddedEvent(object sender, EventArgs e)
        {
            btnAddMarker.Background = Brushes.LightGray;
            GenEventsTable();
        }

        private void MarkerMovedEvent(object sender, EventArgs e)
        {
            GenEventsTable();
        }

        private void MarkerAMovedEvent(object sender, EventArgs e)
        {
            GenMarkersTable();
        }

        private void MarkerBMovedEvent(object sender, EventArgs e)
        {
            GenMarkersTable();
        }

        /// <summary>
        /// nastavení barvy pozadí daného sloupce 
        /// </summary>
        /// <param name="grid">grid ve kterém je daný sloupec</param>
        /// <param name="nColNum">číslo sloupce</param>
        /// <param name="nRowSpan">počet řádků tabulky v gridu</param>
        /// <param name="color">barva, která se má nastavit do pozadí</param>
        private void SetGridColBackground(Grid grid, int nColNum, int nRowSpan, Brush color)
        {
            Rectangle rect = new Rectangle();
            rect.Fill = color;
            Grid.SetColumn(rect, nColNum);
            Grid.SetRowSpan(rect, nRowSpan);
            grdEvents.Children.Add(rect);

        }

        private void GenTextToGridCell(Grid grid, string sText, int nRowNum, int nColNum)
        {
            GenTextToGridCell(grid, sText, nRowNum, nColNum, HorizontalAlignment.Left);
        }

        private void GenTextToGridCell(Grid grid, string sText, int nRowNum, int nColNum, HorizontalAlignment halgn)
        {
            TextBlock txbl = new TextBlock() { Text = sText, HorizontalAlignment = halgn };
            if (halgn == HorizontalAlignment.Left)
                txbl.Margin = new Thickness(5, 0, 0, 0);
            if (halgn == HorizontalAlignment.Right)
                txbl.Margin = new Thickness(0, 0, 5, 0);

            Grid.SetColumn(txbl, nColNum);
            Grid.SetRow(txbl, nRowNum);
            grid.Children.Add(txbl);
        }

        private void btnPdfExport_Click(object sender, RoutedEventArgs e)
        {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.DefaultExt = ".pdf";
            dlg.Filter = "PDF documents (.pdf)|*.pdf";
            if (dlg.ShowDialog() == true)
            {
                PDFExport pdfEx = new PDFExport();
                pdfEx.GenToPDF(dlg.FileName, charts);
            }
        }

        private void btnInfoFile_Click(object sender, RoutedEventArgs e)
        {
            char sId = ((Button)sender).Name[((Button)sender).Name.Length - 1];
            int nChartIndex = int.Parse(sId.ToString());
            if (charts.lstCharts.Count < nChartIndex)
                return;
            if (charts.lstCharts[nChartIndex - 1].externalObjects == null || !charts.lstCharts[nChartIndex - 1].externalObjects.ContainsKey("sorparser"))
                return;
            Window wnd = new Window();
            wnd.Title = charts.lstCharts[nChartIndex - 1].fileName;
            wnd.Width = 250;
            wnd.Height = 350;
            StackPanel spnl = new StackPanel();
            wnd.Content = spnl;
            TextBlock txb;
            txb = new TextBlock();
            txb.Margin = new Thickness(5);
            txb.Text = ((SorParserFinal.SorParser)charts.lstCharts[nChartIndex-1].externalObjects["sorparser"]).getFixedParamsText();
            spnl.Children.Add(txb);
            wnd.ShowDialog();

        }

        private void ckbSmoothData_Checked(object sender, RoutedEventArgs e)
        {
            // mají se používat pro vykreslování data zbavená šumu?
            charts.bUseSmoothedData = ckbSmoothData.IsChecked ?? false;
        }
    }
}
