using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using MaddoLibrary.Base.Log;
using SixLabors.Fonts;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.Processing;
using SixLabors.Primitives;
using Font = SixLabors.Fonts.Font;
using FontFamily = SixLabors.Fonts.FontFamily;
using FontStyle = SixLabors.Fonts.FontStyle;
using Rgba32 = SixLabors.ImageSharp.Rgba32;
using Image = SixLabors.ImageSharp.Image;

namespace CatalogLib
{
    public class ImgSharpCreator : IImageProcessor
    {
        private Image<Rgba32> _logo;
        public ImgSharpCreator()
        {
            if (!PicSettings.Instance.EnableLogo) return;
            if (string.IsNullOrWhiteSpace(PicSettings.Instance.LogoPath)) return;
            if (!File.Exists(PicSettings.Instance.LogoPath)) return;

            _logo = Image.Load(PicSettings.Instance.LogoPath);

        }
        private FileInfo _currentFile;
        public void Start(FileInfo workFile)
        {

            Stopwatch s = new Stopwatch();
            s.Start();

            _currentFile = workFile;



            using (Image<Rgba32> image = Image.Load(workFile.FullName)/* new Image(workFile.FullName)*/)
            {
                MaddoLogger.Log("Loaded Image: {0}", workFile.FullName);
                //image.Rotate(-90);
                bool isRotated;

                var orientation = image.MetaData.ExifProfile.GetValue(ExifTag.Orientation);
                if ((ushort)orientation.Value != 1)
                {
                    isRotated = true;
                }
                else
                {
                    isRotated = false;
                }

                if (PicSettings.Instance.FotoRidimensiona)
                {
                    Resize(image);
                }


                if (PicSettings.Instance.GeneraleRotazioneAutomatica)
                {
                    image.Mutate(img => img.AutoOrient());
                    MaddoLogger.Log("Rotated Image: {0}", workFile.FullName);
                    //image.AutoOrient();

                    //var exif = image.MetaData.ExifProfile;
                    //if (exif != null)
                    //{
                    //    var o = exif.GetValue(ExifTag.Orientation);
                    //    if (o != null)
                    //    {
                    //        var v = (ushort)o.Value;



                    //        switch (v)
                    //        {


                    //            //1 = Horizontal(normal)
                    //            //2 = Mirror horizontal
                    //            //3 = Rotate 180
                    //            //4 = Mirror vertical
                    //            //5 = Mirror horizontal and rotate 270 CW
                    //            //6 = Rotate 90 CW
                    //            //7 = Mirror horizontal and rotate 90 CW
                    //            //8 = Rotate 270 CW
                    //            case 1:
                    //                break;
                    //            case 2:
                    //                image.Flip(FlipType.Horizontal);
                    //                break;
                    //            case 3:
                    //                //image.Rotate(180f);

                    //                //image.Rotate(90);
                    //                image.Rotate(RotateType.Rotate180);
                    //                break;
                    //            case 4:
                    //                image.Flip(FlipType.Vertical);
                    //                break;
                    //            case 5:
                    //                image.RotateFlip(RotateType.Rotate270, FlipType.Horizontal);
                    //                break;
                    //            case 6:
                    //                image.Rotate(RotateType.Rotate90);
                    //                break;
                    //            case 7:
                    //                image.RotateFlip(RotateType.Rotate90, FlipType.Horizontal);
                    //                break;
                    //            case 8:
                    //                //image.Rotate(-90);
                    //                image.Rotate(RotateType.Rotate270);

                    //                image.MetaData.ExifProfile.SetValue(ExifTag.Orientation, new ExifValue(exif.GetValue(ExifTag.Orientation)).Value = (ushort)1);
                    //                break;


                    //        }
                    //    }
                    //}

                }

                if (PicSettings.Instance.EnableText)
                {
                    //SetTextTest(image);
                    SetExtraText(image, isRotated);


                    MaddoLogger.Log("Drawn text on Image: {0}", workFile.FullName);
                }

                //JpegDecoder j = new JpegDecoder();
                var va = Vector.IsHardwareAccelerated;

                MaddoLogger.Log("Hardware Accelerated: {0}", va);
                //image.Resize(PicSettings.Instance.FotoLarghezza, PicSettings.Instance.FotoAltezza);

                //image.Resize(2240, 2240);
                //var fff = FontCollection.SystemFonts.Find(PicSettings.Instance.NomeFont);
                //var font = new Font(fff, (float)PicSettings.Instance.DimensioneFont, FontStyle.Regular);
                //image.DrawText("sssssssssssssssssssssssssssssssssssssssssssssss", font, Color.Black, new Vector2(200, 200));
                image.Save(Path.Combine(PicSettings.Instance.DirectoryDestinazione, workFile.Name), new JpegEncoder() { Quality = PicSettings.Instance.CompressioneJpeg });
                //image.Resize(200, 200).Save("");

                MaddoLogger.Log("Saved Image: {0} to: {1}", workFile.FullName, Path.Combine(PicSettings.Instance.DirectoryDestinazione, workFile.Name));
            }
            s.Stop();
            MaddoLogger.Log("Time Taken for {0}: {1}", workFile.FullName, s.Elapsed);
        }

        private void Resize(Image<Rgba32> image)
        {
            IResampler resampler;
            switch (PicSettings.Instance.ResizeMode)
            {
                case ResizeModes.Bicubic:
                    resampler = new BicubicResampler();
                    break;
                case ResizeModes.Box:
                    resampler = new BoxResampler();
                    break;
                case ResizeModes.CatmullRom:
                    resampler = new CatmullRomResampler();
                    break;
                case ResizeModes.Hermite:
                    resampler = new HermiteResampler();
                    break;
                case ResizeModes.Lanczos2:
                    resampler = new Lanczos2Resampler();
                    break;
                case ResizeModes.Lanczos3:
                    resampler = new Lanczos3Resampler();
                    break;
                case ResizeModes.Lanczos5:
                    resampler = new Lanczos5Resampler();
                    break;
                case ResizeModes.Lanczos8:
                    resampler = new Lanczos8Resampler();
                    break;
                case ResizeModes.MitchellNetravali:
                    resampler = new MitchellNetravaliResampler();
                    break;
                case ResizeModes.NearestNeighbor:
                    resampler = new NearestNeighborResampler();
                    break;
                case ResizeModes.Robidoux:
                    resampler = new RobidouxResampler();
                    break;
                case ResizeModes.Spline:
                    resampler = new SplineResampler();
                    break;
                case ResizeModes.Triangle:
                    resampler = new TriangleResampler();
                    break;
                case ResizeModes.Welch:
                    resampler = new WelchResampler();
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            // todo calcolare ridimensionamento
            var size = new Size(PicSettings.Instance.FotoLarghezza, PicSettings.Instance.FotoAltezza);
            if (PicSettings.Instance.FotoMantieniDimensioni)
            {
                size = ResizeImage(image, size, PicSettings.Instance.ResizeDimension);
                image.Mutate(x => x.Resize((size.Width), (size.Height), resampler));

                //Width Formula:
                //original height / original width * new width = new height
                //Height Formula:
                //orignal width / orignal height * new height = new width
            }
            else
            {

                image.Mutate(x => x.Resize(PicSettings.Instance.FotoAltezza, PicSettings.Instance.FotoLarghezza, resampler));
            }

        }

        private Size ResizeImage(Image<Rgba32> image, Size size, ResizeDimensions side)
        {
            switch (side)
            {
                case ResizeDimensions.LatoLungo:
                    size = GetResizeDimensions(new Size(image.Width, image.Height), size, image.Width > image.Height);
                    break;
                case ResizeDimensions.LatoCorto:
                    size = GetResizeDimensions(new Size(image.Width, image.Height), size, image.Width <= image.Height);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            return size;
        }

        private Size GetResizeDimensions(Size originalSize, Size newSize, bool adjustHeight)
        {
            return adjustHeight ? new Size(newSize.Width, originalSize.Height / originalSize.Width * newSize.Height) : new Size(originalSize.Width / originalSize.Height * newSize.Height, newSize.Height);
        }

        private void SetTextTest(Image<Rgba32> image)
        {
            string text = "test test test test test testtest test test test test test test";
            Font font = new Font(SystemFonts.Find("verdana"), 300, FontStyle.Regular);
            image.Mutate(x => x.DrawText(text, font, Rgba32.Yellow, new PointF(2760, 3295.54932f), new TextGraphicsOptions()
            {
                HorizontalAlignment = HorizontalAlignment.Center
            }));
        }

        private void SetExtraText(Image<Rgba32> image, bool isRotated)
        {
            if (string.IsNullOrWhiteSpace(PicSettings.Instance.TestoApplicareOrizzontale))
            {
                Debug.WriteLine($"{_currentFile.Name} No text");
                return;
            }

            string text;
            if (isRotated)
            {
                if (PicSettings.Instance.TestoApplicareOrizzontale.Contains("$_"))
                {
                    text = PicSettings.Instance.TestoApplicareOrizzontale.Replace("$_", "\r\n");
                }
                else
                {
                    text = PicSettings.Instance.TestoApplicareOrizzontale.Replace("$_", "");
                }

            }
            else
            {
                text = PicSettings.Instance.TestoApplicareOrizzontale.Replace("$_", "");
            }


            var fo = SixLabors.Fonts.SystemFonts.Find(PicSettings.Instance.NomeFont);


            //var fff = FontCollection.SystemFonts.Find(PicSettings.Instance.NomeFont);
            //var fff = FontCollection.SystemFonts.Find("Segoe Print");

            Font font;

            if (!PicSettings.Instance.Grassetto)
            {
                font = new Font(fo, (float)PicSettings.Instance.DimensioneFont, FontStyle.Regular);
            }
            else
            {
                font = new Font(fo, (float)PicSettings.Instance.DimensioneFont, FontStyle.Bold);
            }
            // todo corsivo



            //var font = new Font(fff, 8f, FontStyle.Regular);


            //Color c = Color.FromHex(FlipRgbString(PicSettings.Instance.ColoreTestoRGB));
            Rgba32 g = Rgba32.FromHex(FlipRgbString(PicSettings.Instance.ColoreTestoRGB));

            Rgba32 gBack = Rgba32.Black; // todo alpha
            //TextMeasurer measurer = new TextMeasurer();

            //var size = measurer.MeasureText(PicSettings.Instance.TestoApplicareOrizzontale, font, 72);
            //float scalingFactor = Math.Min(image.Width / size.Width, image.Height / size.Height);
            //Font scaledFont = new Font(font, scalingFactor * font.Size);

            //image.DrawText(PicSettings.Instance.TestoApplicareOrizzontale, scaledFont, g, new Vector2(0, 0));

            Vector2 center = new Vector2(image.Width / 2, image.Height / 2); //center horizontally, 10px down 

            var size = TextMeasurer.Measure(text, new RendererOptions(font));

            var larghezzaStandard = size.Width;
            var dimensioneStandard = (int)Math.Round(PicSettings.Instance.DimensioneFont);
            if (size.Width > image.Width)
            {
                var c = dimensioneStandard;
                do
                {
                    if (c > 20)
                    {
                        c -= 5;
                    }
                    else
                    {
                        c -= 1;
                    }

                    if (PicSettings.Instance.Grassetto)
                    {
                        font = new Font(fo, c, FontStyle.Bold);
                    }
                    else
                    {
                        font = new Font(fo, c, FontStyle.Regular);
                    }
                    size = TextMeasurer.Measure(text,
                        new RendererOptions(font));
                    if (size.Width < image.Width)
                    {
                        larghezzaStandard = (int)Math.Round(size.Width);
                        break;
                    }
                    if (c <= 5)
                    {
                        break;
                    }
                } while (dimensioneStandard == c);
            }

            float yPosFromBottom = 0;

            switch (PicSettings.Instance.TextPosition)
            {
                case Positions.Alto:
                    yPosFromBottom = PicSettings.Instance.Margine;
                    break;
                case Positions.Basso:
                    yPosFromBottom = image.Height - size.Height - (image.Height * PicSettings.Instance.Margine / 100);
                    break;
            }

            float xCenterofImg = 0;

            // stringformat

            switch (PicSettings.Instance.TextAlignment)
            {
                case Alignments.Sinistra:
                    xCenterofImg = PicSettings.Instance.Margine + (larghezzaStandard / 2);
                    if ((larghezzaStandard / 2) > (image.Width / 2) - PicSettings.Instance.Margine)
                    {
                        xCenterofImg = image.Width / 2;
                    }
                    break;
                case Alignments.Centro:
                    xCenterofImg = image.Width / 2;
                    break;
                case Alignments.Destra:
                    xCenterofImg = image.Width - PicSettings.Instance.Margine - larghezzaStandard / 2;

                    if (larghezzaStandard / 2 > image.Width / 2 - PicSettings.Instance.Margine)
                    {
                        xCenterofImg = image.Width / 2;
                    }

                    break;
            }
            // stringformat alignment center

            if (PicSettings.Instance.Grassetto)
            {
                font = new Font(fo, dimensioneStandard, FontStyle.Bold);
            }
            else
            {
                font = new Font(fo, dimensioneStandard, FontStyle.Regular);
            }

            image.Mutate(x => x.DrawText(text, font, gBack, new PointF((float)Math.Round(xCenterofImg + 1), (float)Math.Round(yPosFromBottom + 1)), new TextGraphicsOptions()
            {
                HorizontalAlignment = HorizontalAlignment.Center
            }));

            image.Mutate(x => x.DrawText(text, font, g, new PointF((float)Math.Round(xCenterofImg), (float)Math.Round(yPosFromBottom)), new TextGraphicsOptions()
            {
                HorizontalAlignment = HorizontalAlignment.Center
            }));

            return;


            float scalingFactor = Math.Min(image.Width / size.Width, image.Height / size.Height);
            Font scaledFont = new Font(font, scalingFactor * font.Size);

            image.Mutate(x =>
            x.DrawText(PicSettings.Instance.TestoApplicareOrizzontale, scaledFont, g, center,
            new TextGraphicsOptions(true)
            {
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Bottom
            }));
            //image.DrawText(PicSettings.Instance.TestoApplicareOrizzontale, scaledFont, g, center, new TextGraphicsOptions(true) { HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Bottom });


        }

        private void AddLogo(Image<Rgba32> image)
        {
            //if (string.IsNullOrWhiteSpace(PicSettings.Instance.LogoPath)) return;
            //if (!File.Exists(PicSettings.Instance.LogoPath)) return;
            //using (Image<Rgba32> logo = Image.Load(PicSettings.Instance.LogoPath))
            //{
            //    Size size = new Size();
            //    Point location = new Point();
            //    image.Mutate(x => x.DrawImage(logo, size, location, new GraphicsOptions()
            //    {

            //    }));

            //}

            Size size = new Size();
            Point location = new Point();

            if (_logo != null)
            {
                var width = PicSettings.Instance.LogoWidth;
                var height = PicSettings.Instance.LogoHeight;
                var heightFactor = _logo.Height / height;
                var widthFactor = _logo.Width / width;
                var newLogoSize = new Size();

                newLogoSize = GetResizeDimensions(new Size(_logo.Width, _logo.Height), new Size(width, height), PicSettings.Instance.LogoResizeSide.Equals(ResizeDimensions.LatoCorto));
                //todo riguardare perché non torna cosa ho fatto

                int margineUsato = 0;
                int margineL;
                bool inPercentualeL;

                inPercentualeL = PicSettings.Instance.LogoMargin.EndsWith("%");
                if (inPercentualeL)
                {
                    margineL = int.Parse(PicSettings.Instance.LogoMargin.Replace("%", ""));
                }
                else
                {
                    margineL = int.Parse(PicSettings.Instance.LogoMargin);
                }

                switch (PicSettings.Instance.LogoPosition)
                {
                    case Positions.Alto:
                        break;
                    case Positions.Centro:
                        break;
                    case Positions.Basso:
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }

                switch (PicSettings.Instance.LogoAlignment)
                {
                    case Alignments.Sinistra:
                        location.X = margineUsato;
                        break;
                    case Alignments.Centro:
                        //location.X = image.Width - 
                        break;
                    case Alignments.Destra:
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
                image.Mutate(x => x.DrawImage(_logo, size, location, new GraphicsOptions()));




            }

        }

        private void WriteTextFixed(Image<Rgba32> image)
        {
            var fo = SixLabors.Fonts.SystemFonts.Find("Microsoft Sans Serif");
            var font = new Font(fo, 8, FontStyle.Regular);
            Rgba32 g = Rgba32.FromHex("#FF00FFFF");
            Vector2 center = new Vector2(image.Width / 2, image.Height / 2);
            var size = TextMeasurer.Measure("Test test test test test", new RendererOptions(font));
            float scalingFactor = Math.Min(image.Width / size.Width, image.Height / size.Height);
            Font scaledFont = new Font(font, scalingFactor * font.Size);
            image.Mutate(x => x.DrawText("Test test test test test", scaledFont, g, center, new TextGraphicsOptions(true) { HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Bottom }));
        }

        private string FlipRgbString(string originalString)
        {
            string s;
            if (originalString.Length == 7)
            {
                s = string.Concat("#", originalString.Substring(1, 6));
            }
            else
            {
                s = string.Concat("#", originalString.Substring(3, 6), originalString.Substring(1, 2));
            }


            return s;
        }
    }
}
