using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using InABox.Clients;
using InABox.Core;
using InABox.Dxf;
using InABox.WPF;
using Microsoft.Win32;

namespace InABox.DynamicGrid;

public class DynamicImagePreviewColumn<T> : DynamicImageColumn
    where T : BaseObject
{

    private static readonly BitmapImage _preview = Wpf.Resources.doc_png.AsBitmapImage();

    protected static Expression<Func<T, ImageDocumentLink>> Property { get; private set; }

    protected static string ImageIDProperty => Property != null ? CoreUtils.GetFullPropertyName(Property, ".") + ".ID" : "";

    public DynamicImagePreviewColumn(Expression<Func<T, ImageDocumentLink>> property) : base(PreviewImage)
    {
        Property = property;
        Image = PreviewImage;
        ToolTip = CreateImageToolTip;

        GetFilter = () => new FuncCheckBoxDynamicGridColumnFilter(DoFilterImages, FilterData);
    }

    private static BitmapImage? PreviewImage(CoreRow? arg)
    {
        if (arg is null || arg.Get<Guid>(ImageIDProperty) == Guid.Empty)
            return null;
        return _preview;
    }

    protected Bitmap? LoadBitmapFromDatabase(Guid imageid)
    {
        if (imageid == Guid.Empty)
            return null;
        Bitmap? result = null;
        var image = new Client<Document>().Query(
            new Filter<Document>(x => x.ID).IsEqualTo(imageid),
            Columns.None<Document>().Add(x => x.ID, x => x.Data)
        ).Rows.FirstOrDefault();
        if (image != null)
        {
            var bytes = image.Get<Document, byte[]>(x => x.Data);
            if (bytes?.Any() == true)
            {
                var ms = new MemoryStream(bytes);
                result = new Bitmap(ms);
            }
        }

        return result;
    }

    private FrameworkElement? CreateImageToolTip(DynamicActionColumn column, CoreRow? arg)
    {
        if (arg is null)
            return null;
        FrameworkElement? result = null;
        var imageid = arg.Get<Guid>(ImageIDProperty);
        if (!imageid.Equals(Guid.Empty))
            using (new WaitCursor())
            {
                var bmp = LoadBitmapFromDatabase(imageid);
                result = ImageToolTip(bmp?.AsBitmapImage(false));
            }

        return result;
    }

    private IEnumerable<Tuple<string, object?>> FilterData()
    {
        yield return new("Image Present", true);
        yield return new("Blank Image", false);
    }

    private bool DoFilterImages(CoreRow row, Predicate<object?> filter)
    {
        var hasimage = row.Get<Guid>(ImageIDProperty) != Guid.Empty;
        return filter(hasimage);
    }
}

public class DynamicImageManagerColumn<T> : DynamicImagePreviewColumn<T>
    where T : Entity, IRemotable, IPersistent, new()
{


    private readonly bool _canupdate;

    private readonly IDynamicGrid _parent;

    private static string _imagefilename => Property != null ? CoreUtils.GetFullPropertyName(Property, ".") + ".FileName" : "";

    public DynamicImageManagerColumn(IDynamicGrid parent, Expression<Func<T, ImageDocumentLink>> property, bool canupdate) : base(property)
    {
        _parent = parent;
        _canupdate = canupdate;
        ContextMenu = CreateImageMenu;
    }

    private MenuItem CreateMenu(string caption, RoutedEventHandler click)
    {
        var item = new MenuItem();
        item.Header = caption;
        item.Click += click;
        return item;
    }

    private void SaveBitmapToDatabase(CoreRow row, Guid id, string filename, Bitmap? bitmap)
    {
        var docid = id;

        if (bitmap != null && docid == Guid.Empty)
        {
            byte[] data;
            using (var ms = new MemoryStream())
            {
                bitmap.Save(ms, ImageFormat.Png);
                data = ms.GetBuffer();
            }

            var crc = CoreUtils.CalculateCRC(data);

            var doc = new Client<Document>().Query(
                new Filter<Document>(x => x.FileName).IsEqualTo(Path.GetFileName(filename)).And(x => x.CRC).IsEqualTo(crc),
                Columns.None<Document>().Add(x => x.ID)
            ).Rows.FirstOrDefault()?.ToObject<Document>();
            if (doc == null)
            {
                doc = new Document
                {
                    FileName = Path.GetFileName(filename),
                    CRC = crc,
                    TimeStamp = DateTime.Now,
                    Data = data
                };
                new Client<Document>().Save(doc, "");
            }

            docid = doc.ID;
        }

        var item = row.ToObject<T>();
        CoreUtils.SetPropertyValue(item, ImageIDProperty, docid);
        CoreUtils.SetPropertyValue(item, _imagefilename, Path.GetFileName(filename));
        new Client<T>().Save(item, "", (p, err) => { });

        // False here to prevent Refreshing and losing the selected row record
        _parent.UpdateRow(row, ImageIDProperty, docid, false);
        _parent.UpdateRow(row, _imagefilename, Path.GetFileName(filename));
    }

    private ContextMenu? CreateImageMenu(CoreRow[]? rows)
    {
        if (rows == null || rows.Length != 1)
            return null;

        var hasimage = rows[0].Get<Guid>(ImageIDProperty) != Guid.Empty;
        var canpaste = (_canupdate && Clipboard.ContainsData("ProductImage")) || Clipboard.ContainsImage();
        var result = new ContextMenu();

        if (_canupdate)
            result.Items.Add(CreateMenu("Load From File", (o, e) => LoadImage(rows[0])));
        if (hasimage)
            result.Items.Add(CreateMenu("Save To File", (o, e) => SaveImage(rows[0])));
        if (hasimage || canpaste)
            result.Items.Add(new Separator());
        if (hasimage)
            result.Items.Add(CreateMenu("Copy To Clipboard", (o, e) => CopyImage(rows[0])));
        if (canpaste)
            result.Items.Add(CreateMenu("Paste From Clipboard", (o, e) => PasteImage(rows[0])));

        if (_canupdate && hasimage)
        {
            result.Items.Add(new Separator());
            result.Items.Add(CreateMenu("Clear Image", (o, e) => ClearImage(rows[0])));
        }

        return result;
    }

    private void LoadImage(CoreRow row)
    {
        var dlg = new OpenFileDialog();
        dlg.Filter = "Image Files (*.png;*.jpg;*.jpeg;*.bmp)|*.png;*.jpg;*.jpeg;*.bmp|DXF Files (*.dxf)|*.dxf";
        if (dlg.ShowDialog() == true)
            using (new WaitCursor())
            {
                var filename = dlg.FileName.ToLower();

                Bitmap? bmp;
                if (Path.GetExtension(filename).ToLower().Equals(".dxf"))
                {
                    DxfUtils.OnProcessError += ((message) =>
                    {
                        MessageBox.Show("Error - " + message);
                    });
                    DxfUtils.DXFToBitmap(filename).GetOk(out bmp);
                }
                else
                    bmp = System.Drawing.Image.FromFile(filename) as Bitmap;

                SaveBitmapToDatabase(row, Guid.Empty, filename, bmp);
                //_parent?.Refresh(false, false);
            }
    }

    private void SaveImage(CoreRow row)
    {
        var imageid = row.Get<Guid>(ImageIDProperty);
        if (imageid == Guid.Empty)
            return;
        var filename = row.Get<string>(_imagefilename);
        var dlg = new SaveFileDialog();
        dlg.Filter = "Image Files (*.png)|*.png";
        dlg.FileName = filename;
        if (dlg.ShowDialog() == true)
        {
            var bmp = LoadBitmapFromDatabase(imageid);
            bmp?.Save(dlg.FileName);
        }
    }

    private void CopyImage(CoreRow row)
    {
        var bmp = LoadBitmapFromDatabase(row.Get<Guid>(ImageIDProperty));
        var data = new Tuple<Guid, string, Bitmap?>(
            row.Get<Guid>(ImageIDProperty),
            row.Get<string>(_imagefilename),
            bmp
        );
        Clipboard.SetData("ProductImage", data);
    }

    private void PasteImage(CoreRow row)
    {
        if (row.Get<Guid>(ImageIDProperty) != Guid.Empty)
            if (MessageBox.Show("Are you sure you wish to update the image for the selected items(s)?", "Update Images", MessageBoxButton.YesNo,
                    MessageBoxImage.Question) != MessageBoxResult.Yes)
                return;

        var id = Guid.Empty;
        var filename = "";
        Bitmap? bitmap = null;

        if (Clipboard.ContainsData("ProductImage") && Clipboard.GetData("ProductImage") is Tuple<Guid, string, Bitmap?> data)
        {
            id = data.Item1;
            filename = data.Item2;
            bitmap = data.Item3;
        }
        else if (Clipboard.ContainsImage())
        {
            var dataImage = Clipboard.GetImage();
            bitmap = dataImage.AsBitmap2();

            filename = string.Format("clip{0:yyyyMMddhhmmss}.png", DateTime.Now);
            if (Clipboard.ContainsFileDropList())
            {
                var list = Clipboard.GetFileDropList();
                if (list.Count > 0)
                    filename = Path.ChangeExtension(Path.GetFileName(list[0]!), ".png");
            }

            //filename = String.Format("clip{0:yyyyMMddhhmmss}.png",DateTime.Now);
            //bitmap.Save(filename);
            id = Guid.Empty;
        }

        if (bitmap is null)
        {
            MessageBox.Show("Unable to paste data from clipboard");
            return;
        }

        using (new WaitCursor())
        {
            SaveBitmapToDatabase(row, id, filename, bitmap);
        }

        //_parent?.Refresh(false, false);
    }

    private void ClearImage(CoreRow row)
    {
        if (MessageBox.Show("Are you sure you wish to clear the image for the selected product(s)?", "Clear Images", MessageBoxButton.YesNo,
                MessageBoxImage.Question) != MessageBoxResult.Yes)
            return;

        using (new WaitCursor())
        {
            SaveBitmapToDatabase(row, Guid.Empty, "", null);
        }

        //_parent?.Refresh(false, false);
    }


}