using System;
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 InABox.Clients;
using InABox.Core;
using InABox.Dxf;
using InABox.DynamicGrid.Properties;
using InABox.WPF;
using Microsoft.Win32;

namespace InABox.DynamicGrid
{
    public class DynamicImagePreviewColumn<T> : DynamicImageColumn
        where T : Entity, IRemotable, IPersistent, new()
    {
        private static readonly BitmapImage _preview = Resources.doc_png.AsBitmapImage();

        private static Expression<Func<T, ImageDocumentLink>> _property;

        private readonly bool _canupdate;

        private readonly IDynamicGrid _parent;

        public DynamicImagePreviewColumn(IDynamicGrid parent, Expression<Func<T, ImageDocumentLink>> property, bool canupdate) : base(PreviewImage)
        {
            _parent = parent;
            _property = property;
            _canupdate = canupdate;
            Image = PreviewImage;
            Filters = new[] { "Image Present", "Blank Image" };
            FilterRecord = DoFilterImages;
            ContextMenu = CreateImageMenu;
            ToolTip = CreateImageToolTip;
        }

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

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

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

        private 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),
                new Columns<Document>(x => x.ID, x => x.Data)
            ).Rows.FirstOrDefault();
            if (image != null)
            {
                var ms = new MemoryStream(image.Get<Document, byte[]>(x => x.Data));
                result = new Bitmap(ms);
            }

            return result;
        }

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

            if (bitmap != null && docid == Guid.Empty)
            {
                byte[] data = null;
                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),
                    new Columns<Document>(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, _imageid, 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, _imageid, 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>(_imageid) != 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 = null;
                    if (Path.GetExtension(filename).ToLower().Equals(".dxf"))
                        bmp = DxfUtils.DXFToBitmap(filename);
                    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>(_imageid);
            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>(_imageid));
            var data = new Tuple<Guid, string, Bitmap>(
                row.Get<Guid>(_imageid),
                row.Get<string>(_imagefilename),
                bmp
            );
            Clipboard.SetData("ProductImage", data);
        }

        private void PasteImage(CoreRow row)
        {
            if (row.Get<Guid>(_imageid) != 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"))
            {
                var data = Clipboard.GetData("ProductImage") as Tuple<Guid, string, Bitmap>;
                id = data.Item1;
                filename = data.Item2;
                bitmap = data.Item3;
            }
            else if (Clipboard.ContainsImage())
            {
                var data = Clipboard.GetImage();
                bitmap = data.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 == 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);
        }

        private bool DoFilterImages(CoreRow row, string[] filter)
        {
            var hasimage = row.Get<Guid>(_imageid) != Guid.Empty;
            if (filter.Contains("Image Present") && hasimage)
                return true;
            if (filter.Contains("Blank Image") && !hasimage)
                return true;
            return false;
        }

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

            return result;
        }
    }
}