using System;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Navigation;
using System.Windows.Threading;

using BeWo.Core;
using BeWo.ViewModel;
using BeWo.ServiceProxy;

using BS.Shared.DataContracts;
using BS.Shared.Extensions;
using BS.Shared;
using BS.Shared.Core;

using Microsoft.Win32;

namespace BeWo.View
{
    public partial class BeWoFileView
    {
        private TreeViewItem _SelectedTreeNode;

        private AbstractBaseVM _ViewModel;

        private object drag;

        private Point _MouseDownPosition = new Point(-1, -1);

        private readonly long? _ObjectOid;

        private readonly TableID _ObjectTid;

        private String _UploadFileName;

        private BeWoFolderVM selectedVM;

        public BeWoFileView(TableID pObjectTid, long? pObjectOid)
        {
            InitializeComponent();

            CursorControl.Visibility = Visibility.Collapsed;
            _ObjectOid = pObjectOid;
            _ObjectTid = pObjectTid;

            ServiceFacade.DoOperationsServiceAsync(
                s => s.GetFolderTree(_ObjectTid, _ObjectOid ?? 0),
                r => this.Dispatch(
                    delegate
                    {
                        if (r != null)
                        {
                            var vmList = r.Select(item => new BeWoFolderVM(item)).ToList();
                            if (vmList.Count == 0)
                            {
                                //erzeuge root
                            }

                            ViewModel = vmList[0];
                            treeview.DataContext = vmList;

                            var treeviewitem = treeview.Items[0];

                            if (treeviewitem != null)
                            {
                                var container = treeview.ItemContainerGenerator.ContainerFromItem(treeviewitem);
                                var ci = container as TreeViewItem;

                                if (ci != null)
                                {
                                    ci.IsSelected = true;
                                    ci.IsExpanded = true;
                                }
                            }

                        }
                    }));


            //BeWoFolderVM vm = new BeWoFolderVM(new BeWoFolderDC()) { Name = "root" };

            //for (int i = 0; i < 10; i++)
            //{
            //    vm.SubFolders.Add(new BeWoFolderVM(new BeWoFolderDC()) { Name = "Folder " + i, ParentFolder = vm });
            //}

            //for (int i = 0; i < 10; i++)
            //{
            //    for (int j = 0; j < 10; j++)
            //    {
            //        vm.SubFolders[i].Files.Add(new FileAttachmentVM(new FileAttachmentDC()) { FileName = "File " + i + " " + j, Folder = vm.SubFolders[i] });
            //    }
            //}



            PreviewMouseLeftButtonDown += treeview_PreviewMouseLeftButtonDown;
            PreviewMouseLeftButtonUp   += treeview_PreviewMouseLeftButtonUp;
            PreviewMouseMove           += treeview_PreviewMouseMove;
        }

        public override bool IsDirty
        {
            get
            {
                return ViewModel != null && ViewModel.IsDirty;
            }
        }

        private AbstractBaseVM ViewModel
        {
            get { return _ViewModel; }

            set
            {
                DataContext = value;
                _ViewModel = value;
            }
        }

        private static void UpdateFile(FileAttachmentVM file)
        {
            ServiceFacade.DoOperationsServiceAsync(
                        s => s.UpdateFileAttachment(file.CommitToDataContract()),
                        r => file.DataContract.FileAttachmentVersion = r.FileAttachmentVersion);
        }

        private void InsertFolder(BeWoFolderVM folder)
        {
            ServiceFacade.DoOperationsServiceAsync(
                            s => s.InsertNewFolder(folder.CommitToDataContract()),
                            r =>
                            {
                                folder.DataContract.BeWoFolderOid = r;
                                folder.DataContract.BeWoFolderVersion = 1;
                            });
        }

        private static void UpdateFolder(BeWoFolderVM folder)
        {
            ServiceFacade.DoOperationsServiceAsync(
                            s => s.UpdateFolder(folder.CommitToDataContract()),
                            r => folder.DataContract.BeWoFolderVersion = r.BeWoFolderVersion);
        }

        private static void DeleteFolder(BeWoFolderVM folder)
        {
            if (folder.SubFolders != null)
            {
                foreach (var subfolder in folder.SubFolders)
                {
                    DeleteFolder(subfolder);
                }
            }

            if (folder.ParentFolder != null && folder.Files != null)
            {
                foreach (var file in folder.Files)
                {
                    folder.ParentFolder.Files.Add(file);
                    file.Folder = folder.ParentFolder;
                }
            }

            ServiceFacade.DoOperationsServiceSync(s => s.DeleteFolder(folder.DataContract.BeWoFolderOid.Value, folder.DataContract.BeWoFolderVersion.Value));
        }

        private void DeleteButton_Click(object sender, RoutedEventArgs e)
        {
            var lDC = this.datagrid_documents.GetCurrentValue<FileAttachmentVM>();

            if (lDC == null)
            {
                return;
            }

            if (MessageBox.Show("ACHTUNG: Das Löschen von Dateien kann nicht rückgängig gemacht werden!\nWollen Sie das Dokument '" + lDC.FileName + "' wirklich löschen?", "Löschen bestätigen", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.No)
            {
                return;
            }

            BeWoFolderVM parentFolder = lDC.Folder;

            if (lDC.DataContract.FileAttachmentOid.HasValue)
            {
                ServiceFacade.DoOperationsServiceAsync(s => s.DeleteFileAttachment(lDC.DataContract.FileAttachmentOid.Value, lDC.DataContract.FileAttachmentVersion.Value));
            }

            if (parentFolder != null)
                parentFolder.Files.Remove(lDC);

            BeWoWpfUtils.RefreshDXGrid(this.datagrid_documents);
        }

        private void ContextMenu_Click(object sender, RoutedEventArgs e)
        {
            if (e.OriginalSource is MenuItem)
            {
                var twi = e.OriginalSource as MenuItem;

                if (twi.Header.Equals("Neu"))
                {
                    var item = this.treeview.SelectedItem as BeWoFolderVM;
                    var newItem = new BeWoFolderVM(new BeWoFolderDC()) { Name = "Neuer Ordner" };

                    this._SelectedTreeNode.ItemContainerGenerator.StatusChanged += this.ItemContainerGenerator_StatusChanged;
                    item.SubFolders.Add(newItem);
                    newItem.ParentFolder = item;
                    InsertFolder(newItem);
                    this._SelectedTreeNode.IsExpanded = true;
                }
                else if (twi.Header.Equals("Umbenennen"))
                {
                    this.EditSelected();
                }
                else if (twi.Header.Equals("Löschen"))
                {
                    var child = this.treeview.SelectedItem as BeWoFolderVM;

                    if (MessageBox.Show("ACHTUNG: Das Löschen von Ordnern kann nicht rückgängig gemacht werden.\n" +
                                        "Alle enthaltenen Dokumente bleiben erhalten und werden in den obersten Ordner \"Alle Ordner\" verschoben.\n\n" +
                                        "Wollen Sie den Ordner '" + child.Name + "' wirklich löschen?", "Löschen bestätigen", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
                    {

                        var p = ItemsControl.ItemsControlFromItemContainer(this._SelectedTreeNode) as TreeViewItem;
                        p.IsSelected = true;
                        var parent = this.treeview.SelectedItem as BeWoFolderVM;
                        parent.SubFolders.Remove(child);
                        DeleteFolder(child);
                    }
                }
            }
        }

        private void EditSelected()
        {
            var textBox = BeWoWpfUtils.FindVisualChild<TextBox>(this._SelectedTreeNode);
            textBox.IsReadOnly = false;
            textBox.BorderThickness = new Thickness(1);
            textBox.Background = Brushes.White;
            textBox.Tag = treeview.SelectedItem;
            textBox.Focus();
            textBox.SelectAll();
        }

        private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
        {
            var ig = (ItemContainerGenerator) sender;
            if (ig.Status != GeneratorStatus.ContainersGenerated)
            {
                return;
            }

            var n = ig.ContainerFromIndex(_SelectedTreeNode.Items.Count - 1) as TreeViewItem;
            n.IsSelected = true;

            new DispatcherTimer(
                TimeSpan.FromMilliseconds(200),
                DispatcherPriority.Background,
                (s1, e1) =>
                {
                    _SelectedTreeNode = n;
                    EditSelected();
                    ((DispatcherTimer) s1).Stop();
                },
                Dispatcher).Start();

            _SelectedTreeNode.ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged;
        }

        private void TextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Return)
            {
                UpdateSelectedTreeItemText((TextBox)sender);
            }
        }

        private void TextBox_LostFocus(object sender, RoutedEventArgs e)
        {
            UpdateSelectedTreeItemText((TextBox)sender);
        }

        private static void UpdateSelectedTreeItemText(TextBox textBox)
        {
            if (!textBox.IsReadOnly)
            {
                textBox.Background = Brushes.Transparent;
                textBox.IsReadOnly = true;
                textBox.BorderThickness = new Thickness(0);

                if (textBox.Tag != null)
                {

                    var folder = textBox.Tag as BeWoFolderVM;
                    if (folder != null)
                    {
                        folder.Name = textBox.Text;
                        if (folder.IsDirty)
                        {
                            UpdateFolder(folder);
                        }
                    }
                }
            }
        }

        private void TreeViewItem_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            var source = e.OriginalSource as DependencyObject;
            while (source != null && !(source is TreeViewItem))
            {
                source = VisualTreeHelper.GetParent(source);
            }

            if (source != null)
            {
                ((TreeViewItem)source).IsSelected = true;
                _SelectedTreeNode = (TreeViewItem)source;
            }
            else
            {
                _SelectedTreeNode = null;
            }
        }

        private void treeview_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            drag = treeview.GetDataItemUnderMouse<BeWoFolderVM>(e) ?? datagrid_documents.GetRow(datagrid_documents.View.GetRowHandleByMouseEventArgs(e));

            _MouseDownPosition = e.GetPosition(this);
        }

        private void treeview_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (drag == null)
            {
                return;
            }

            var node = drag as BeWoFolderVM;
            var file = drag as FileAttachmentVM;
            drag = null;

            CursorControl.Visibility = Visibility.Collapsed;
            //Mouse.SetCursor(Cursors.Arrow);

            var newParent = treeview.GetDataItemUnderMouse<BeWoFolderVM>(e);

            if (newParent != null)
            {
                if (node != null)
                {
                    var children = node.SubFolders.FlatOut(d => d.SubFolders).ToList();

                    if (newParent.Equals(node) || node.ParentFolder == null || children.Contains(newParent))
                    {
                        return;
                    }

                    node.ParentFolder.SubFolders.Remove(node);
                    newParent.SubFolders.Add(node);
                    node.ParentFolder = newParent;

                    UpdateFolder(node);
                }
                else if (file != null)
                {
                    file.Folder.Files.Remove(file);
                    newParent.Files.Add(file);
                    file.Folder = newParent;

                    UpdateFile(file);


                }
            }
            _MouseDownPosition = new Point(-1, -1);
        }

        private void treeview_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (this.drag != null && _MouseDownPosition.X >= 0 && _MouseDownPosition.Y >= 0)
            {
                Point p = e.GetPosition(this);
                int distX = (int)Math.Abs(p.X - _MouseDownPosition.X);
                int distY = (int)Math.Abs(p.Y - _MouseDownPosition.Y);
                if (distX > 10 || distY > 10)
                {
                    //Mouse.SetCursor(Cursors.Hand);
                    Rect bounds = VisualTreeHelper.GetDescendantBounds(this);

                    if ((bounds.Top < p.Y) && (bounds.Bottom > p.Y))
                    {
                        Canvas.SetTop(this.CursorControl, p.Y);
                    }

                    if ((bounds.Left < p.X) && (bounds.Right > p.X))
                    {
                        Canvas.SetLeft(this.CursorControl, p.X);
                    }

                    this.CursorControl.Visibility = Visibility.Visible;

                    if (drag is BeWoFolderVM)
                    {
                        imageFolder.Visibility = System.Windows.Visibility.Visible;
                        imageFile.Visibility = System.Windows.Visibility.Hidden;
                        BeWoFolderVM folder = drag as BeWoFolderVM;
                        labelFrom.Text = String.Format("Verzeichnis {0}", folder.Name);
                    }
                    else if (drag is FileAttachmentVM)
                    {
                        imageFolder.Visibility = System.Windows.Visibility.Hidden;
                        imageFile.Visibility = System.Windows.Visibility.Visible;
                        FileAttachmentVM file = drag as FileAttachmentVM;
                        labelFrom.Text = String.Format("Datei {0}", file.FileName);
                    }
                    BeWoFolderVM targetFolder = this.treeview.GetDataItemUnderMouse<BeWoFolderVM>(e);


                    if (targetFolder != null)
                        labelTo.Text = String.Format("-> Nach {0} verschieben", targetFolder.Name);
                    else
                        labelTo.Text = "";
                }
            }
        }

        private void button_uploadDocument_Click(object sender, RoutedEventArgs pe)
        {
            if (!String.IsNullOrEmpty(popupedit_file.Text))
            {
                long? bewoFolderOid = null;

                BeWoFolderVM folder = GetSelectedFolder();
                if (folder != null)
                    bewoFolderOid = folder.DataContract.BeWoFolderOid;

                try
                {
                    var upload = new ProgressStream(File.OpenRead(_UploadFileName));
                    upload.ProgressChanged += (s, e) => Dispatcher.BeginInvoke(
                        DispatcherPriority.Normal,
                        (Action)(() =>
                        {
                            progressbar_upload.Value = e.Data;
                            text_progress.Content = string.Format("Bitte warten... {0:00}%", e.Data * 100);
                        }));

                    var lUlPackage = new UploadPackage(bewoFolderOid, 0, popupedit_file.Text, textbox_fileNotice.Text, _ObjectOid ?? 0, (int)_ObjectTid, upload);

                    ServiceFacade.DoStreamingServiceAsync(
                        s => s.UploadDocument(lUlPackage),
                        r => this.Dispatch(
                            delegate
                            {
                                selectedVM = (BeWoFolderVM)treeview.SelectedItem;
                                var newFileVM = new FileAttachmentVM(r.Attachment);
                                selectedVM.Files.Add(newFileVM);
                                newFileVM.Folder = selectedVM;

                                progressbar_upload.Value = 0;
                                text_progress.Content = string.Empty;

                                _UploadFileName = null;
                                button_uploadDocument.IsEnabled = false;
                                popupedit_file.Text = string.Empty;
                                textbox_fileNotice.Text = string.Empty;

                                upload.Dispose();
                            }),
                                () => this.Dispatch(
                                    delegate
                                    {

                                        progressbar_upload.Value = 0;
                                        text_progress.Content = string.Empty;

                                        upload.Dispose();
                                    }));
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Exclamation);
                }


            }
        }

        private BeWoFolderVM GetSelectedFolder()
        {
            return treeview.SelectedItem as BeWoFolderVM;
        }

        private void popupedit_file_PopUpClick(object sender, RoutedEventArgs ev)
        {
            var dlg = new OpenFileDialog();

            if (dlg.ShowDialog() == true)
            {
                try
                {

                    _UploadFileName = dlg.FileName;

                    popupedit_file.Text = dlg.SafeFileName;
                    button_uploadDocument.IsEnabled = true;


                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Exclamation);
                }
            }
        }

        private void ContextMenu_Opened(object sender, RoutedEventArgs e)
        {
            if (e.OriginalSource is ContextMenu)
            {
                ContextMenu menu = e.OriginalSource as ContextMenu;

                BeWoFolderVM folder = this.treeview.SelectedItem as BeWoFolderVM;

                bool enabled = false;

                if (folder != null)
                {
                    if (folder.ParentFolder != null)
                        enabled = true;
                }

                foreach (var item in menu.Items)
                {
                    if (item is MenuItem)
                    {
                        MenuItem menuitem = item as MenuItem;
                        if (menuitem.Header.Equals("Umbenennen"))
                            menuitem.IsEnabled = enabled;
                    }
                }
            }
        }


        private void Hyperlink_OnRequestNavigate(object sender, RequestNavigateEventArgs eventargs)
        {
            var navitageUri = ((Hyperlink)sender).NavigateUri.ToString();
            var splittedUri = navitageUri.Split('=');
            var fileOid = 0L;
            long.TryParse(splittedUri[1], out fileOid);

            try
            {
                var lFA = new FileAttachmentDC();

                ServiceFacade.DoDownloadServiceAsync(d => lFA = d.GetFileAttachment(fileOid),
                                                     cb => this.Dispatch(delegate
                                                        {
                                                            try
                                                            {
                                                                if (lFA.URL == null)
                                                                    return;

                                                                var odialog = new OpenOrSaveDialog(lFA.FileName);
                                                                odialog.ShowDialog();

                                                                if (odialog.Selection == null)
                                                                    return;

                                                                if (!odialog.Selection.Value)
                                                                {
                                                                    var tmp = Path.GetTempPath() + Path.GetFileName(lFA.URL);
                                                                    

                                                                    if (File.Exists(tmp))
                                                                        File.Delete(tmp);

                                                                    using (var uFileStream = File.Create(tmp))
                                                                    {
                                                                        uFileStream.Write(lFA.BinaryData, 0, lFA.BinaryData.Length);
                                                                    }

                                                                    System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(tmp));

                                                                    BeWoApp.CurrentBeWo.DocumentWatcher.FileCreated -= DocumentWatcher_FileCreated;
                                                                    BeWoApp.CurrentBeWo.DocumentWatcher.FileDeleted -= DocumentWatcher_FileDeleted;
                                                                    BeWoApp.CurrentBeWo.DocumentWatcher.StoppeDateiUeberwachung(tmp);
                                                                    BeWoApp.CurrentBeWo.DocumentWatcher.StarteDateiUeberwachung(new DocumentWatcherInfo(tmp, cb.Description, cb.BeWoFolderOid, cb.FileAttachmentOid, cb.FileAttachmentVersion, _ObjectOid, _ObjectTid));
                                                                    BeWoApp.CurrentBeWo.DocumentWatcher.FileCreated += DocumentWatcher_FileCreated;
                                                                    BeWoApp.CurrentBeWo.DocumentWatcher.FileDeleted += DocumentWatcher_FileDeleted;


                                                                    return;
                                                                }

                                                                if (odialog.Selection.Value)
                                                                {
                                                                    var sf = new SaveFileDialog { FileName = Path.GetFileName(lFA.URL) };
                                                                    if (sf.ShowDialog() != true)
                                                                        return;

                                                                    using (var myStream = (FileStream)sf.OpenFile())
                                                                    {
                                                                        myStream.Write(lFA.BinaryData, 0, lFA.BinaryData.Length);
                                                                        myStream.Close();
                                                                    }
                                                                }
                                                            }
                                                            catch (Exception ex)
                                                            {
                                                                MessageBox.Show("Beim Öffnen des Dokuments ist leider ein Fehler aufgetreten:\n" + ex.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Exclamation);
                                                            }

                                                        }));
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Exclamation);
            }
        }

        private BeWoFolderVM GetFolderWithOid(long? folderOid)
        {
            BeWoFolderVM alleOrdnerFolder = _ViewModel as BeWoFolderVM;

            if (folderOid.HasValue)
            {
                //Durchsuche alle kinder und deren kinder bis folderoid gefunden
                return GetChildFolderWithOid(folderOid.Value, alleOrdnerFolder);

            }
            return alleOrdnerFolder;
        }

        private BeWoFolderVM GetChildFolderWithOid(long folderOid, BeWoFolderVM folder)
        {
            if (folder.DataContract.BeWoFolderOid.HasValue && folder.DataContract.BeWoFolderOid.Value == folderOid)
                return folder;

            foreach (var childFolder in folder.SubFolders)
            {
                var f = GetChildFolderWithOid(folderOid, childFolder);
                if (f != null)
                {
                    return f;
                }
            }
            return null;
        }

        void DocumentWatcher_FileDeleted(long? folderOid, long? objectOid, TableID objectTid, long fileAttachmentOid)
        {
            if (objectOid == _ObjectOid && objectTid == _ObjectTid)
            {
                BeWoFolderVM parentFolder = GetFolderWithOid(folderOid);
                if (parentFolder != null)
                {
                    FileAttachmentVM fileToDelete = null;
                    foreach (var file in parentFolder.Files)
                    {
                        if (file.DataContract.FileAttachmentOid == fileAttachmentOid)
                        {
                            fileToDelete = file;
                        }
                    }

                    if (fileToDelete != null)
                    {
                        parentFolder.Files.Remove(fileToDelete);
                        BeWoWpfUtils.RefreshDXGrid(this.datagrid_documents);
                    }
                }
            }
        }

        void DocumentWatcher_FileCreated(long? folderOid, long? objectOid, TableID objectTid, FileAttachmentDC fileAttachment)
        {
            if (objectOid == _ObjectOid && objectTid == _ObjectTid)
            {
                BeWoFolderVM folder = GetFolderWithOid(folderOid);
                if (folder != null)
                {
                    var newFileVM = new FileAttachmentVM(fileAttachment);
                    BeWoApp.CurrentBeWo.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)delegate
                    {                  
                        folder.Files.Add(newFileVM);
                        newFileVM.Folder = folder;
                    });
                }
            }
        }
    }
}