using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using InABox.Core;
using InABox.WPF;
using Microsoft.Win32;
using Microsoft.Xaml.Behaviors.Core;
using Image = System.Windows.Controls.Image;
using InABox.Wpf;
using System.Threading;

namespace InABox.DynamicGrid;


public class DocumentConverter : AbstractConverter<object, object>
{
    public override object Convert(object value)
    {
        return value;
    }
}

public class TimeStampToBrushConverter : AbstractConverter<DateTime, System.Windows.Media.Brush?>
{
    public System.Windows.Media.Brush? Empty { get; init; }
    public System.Windows.Media.Brush? Set { get; init; }
    
    public override System.Windows.Media.Brush? Convert(DateTime value)
    {
        return value.IsEmpty()
            ? Empty
            : Set;
    }
}

public class DynamicDocumentGrid<TDocument, TEntity, TEntityLink> : DynamicManyToManyGrid<TDocument, TEntity>
    where TEntity : Entity, IPersistent, IRemotable, new()
    where TDocument : Entity, IEntityDocument<TEntityLink>, IPersistent, IRemotable, new() // Entity, IPersistent, IRemotable, IManyToMany<TEntity, Document>, new()
    where TEntityLink : EntityLink<TEntity>, new()
{
    
    public bool ShowSupercededColumn { get; set; }

    private bool _simpleTemplate;
    public bool SimpleTemplate
    {
        get => _simpleTemplate;
        set
        {
            _simpleTemplate = value;
            RowHeight = value
                ? 150
                : 100;
        } 
    }

    private DynamicTemplateColumn _template;
    
    public DynamicDocumentGrid()
    {
        MultiSelect = false;
        HiddenColumns.Add(x => x.DocumentLink.ID);
        HiddenColumns.Add(x => x.Superceded);
        HiddenColumns.Add(x => x.DocumentLink.FileName);
        HiddenColumns.Add(x => x.Thumbnail);
        HiddenColumns.Add(x => x.Notes);
        //ActionColumns.Add(new DynamicImageColumn(DocumentImage, ViewDocument) { Position = DynamicActionColumnPosition.Start });
        //ActionColumns.Add(new DynamicImageColumn(DiskImage, SaveDocument) { Position = DynamicActionColumnPosition.Start });
        _template = new DynamicTemplateColumn(DocumentTemplate)
        {
            Position = DynamicActionColumnPosition.Start,
            Width = 0,
            HeaderText = "Attached Documents"
        };
        ActionColumns.Add(_template);
        //supercedecolumn = new DynamicImageColumn(SupercededImage, SupercedeDocument);
        //ActionColumns.Add(supercedecolumn);
        RowHeight = 100;
    }

    protected override void DoDoubleClick(object sender, DynamicGridCellClickEventArgs args)
    {
        var doc = SelectedRows.FirstOrDefault()?.ToObject<TDocument>();
        if (doc != null)
        {
            var editor = new DocumentEditor(new IEntityDocument[] { doc });
            //editor.PrintAllowed = Security.IsAllowed<CanPrintFactoryFloorDrawings>();
            editor.SaveAllowed = false;
            editor.ShowDialog();
        }
    }

    private FrameworkElement DocumentTemplate(CoreRow row)
    {
        return SimpleTemplate
            ? CreateSimpleTemplate()
            : CreateDetailedTemplate();
    }

    private FrameworkElement CreateDetailedTemplate()
    {
        
        Grid grid = new Grid()
        {
            Height = 100,
            ContextMenu = CreateContextMenu(),
            RowDefinitions = 
            {
                new RowDefinition() { Height = new GridLength(1, GridUnitType.Auto) },
                new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) },
            },
            ColumnDefinitions = 
            {
                new ColumnDefinition() { Width = new GridLength(100) },
                new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) },
                new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Auto) },
            }
        };
        
        // grid.SetBinding(
        //     Grid.BackgroundProperty,
        //     new Binding("Superceded")
        //     {
        //         Converter = new TimeStampToBrushConverter()
        //         {
        //             Empty = new SolidColorBrush(Colors.LightYellow),
        //             Set = new SolidColorBrush(Colors.Silver)
        //         }
        //     }
        // );

        Image thumbnail = new Image()
        {
            Stretch = Stretch.Uniform,
            Margin = new Thickness(5, 2, 5, 2),

        };

        var ttImage = new Image();
        ttImage.SetBinding(Image.SourceProperty,
            new Binding("Thumbnail") { Converter = new BytesToBitmapImageConverter() });
        thumbnail.ToolTip = new ToolTip()
        {
            Content = ttImage
        };

        thumbnail.SetBinding(Image.SourceProperty,
            new Binding("Thumbnail") { Converter = new BytesToBitmapImageConverter() });
        thumbnail.SetValue(Grid.RowProperty, 0);
        thumbnail.SetValue(Grid.RowSpanProperty, 2);
        thumbnail.SetValue(Grid.ColumnProperty, 0);
        grid.Children.Add(thumbnail);

        var dock = new DockPanel();
        dock.SetValue(Grid.RowProperty, 0);
        dock.SetValue(Grid.ColumnProperty, 1);
        grid.Children.Add(dock);

        var superceded = new Label()
        {
            FontWeight = FontWeights.Bold,
            Content = "*** SUPERCEDED ***",
            Margin = new Thickness(0, 0, 5, 0)
        };
        superceded.SetBinding(Label.VisibilityProperty,
            new Binding("Superceded") { Converter = new DateTimeToVisibilityConverter() });
        superceded.SetValue(DockPanel.DockProperty, Dock.Left);
        dock.Children.Add(superceded);

        var filename = new Label()
        {
            FontWeight = FontWeights.Bold
        };
        filename.SetBinding(Label.ContentProperty, new Binding("DocumentLink_FileName"));
        filename.SetValue(DockPanel.DockProperty, Dock.Left);
        dock.Children.Add(filename);

        var buttons = new StackPanel()
        {
            Orientation = Orientation.Horizontal
        };
        buttons.SetValue(Grid.RowProperty, 0);
        buttons.SetValue(Grid.ColumnProperty, 2);
        grid.Children.Add(buttons);

        var view = new Button()
        {
            Content = new Image() { Source = Wpf.Resources.multi_image.AsBitmapImage() },
            BorderBrush = new SolidColorBrush(Colors.Transparent),
            Background = new SolidColorBrush(Colors.Transparent),
            Height = 32,
            Width = 32,
            ToolTip = "View Documents",
            Command = new ActionCommand(ViewDocuments)
        };
        buttons.Children.Add(view);


        var copy = new Button()
        {
            Content = new Image() { Source = Wpf.Resources.copy.AsBitmapImage() },
            BorderBrush = new SolidColorBrush(Colors.Transparent),
            Background = new SolidColorBrush(Colors.Transparent),
            Height = 32,
            Width = 32,
            ToolTip = "Copy to Clipboard",
            Command = new ActionCommand(CopyDocuments)
        };
        buttons.Children.Add(copy);

        var save = new Button()
        {
            Content = new Image() { Source = ImageUtils.SvgToBitmapImage(Wpf.Resources.download) },
            BorderBrush = new SolidColorBrush(Colors.Transparent),
            Background = new SolidColorBrush(Colors.Transparent),
            Height = 32,
            Width = 32,
            ToolTip = "Save Documents",
            Command = new ActionCommand(SaveDocuments)
        };
        buttons.Children.Add(save);

        var print = new Button()
        {
            Content = new Image() { Source = Wpf.Resources.print.AsBitmapImage(), Margin = new Thickness(2) },
            BorderBrush = new SolidColorBrush(Colors.Transparent),
            Background = new SolidColorBrush(Colors.Transparent),
            Height = 32,
            Width = 32,
            ToolTip = "Print Documents",
            Command = new ActionCommand(PrintDocuments)
        };
        buttons.Children.Add(print);

        var notes = new Label()
        {
        };
        notes.SetBinding(Label.ContentProperty, new Binding("Notes"));
        notes.SetValue(Grid.RowProperty, 1);
        notes.SetValue(Grid.ColumnProperty, 1);
        notes.SetValue(Grid.ColumnSpanProperty, 2);
        grid.Children.Add(notes);

        return grid;
    }

    private ContextMenu CreateContextMenu()
    {
        var menu = new ContextMenu();
        menu.Items.Add(new MenuItem()
        {
            Header = "View Documents",
            Command = new ActionCommand(ViewDocuments)
        });
        menu.Items.Add(new MenuItem()
        {
            Header = "Copy To Clipboard",
            Command = new ActionCommand(CopyDocuments)
        });
        menu.Items.Add(new MenuItem()
        {
            Header = "Save Documents",
            Command = new ActionCommand(SaveDocuments)
        });
        return menu;
    }

    private FrameworkElement CreateSimpleTemplate()
    {
        Grid grid = new Grid()
        {
            //Height = 150,
            ContextMenu = CreateContextMenu(),
            RowDefinitions = 
            {
                new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) },
                new RowDefinition() { Height = new GridLength(1, GridUnitType.Auto) },
            },
            ColumnDefinitions = 
            {
                new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) },
            }
        };

        Image thumbnail = new Image()
        {
            Stretch = Stretch.Uniform,
            Margin = new Thickness(5),
            //HorizontalAlignment = HorizontalAlignment.Stretch,
            //VerticalAlignment = VerticalAlignment.Stretch
        };
            
        thumbnail.SetBinding(Image.SourceProperty, new Binding("Thumbnail") { Converter = new BytesToBitmapImageConverter() });
        thumbnail.SetValue(Grid.RowProperty,0);
        grid.Children.Add(thumbnail);
        
        var filename = new Label()
        {
            HorizontalContentAlignment = HorizontalAlignment.Center,
            FontSize = 10
        };
        filename.SetBinding(Label.ContentProperty, new Binding("DocumentLink_FileName"));
        filename.SetValue(Grid.RowProperty,1);
        grid.Children.Add(filename);
        
        return grid;
    }

    private void GetDocuments(Action<Dictionary<string,byte[]>> action)
    {
        var ids = SelectedRows.Select(r => r.Get<IEntityDocument, Guid>(c => c.DocumentLink.ID)).ToArray();
        var files = Client.Query(
            new Filter<Document>(x => x.ID).InList(ids),
            Columns.None<Document>().Add(x => x.FileName).Add(x => x.Data)
        ).ToDictionary<Document, String, byte[]>(x => x.FileName, x => x.Data);
        action?.Invoke(files);
    }

    private static string SanitiseFileName(string filename)
    {
        var basefilename = Path.GetFileNameWithoutExtension(filename);
        var extension = Path.GetExtension(filename);
        return Path.ChangeExtension(string.Join("_", basefilename.Split(Path.GetInvalidFileNameChars())), extension);
    }

    private void ViewDocuments()
    {
        GetDocuments((files) =>
        {
            
            foreach (var file in files)
            {
                Task.Run(() =>
                {
                    var tempfile = Path.Combine(System.IO.Path.GetTempPath(), SanitiseFileName(file.Key));
                    try
                    {
                        File.WriteAllBytes(tempfile, file.Value);
                    }
                    catch
                    {
                        // Outlook likes to keep files open apparently, which breaks this code.
                    }
                    var info = new System.Diagnostics.ProcessStartInfo(tempfile);
                    info.UseShellExecute = true;
                    info.Verb = "Open";
                    Process.Start(info);
                });
            }
        });
    }

    private void CopyDocuments()
    {
        if (SelectedRows?.Any() != true)
            return;
        GetDocuments((files) =>
        {
            System.Collections.Specialized.StringCollection FileCollection = new System.Collections.Specialized.StringCollection();
            foreach(var file in files)
            {
                var tempfile = Path.Combine(System.IO.Path.GetTempPath(), SanitiseFileName(file.Key));
                File.WriteAllBytes(tempfile, file.Value);
                FileCollection.Add(tempfile);
            }
            Clipboard.SetFileDropList(FileCollection);
        });
    }

    private void SaveDocuments()
    {
        if (SelectedRows?.Any() != true)
            return;
        
        using(var fbd = new System.Windows.Forms.FolderBrowserDialog())
        {
            var result = fbd.ShowDialog();
            if (result == System.Windows.Forms.DialogResult.OK && !string.IsNullOrWhiteSpace(fbd.SelectedPath))
            {
                var path = fbd.SelectedPath;
                GetDocuments(files =>
                {
                    foreach (var file in files)
                        File.WriteAllBytes(Path.Combine(path, SanitiseFileName(file.Key)), file.Value);
                });
            }
        }

    }

    private void PrintDocuments()
    {
        if (SelectedRows?.Any() != true)
            return;
        GetDocuments(files =>
        {
            Task.Run(() =>
            {
                foreach (var file in files)
                {
                    var tempfile = Path.Combine(System.IO.Path.GetTempPath(), SanitiseFileName(file.Key));
                    File.WriteAllBytes(tempfile, file.Value);
                    var info = new System.Diagnostics.ProcessStartInfo(tempfile);
                    info.CreateNoWindow = true;
                    info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
                    info.UseShellExecute = true;
                    info.Verb = "print";
                    Process.Start(info);
                }
            });
        });

    }

    protected override DynamicGridColumns LoadColumns()
    {
        return new DynamicGridColumns();
    }
    
    protected override void DoReconfigure(DynamicGridOptions options)
    {
        base.DoReconfigure(options);
        options.SelectColumns = false;
        options.DragTarget = true;
    }

    public override int Order { get; set; } = int.MaxValue;

    protected override void HandleDragOver(object sender, DragEventArgs e)
    {
        if (e.Data.GetDataPresent(DataFormats.FileDrop) || e.Data.GetDataPresent("FileGroupDescriptor"))
        {
            e.Effects = DragDropEffects.Copy;
        }
        else
        {
            e.Effects = DragDropEffects.None;
        }
        e.Handled = true;
    }

    protected override void HandleDragDrop(object sender, DragEventArgs e)
    {
        var result = DocumentUtils.HandleFileDrop(e);
        if(result is not null)
        {
            var docs = new List<Document>();
            foreach (var (filename, stream) in result)
            {
                var doc = new Document();
                doc.FileName = Path.GetFileName(filename).ToLower();
                if (stream is null)
                {
                    doc.Data = File.ReadAllBytes(filename);
                    doc.TimeStamp = new FileInfo(filename).LastWriteTime;
                }
                else
                {
                    using var memoryStream = new MemoryStream();
                    stream.CopyTo(memoryStream);
                    doc.Data = memoryStream.ToArray();
                    doc.TimeStamp = DateTime.Now;
                }
                doc.CRC = CoreUtils.CalculateCRC(doc.Data);
                docs.Add(doc);
            }
            AddDocuments(docs);
        }
    }

    protected override void OnDragEnd(Type entity, CoreTable table, DragEventArgs e)
    {
        if (entity == typeof(Document))
        {
            var refresh = false;

            var docIDS = table.Rows.Select(x => x.Get<Document, Guid>(x => x.ID)).ToArray();

            var columns = Columns.None<Document>().Add(x => x.ID);
            foreach (var column in VisibleColumns)
            {
                if (column.ColumnName.StartsWith("DocumentLink."))
                {
                    columns.Add(string.Join('.', column.ColumnName.Split('.').Skip(1)));
                }
            }
            var docs = Client.Query(
                new Filter<Document>(x => x.ID).InList(docIDS),
                columns);

            foreach (var doc in docs.ToObjects<Document>())
            {
                var entityDocument = new TDocument();
                entityDocument.EntityLink.ID = Item.ID;
                entityDocument.DocumentLink.ID = doc.ID;
                entityDocument.DocumentLink.Synchronise(doc);
                SaveItem(entityDocument);
                refresh = true;
            }
            if (refresh)
            {
                DoChanged();
                Refresh(false, true);
            }
        }
        else
        {
            base.OnDragEnd(entity, table, e);
        }
    }

    private void AddDocuments(IList<Document> documents)
    {
        if (documents.Any())
        {
            Client.Save(documents, "Initial Upload");
            foreach (var doc in documents)
            {
                var newitem = CreateItem();
                var prop = GetOtherLink(newitem);
                prop.ID = doc.ID;
                prop.Synchronise(doc);
                SaveItem(newitem);
            }
            DoChanged();
            Refresh(false, true);
        }
    }

    protected override void DoAdd(bool openEditorOnDirectEdit = false)
    {
        var dlg = new OpenFileDialog();
        dlg.Multiselect = true;
        if (dlg.ShowDialog() == true)
        {
            using (new WaitCursor())
            {
                var docs = new List<Document>();
                foreach (var filename in dlg.FileNames)
                {
                    // Create a Document
                    var doc = new Document();
                    doc.FileName = Path.GetFileName(filename).ToLower();
                    doc.TimeStamp = new FileInfo(dlg.FileName).LastWriteTime;
                    doc.Data = File.ReadAllBytes(filename);
                    doc.CRC = CoreUtils.CalculateCRC(doc.Data);
                    docs.Add(doc);
                }
                AddDocuments(docs);
            }
        }
    }

    protected override void Reload(
        Filters<TDocument> criteria, Columns<TDocument> columns, ref SortOrder<TDocument>? sort, 
        CancellationToken token, Action<CoreTable?, Exception?> action)
    {
        base.Reload(criteria, columns, ref sort, token, (t,e) =>
        {
            if (token.IsCancellationRequested) return;

            action(t,e);
            
            // Download Hi Res images in the background and replace them when available
            if (t != null && SimpleTemplate)
            {
                var ids = t.ExtractValues<TDocument, Guid>(x => x.DocumentLink.ID).Distinct().ToArray();
                Client.Query(
                    new Filter<Document>(x => x.ID).InList(ids),
                    Columns.None<Document>().Add(x => x.ID).Add(x => x.Data),
                    null,
                    null,
                    (d, _) =>
                    {
                        if (token.IsCancellationRequested) return;

                        if (d == null)
                            return;
                        var docs = d.ToDictionary<Document, Guid, byte[]>(x => x.ID, x => x.Data);
                        foreach (var row in t.Rows)
                        {
                            if (docs.TryGetValue(row.Get<TDocument, Guid>(x => x.DocumentLink.ID),
                                    out byte[]? data) && (data?.Any() == true))
                            {
                                if (ImageUtils.IsPdf(data))
                                    data = ImageUtils.PDFToBitmap(data, 0);
                                row.Set<TDocument, byte[]>(x => x.Thumbnail!, data);
                            }
                        }
                        Dispatcher.BeginInvoke(() => base.Refresh(false,false));
                    }
                );
            }


        });
    }
}