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.Controls.Ribbon;
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 System.IO;
using Microsoft.Win32;

namespace TextEditor
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : RibbonWindow
    {
        private bool dirty;
        private string filename;

        private OpenFileDialog openFileDialog;
        private SaveFileDialog saveFileDialog;

        public MainWindow()
        {
            InitializeComponent();
            richTextBox.Document.Blocks.Clear();          // RichTextBox leeren
            dirty = false; ;                              // keine ungespeicherten Änderungen
            filename = null;
            setFormTitle();


            // Befehle für Anwendungsmenü registrieren
            CommandBindings.Add(new CommandBinding(ApplicationCommands.New,
                                                   miFileNew_Handler));
            CommandBindings.Add(new CommandBinding(ApplicationCommands.Open,
                                                   miFileOpen_Handler));
            CommandBindings.Add(new CommandBinding(ApplicationCommands.Save,
                                                   miFileSave_Handler));
            CommandBindings.Add(new CommandBinding(ApplicationCommands.SaveAs,
                                                   miFileSaveAs_Handler));
            CommandBindings.Add(new CommandBinding(ApplicationCommands.Print,
                                                   miFilePrint_Handler));
            CommandBindings.Add(new CommandBinding(ApplicationCommands.Close,
                                                   miFileQuit_Handler));
            ApplicationCommands.Close.InputGestures.Add((new KeyGesture(Key.B, ModifierKeys.Control, "Strg+B")));

            // Befehle für Anwendungsmenü mit Oberflächenelementen verbinden
            miFileNew.Command = ApplicationCommands.New;
            miFileOpen.Command = ApplicationCommands.Open;
            miFileSave.Command = ApplicationCommands.Save;
            miFileSaveAs.Command = ApplicationCommands.SaveAs;
            miFilePrint.Command = ApplicationCommands.Print;
            miFileQuit.Command = ApplicationCommands.Close;
            btnSave.Command = ApplicationCommands.Save;

            // Befehle für Zwischenablage mit Oberflächenelementen verbinden
            richTextBox.CommandBindings.Add(new CommandBinding(ApplicationCommands.Cut,
                                                               cmiEditCut_Handler,
                                                               cmiEditCut_CanExecHandler));
            richTextBox.CommandBindings.Add(new CommandBinding(ApplicationCommands.Copy,
                                                               cmiEditCopy_Handler,
                                                               cmiEditCopy_CanExecHandler));
            richTextBox.CommandBindings.Add(new CommandBinding(ApplicationCommands.Paste,
                                                               cmiEditPaste_Handler,
                                                               cmiEditPaste_CanExecHandler));
            btnCut.Command = ApplicationCommands.Cut;
            btnCopy.Command = ApplicationCommands.Copy;
            btnPaste.Command = ApplicationCommands.Paste;

            // Kontextmenü
            cmiEditCut.Command = ApplicationCommands.Cut;
            cmiEditCopy.Command = ApplicationCommands.Copy;
            cmiEditPaste.Command = ApplicationCommands.Paste;


            // Erzeugung und Konfiguration der Öffnen- und Speichern-Dialoge
            openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "Textdateien (*.txt)|*.txt|CS-Dateien (*.cs)|*.cs";
            openFileDialog.FilterIndex = 1;
            openFileDialog.Multiselect = false;
            openFileDialog.RestoreDirectory = true;

            saveFileDialog = new SaveFileDialog();
            saveFileDialog.Filter = "Textdateien (*.txt)|*.txt|CS-Dateien (*.cs)|*.cs";
            saveFileDialog.FilterIndex = 1;
            saveFileDialog.RestoreDirectory = true;


            // Installierte Schriftarten in Kombinationsfeld einlesen
            List<System.Windows.Media.FontFamily> col = Fonts.SystemFontFamilies.OrderBy(font => font.ToString()).ToList();
            fontsMenu.ItemsSource = col;
            fontsMenu.DisplayMemberPath = "Source";

            // Entweder Arial oder die erste Schriftart in der Liste auswählen
            if (col.Contains(new FontFamily("Arial")))
            {
                int index = col.FindIndex(delegate(FontFamily font) { if (font.ToString().Contains("Arial")) return true; else return false; });
                cbFonts.SelectedItem = col.ElementAt(index);
            }
            else
            {
                cbFonts.SelectedItem = col.ElementAt(0);
            }

            // Dem Textfeld den Fokus zuweisen
            richTextBox.Focus();
        }


        #region Hilfsmethoden zur Dokumentverwaltung

        // Prüft, ob es zu Änderungen gibt. Wenn ja, wird der Benutzer gefragt, 
        // ob er speichern möchte
        private bool nothingToSave()
        {
            if (!dirty)
            {
                return true;
            }

            switch (MessageBox.Show("Änderungen speichern?", "Texteditor",
                                    MessageBoxButton.YesNoCancel))
            {
                case MessageBoxResult.Yes: return save(filename);
                case MessageBoxResult.No: return true;
                case MessageBoxResult.Cancel:
                default: return false;
            }
        }

        // trägt den Namen des geladenen Dokuments in die Titelleiste ein
        private void setFormTitle()
        {
            string title;

            if (filename == null)
                title = "Unbenannt";
            else
            {
                // Nur den reinen Dateinamen ohne Pfad übernehmen
                title = filename.Substring(filename.LastIndexOf('\\') + 1);
            }

            this.Title = "Texteditor - " + title;
        }

        // Textänderungen erkennen
        private void richTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (!dirty)
            {
                dirty = true;
            }
        }

        #endregion


        #region Öffen und Speichern

        void miFileNew_Handler(object sender, ExecutedRoutedEventArgs args)
        {
            if (nothingToSave())
            {
                richTextBox.Document.Blocks.Clear();
                filename = null;
                dirty = false;
                setFormTitle();
                sbInfo.Content = "Neues Dokument wurde angelegt";
            }
        }

        void miFileOpen_Handler(object sender, ExecutedRoutedEventArgs args)
        {
            if (!nothingToSave())
                return;

            if (openFileDialog.ShowDialog() == true)
            {
                // Dateiinhalt einlesen
                open(openFileDialog.FileName);
            }
        }
        
        private void open(string filename)
        {
            try
            {
                if (File.Exists(filename))
                {
                    using (StreamReader streamReader = File.OpenText(filename))
                    {
                        Paragraph paragraph = new Paragraph(new Run(streamReader.ReadToEnd()));
                        richTextBox.Document.Blocks.Clear();
                        richTextBox.Document.Blocks.Add(paragraph);
                    }
                    // Geht auch, ist aber sehr langsam, wenn eine etwas größere Textdatei geladen wird
                    //TextRange text = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
                    //FileStream fs = new FileStream(filename, FileMode.OpenOrCreate);
                    //text.Load(fs, DataFormats.Text);
                    //fs.Close();

                    // Statusleiste und interne Felder aktualisieren
                    this.filename = filename;
                    dirty = false;
                    setFormTitle();
                    sbInfo.Content = filename + " wurde geöffnet.";
                }
            }
            catch (IOException)
            {
                MessageBox.Show("Datei konnte nicht geöffnet werden.", "Fehler");
            }
        }

        void miFileSave_Handler(object sender, ExecutedRoutedEventArgs args)
        {
            save(filename);
        }
        
        void miFileSaveAs_Handler(object sender, ExecutedRoutedEventArgs args)
        {
            save(null);
        }

        private bool save(String filename)
        {
            if (filename == null)
            {
                if (saveFileDialog.ShowDialog() == true)
                    filename = saveFileDialog.FileName;
                else
                    return false;
            }

            try
            {
                TextRange text = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
                FileStream fs = new FileStream(filename, FileMode.Create);
                text.Save(fs, DataFormats.Text);
                fs.Close();

                // Fensteroberfläche und interne Felder aktualisieren
                this.filename = filename;
                dirty = false;
                setFormTitle();
                sbInfo.Content = filename + " wurde gespeichert.";

                return true;
            }
            catch (IOException)
            {
                MessageBox.Show("Datei konnte nicht gespeichert werden.", "Fehler");
            }

            return false;
        }

        #endregion


        #region Drucken

        void miFilePrint_Handler(object sender, ExecutedRoutedEventArgs args)
        {
            PrintDialog printDialog = new PrintDialog();

            if (printDialog.ShowDialog() == true)
            {
                TextRange text = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
                MemoryStream ms = new MemoryStream();
                text.Save(ms, DataFormats.Rtf);

                // FlowDocument anlegen
                FlowDocument fd = new FlowDocument();
                TextRange copyDocumentRange = new TextRange(fd.ContentStart, fd.ContentEnd);
                copyDocumentRange.Load(ms, DataFormats.Rtf);

                fd.ColumnWidth = double.PositiveInfinity;
                printDialog.PrintDocument((((IDocumentPaginatorSource)fd).DocumentPaginator), filename);
            }

        }

        #endregion


        #region Beenden
        void miFileQuit_Handler(object sender, ExecutedRoutedEventArgs args)
        {
            if (nothingToSave())
            {
                this.Close();
            }
        }

        #endregion


        #region Drag&Drop-Unterstützung
        
        private void richTextBox_PreviewDragOver(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effects = DragDropEffects.Copy;
            else
                e.Effects = DragDropEffects.None;

            e.Handled = true;
        }
        
        private void richTextBox_PreviewDrop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                string[] filenames = (string[])e.Data.GetData(DataFormats.FileDrop);
                if (filenames.Length != 0)
                    open(filenames[0]);
            }
            e.Handled = true;
        }

        #endregion


        #region Zwischenablage-Unterstützung

        private void cmiEditCut_Handler(object sender, RoutedEventArgs e)
        {
            if (!richTextBox.Selection.IsEmpty)
            {
                Clipboard.SetText(richTextBox.Selection.Text);
                richTextBox.Selection.Text = "";
            }
        }
        
        private void cmiEditCopy_Handler(object sender, RoutedEventArgs e)
        {
            if (!richTextBox.Selection.IsEmpty)
            {
                Clipboard.SetData(DataFormats.Text, richTextBox.Selection.Text);
            }
        }
        
        private void cmiEditPaste_Handler(object sender, RoutedEventArgs e)
        {
            if (Clipboard.ContainsText() == true)
            {
                TextPointer pos = richTextBox.Selection.Start;
                pos.InsertTextInRun(Clipboard.GetText());
            }
        }

        // Einfügen-Befehl soll nur aktiviert sein, wenn Text ausgewählt ist
        // Soll der Befehl aktiviert sein, setzen wir args.CanExecute auf true
        private void cmiEditCut_CanExecHandler(object sender, CanExecuteRoutedEventArgs args)
        {
            args.CanExecute = !richTextBox.Selection.IsEmpty;
        }
        
        // Kopieren-Befehl soll nur aktiviert sein, wenn Text ausgewählt ist
        // Soll der Befehl aktiviert sein, setzen wir args.CanExecute auf true
        private void cmiEditCopy_CanExecHandler(object sender, CanExecuteRoutedEventArgs args)
        {
            args.CanExecute = !richTextBox.Selection.IsEmpty;
        }
        
        // Einfügen-Befehl soll nur aktiviert sein, wenn in Zwischenablage Text vorhanden ist
        // Soll der Befehl aktiviert sein, setzen wir args.CanExecute auf true
        private void cmiEditPaste_CanExecHandler(object sender, CanExecuteRoutedEventArgs args)
        {
            args.CanExecute = Clipboard.ContainsText();
        }

        #endregion


        #region Schrftart-Auswahl

        // Die ausgewählte Schriftart auf gesamten Text anwenden
        private void cbFonts_SelectionChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            FontFamily font = cbFonts.SelectedItem as FontFamily;
            if (font != null)
            {
                TextRange text = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
                text.ApplyPropertyValue(TextElement.FontFamilyProperty, font);
            }
        }

        #endregion


        #region Textsuche

        private void btnSearch_Click_1(object sender, RoutedEventArgs e)
        {
                // Textstelle suchen
                TextRange foundRange = GetTextRangeFromPosition(richTextBox.CaretPosition, tbSearch.Text);

                // Wenn ein Vorkommen gefunden, dann Stelle hervorheben
                if (foundRange != null)
                {
                    richTextBox.Selection.Select(foundRange.Start, foundRange.End);
                }
        }

        public TextRange GetTextRangeFromPosition(TextPointer pos, String searchText)
        {
            TextRange tr = null;

            while (pos != null)
            {
                // Abbrechen, wenn das Textende erreicht wurde
                if (pos.CompareTo(richTextBox.Document.ContentEnd) == 0)
                {
                    break;
                }

                // Nur in Text suchen
                if (pos.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
                {
                    // Text bis zum nächsten Nicht-Text-Element lesen
                    String text = pos.GetTextInRun(LogicalDirection.Forward);

                    // in Text nach Suchbegriff suchen
                    int index = text.IndexOf(searchText);

                    // Wenn Vorkommen gefunden, dann Position in RichTextBox ermitteln, Suchbegriff in tr speichern und Schleife verlassen
                    if (index >= 0)
                    {
                        pos = pos.GetPositionAtOffset(index);
                        TextPointer nextPointer = pos.GetPositionAtOffset(searchText.Length);
                        tr = new TextRange(pos, nextPointer);
                        break;
                    }
                    else
                    {
                        pos = pos.GetPositionAtOffset(text.Length);
                    }
                }
                else
                {
                    // Nicht-Text-Elemente übergehen
                    pos = pos.GetNextContextPosition(LogicalDirection.Forward);
                }
            }

            return tr;
        }

        #endregion


    }
}
