
using System;
using System.Drawing;
using System.IO;
using System.Threading.Tasks;
using AVFoundation;
using CoreGraphics;
using CoreMedia;
using Foundation;
using UIKit;
using Xamarin.Essentials;
using MobileCoreServices;

[assembly: Xamarin.Forms.Dependency(typeof(InABox.Mobile.iOS.ImageToolsiOS))]
namespace InABox.Mobile.iOS
{
    
    public class ImageToolsiOS : IImageTools
    {
        public byte[] CreateVideoThumbnail(byte[] video, int maxwidth, int maxheight)
        {
            byte[] result = null;
            var filename = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                $"{Guid.NewGuid().ToString()}.tmp"
            );
            File.WriteAllBytes(filename,video);
            // perhaps? on IOS, try to use - NSUrl.FromFilename (path) instead of - new Foundation.NSUrl(url)
            var asset = AVAsset.FromUrl(NSUrl.FromFilename(filename));
            AVAssetImageGenerator imageGenerator = new AVAssetImageGenerator(asset);
            //AVAssetImageGenerator imageGenerator = new AVAssetImageGenerator(AVAsset.FromUrl((new Foundation.NSUrl(filename))));
            imageGenerator.AppliesPreferredTrackTransform = true;
            CMTime actualTime;
            NSError error;
            CGImage cgImage = imageGenerator.CopyCGImageAtTime(new CMTime(1, 1), out actualTime, out error);
            using (var ms = new MemoryStream())
            {
                var png = new UIImage(cgImage).AsPNG().AsStream();
                png.CopyTo(ms);
                result = ms.ToArray();
            }

            if (result != null)
                result = ScaleImage(result, new Size(maxwidth, maxheight), 60);
            
            File.Delete(filename);
            return result;
        }
        
        public byte[] CreateThumbnail(byte[] source, int maxwidth, int maxheight)
        {
            return ScaleImage(source, new Size(maxwidth, maxheight), 60);
        }
        
        public async Task<FileResult> PickPhotoAsync(int? compression, Size? constraints)
        {
            return await MainThread.InvokeOnMainThreadAsync(async () =>
                await InternalGetPhotoAsync<Permissions.Photos>(UIImagePickerControllerSourceType.PhotoLibrary, compression, constraints));
        }
        
         public async Task<FileResult> CapturePhotoAsync(int? compression, Size? constraints)
        {
            return await MainThread.InvokeOnMainThreadAsync(async () =>
                await InternalGetPhotoAsync<Permissions.Camera>(UIImagePickerControllerSourceType.Camera, compression, constraints));
        }

        private async Task<FileResult> InternalGetPhotoAsync<TPermission>(UIImagePickerControllerSourceType source, int? compression, Size? constraints)
            where TPermission : Permissions.BasePermission, new()
        {
            var taskCompletionSource = new TaskCompletionSource<FileResult>();
            if (await Permissions.RequestAsync<TPermission>() == PermissionStatus.Granted)
            {
                var imagePicker = new UIImagePickerController
                {
                    SourceType = source,
                    MediaTypes = new string[] { UTType.Image }
                };
                
                var viewController = Platform.GetCurrentUIViewController();

                imagePicker.AllowsEditing = false;
                imagePicker.FinishedPickingMedia += async (sender, e) =>
                {
                    var jpegFilename = Path.Combine(FileSystem.CacheDirectory, $"{Guid.NewGuid()}.jpg");
                    var source = e.Info[UIImagePickerController.OriginalImage] as UIImage;
                    var rotated = AutoRotateImage(source);
                    var scaled = ScaleImage(rotated, constraints);
                    var result = scaled.AsJPEG(new nfloat(compression ?? 100)/100);
                    await viewController.DismissViewControllerAsync(true);
                    if (result.Save(jpegFilename, false, out var error))
                    {
                        taskCompletionSource.TrySetResult(new FileResult(jpegFilename));
                    }
                    else
                    {
                        taskCompletionSource.TrySetException(new Exception($"Error saving the image: {error}"));
                    }
                    imagePicker?.Dispose();
                    imagePicker = null;
                };

                imagePicker.Canceled += async (sender, e) =>
                {
                    await viewController.DismissViewControllerAsync(true);
                    taskCompletionSource.TrySetResult(null);
                    imagePicker?.Dispose();
                    imagePicker = null;
                };

                await viewController.PresentViewControllerAsync(imagePicker, true);
            }
            else
            {
                taskCompletionSource.TrySetResult(null);
                taskCompletionSource.TrySetException(new PermissionException("Camera permission not granted"));
            }

            return await taskCompletionSource.Task;
        }
        
        private UIImage AutoRotateImage(UIImage source)
        {
            var rotation = source.Orientation switch
            {
                UIImageOrientation.Right => 90F,
                UIImageOrientation.Up => 0F,
                UIImageOrientation.Left => -90F,
                UIImageOrientation.Down => 180F,
                _ => 0F
            };

            return RotateImage(source, rotation);
        }

        private UIImage RotateImage(UIImage source, float rotation)
        {
            UIImage imageToReturn = null;
            float radians = -1 * ((float)(rotation * Math.PI) / 180);
            bool isLandscape = false;

            var x = source.Size.Width / 2;
            var y = source.Size.Height / 2;

            //https://stackoverflow.com/a/8536553
            CGAffineTransform transform = new CGAffineTransform((nfloat)Math.Cos(radians), (nfloat)Math.Sin(radians), -(nfloat)Math.Sin(radians), (nfloat)Math.Cos(radians), (nfloat)(x - x * Math.Cos(radians)) + (nfloat)(y * Math.Sin(radians)), (nfloat)(y - x * Math.Sin(radians) - y * Math.Cos(radians)));

            var diff = (source.Size.Height - source.Size.Width) / 2;
            bool translateWidthAndHeight = false;
            if (rotation == 90)
            {
                translateWidthAndHeight = true;

                transform.Translate(diff, -diff);
            }
            else if (rotation == 180)
            {
                //Transform.Translate(image.Size.Width, -image.Size.Height);
            }
            else if (rotation == 270)
            {
                translateWidthAndHeight = true;
                transform.Translate(diff, -diff);
            }
            else if (rotation == 360)
            {

            }

            if (translateWidthAndHeight)
            {
                //now draw image
                using (var context = new CGBitmapContext(IntPtr.Zero,
                                                        (int)source.Size.Height,
                                                        (int)source.Size.Width,
                                                        source.CGImage.BitsPerComponent,
                                                        source.CGImage.BitsPerComponent * (int)source.Size.Width,
                                                        source.CGImage.ColorSpace,
                                                        source.CGImage.BitmapInfo))
                {
                    context.ConcatCTM(transform);
                    context.DrawImage(new RectangleF(PointF.Empty, new SizeF((float)source.Size.Width, (float)source.Size.Height)), source.CGImage);

                    using (var imageRef = context.ToImage())
                    {
                        imageToReturn = new UIImage(imageRef);
                    }
                }
            }
            else
            {
                //now draw image
                using (var context = new CGBitmapContext(IntPtr.Zero,
                                                        (int)source.Size.Width,
                                                        (int)source.Size.Height,
                                                        source.CGImage.BitsPerComponent,
                                                        source.CGImage.BitsPerComponent * (int)source.Size.Height,
                                                        source.CGImage.ColorSpace,
                                                        source.CGImage.BitmapInfo))
                {
                    context.ConcatCTM(transform);
                    context.DrawImage(new RectangleF(PointF.Empty, new SizeF((float)source.Size.Width, (float)source.Size.Height)), source.CGImage);

                    using (var imageRef = context.ToImage())
                    {
                        imageToReturn = new UIImage(imageRef);
                    }
                }
            }

            return imageToReturn;
        }

        private UIImage ScaleImage(UIImage sourceImage, Size? constraints)
        {
            var maxwidth = constraints?.Width ?? sourceImage.Size.Width;
            var maxheight = constraints?.Height ?? sourceImage.Size.Height;
            var wRatio = maxwidth < sourceImage.Size.Width
                ? maxwidth / (double)sourceImage.Size.Width
                : 1.0F;
            var hRatio = maxheight < sourceImage.Size.Height
                ? maxheight / (double)sourceImage.Size.Height
                : 1.0F;
            var ratio = Math.Min(hRatio, wRatio);
            if (ratio < 1.0F)
            {
                var width = ratio * sourceImage.Size.Width;
                var height = ratio * sourceImage.Size.Height;
                UIGraphics.BeginImageContext(new CGSize(width, height));
                sourceImage.Draw(new CGRect(0, 0, width, height));
                var resultImage = UIGraphics.GetImageFromCurrentImageContext();
                UIGraphics.EndImageContext();
                return resultImage;
            }
            return sourceImage;
        }

        public byte[] RotateImage(byte[] source, float angle, int quality = 100)
        {
            byte[] result = { };
            using (UIImage src = UIImage.LoadFromData(NSData.FromArray(source)))
            {
                if (src != null)
                {
                    var scaled = RotateImage(src, angle);
                    using (NSData imageData = scaled.AsJPEG(new nfloat((float)quality / 100F)))
                    {
                        result = new byte[imageData.Length];
                        System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, result, 0,
                            Convert.ToInt32(imageData.Length));
                    }
                }
            }
            return result;
        }

        public byte[] ScaleImage(byte[] source, Size? constraints, int quality = 100)
        {
            byte[] result = { };
            using (UIImage src = UIImage.LoadFromData(NSData.FromArray(source)))
            {
                if (src != null)
                {
                    var scaled = ScaleImage(src, constraints);
                    using (NSData imageData = scaled.AsJPEG(new nfloat((float)quality / 100F)))
                    {
                        result = new byte[imageData.Length];
                        System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, result, 0,
                            Convert.ToInt32(imageData.Length));
                    }
                }
            }
            return result;
        }
    }

    
}