using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.IO;
using System.Linq;
using ImageMagick;
using System.Threading.Tasks;
using System.Drawing;

namespace Kehyeedra3
{
    public static class Extensions
    {
        private static DateTime YeedraTime = new DateTime(2020, 2, 2, 0, 0, 0, DateTimeKind.Utc);

        public static ulong ToYeedraStamp(this DateTime time)
            => (ulong)(time.Subtract(YeedraTime)).TotalSeconds;

        public static DateTime FromYeedraStamp(this ulong time)
            => YeedraTime.AddSeconds(Convert.ToDouble(time));

        public static string ToYeedraDisplay(this long number)
        {
            double numb = (double)number/10000;
            return numb.ToString("N4");
        }

        public static double Remap(this int value, double min1, double max1, double min2, double max2)
            => ((double)value).Remap(min1, max1, min2, max2);

        public static double Remap(this ulong value, double min1, double max1, double min2, double max2)
            => ((double)value).Remap(min1, max1, min2, max2);

        public static double Remap(this double value, double min1, double max1, double min2, double max2)
            => min2 + (max2 - min2) * ((value - min1) / (max1 - min1));

        //https://stackoverflow.com/a/1262619
        public static void Shuffle<T>(this IList<T> list)
        {
            using (RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider())
            {
                int n = list.Count;
                while (n > 1)
                {
                    byte[] box = new byte[1];
                    do provider.GetBytes(box);
                    while (!(box[0] < n * (byte.MaxValue / n)));
                    int k = (box[0] % n);
                    n--;
                    T value = list[k];
                    list[k] = list[n];
                    list[n] = value;
                }
            }
        }

        public static async Task<MagickImageCollection> GifPngMergeAsync(MagickImage pngFile, string gifFile, Rectangle rect, PixelInterpolateMethod interpolateMethod = PixelInterpolateMethod.Bilinear, bool trimEmpty = false)
            => await GifPngMergeAsync(pngFile, new FileStream(gifFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), rect, interpolateMethod, trimEmpty);

        public static async Task<MagickImageCollection> GifPngMergeAsync(Stream pngFile, string gifFile, Rectangle rect, PixelInterpolateMethod interpolateMethod = PixelInterpolateMethod.Bilinear, bool trimEmpty = false)
            => await GifPngMergeAsync(new MagickImage(pngFile), new FileStream(gifFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), rect, interpolateMethod, trimEmpty);

        public static async Task<MagickImageCollection> GifPngMergeAsync(string pngFile, Stream gifFile, Rectangle rect, PixelInterpolateMethod interpolateMethod = PixelInterpolateMethod.Bilinear, bool trimEmpty = false)
            => await GifPngMergeAsync(new MagickImage(pngFile), gifFile, rect, interpolateMethod, trimEmpty);

        public static async Task<MagickImageCollection> GifPngMergeAsync(string pngFile, string gifFile, Rectangle rect, PixelInterpolateMethod interpolateMethod = PixelInterpolateMethod.Bilinear, bool trimEmpty = false)
            => await GifPngMergeAsync(new MagickImage(pngFile), new FileStream(gifFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), rect, interpolateMethod, trimEmpty);

        public static async Task<MagickImageCollection> GifPngMergeAsync(MagickImage pngFile, Stream gifFile, Rectangle rect, PixelInterpolateMethod interpolateMethod = PixelInterpolateMethod.Bilinear, bool trimEmpty = false)
        {
            try
            {
                MagickImageCollection pngCollection = new MagickImageCollection();

                using MagickImageCollection gifCollection = new MagickImageCollection(gifFile);

                int[] delay = new int[gifCollection.Count];

                int indx = 0;
                foreach (var frame in gifCollection)
                {
                    MagickImage pngTemplate = new MagickImage(pngFile);
                    using MagickImage gifFrame = new MagickImage(frame);

                    gifFrame.Alpha(AlphaOption.Set);

                    if (trimEmpty)
                    {
                        gifFrame.BackgroundColor = MagickColors.None;
                        gifFrame.ColorFuzz = new Percentage(1);
                        gifFrame.Trim();
                        gifFrame.RePage();
                    }

                    var size = GetSize(gifFrame, rect);
                    gifFrame.InterpolativeResize(size.Width, size.Height, interpolateMethod);

                    size.X = (rect.Width + rect.X) / 2 - (gifFrame.Width / 2);
                    size.Y = (rect.Height + rect.Y) / 2 - (gifFrame.Height / 2);

                    pngTemplate.Composite(gifFrame, size.X, size.Y, CompositeOperator.Over);

                    pngCollection.Add(pngTemplate);
                    delay[indx] = gifFrame.AnimationDelay;

                    indx++;
                }

                pngCollection[0].AnimationDelay = 0;
                for (int index = 1; index < pngCollection.Count - 1; index++)
                {
                    pngCollection[index].AnimationDelay = delay[index];
                }

                if (pngCollection.Count > 1)
                {
                    QuantizeSettings settings = new QuantizeSettings()
                    {
                        Colors = 2048
                    };
                    pngCollection.Quantize(settings);
                }

                return pngCollection;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error processing; Exception = {ex}");
            }

            return null;
        }

        static Rectangle GetSize(MagickImage img, Rectangle rect)
        {
            Rectangle output = new Rectangle(rect.X, rect.Y, 0, 0);

            double aspectRatio = (double)img.Width / img.Height;

            double otherAspect = (double)img.Height / img.Width;

            output.Width = (int)Math.Min(Math.Round(rect.Width - rect.X * aspectRatio), rect.Width - rect.X);

            output.Height = (int)(output.Width * otherAspect);

            return output;
        }
    }
}