using System.Drawing;
using Android.Graphics;
using Android.Media;
using InABox.Core;
using Java.IO;
using Microsoft.Maui.Media;
using Microsoft.Maui.Storage;
using Bitmap = Android.Graphics.Bitmap;
using File = System.IO.File;
using Path = System.IO.Path;
using Stream = System.IO.Stream;

namespace InABox.Avalonia.Platform.Android
{
    public class Android_ImageTools : IImageTools
    {
        
        public Logger? Logger { get; set; }

        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);
            using (FileInputStream fs = new FileInputStream(filename))
            {
                FileDescriptor fd = fs.FD;
                MediaMetadataRetriever retriever = new MediaMetadataRetriever();
                retriever.SetDataSource(fd);
                Bitmap bitmap = retriever.GetFrameAtTime(0);
                if (bitmap != null)
                {
                    bitmap = ScaleImage(bitmap, new Size(maxwidth, maxheight));
                    MemoryStream stream = new MemoryStream();
                    bitmap.Compress(Bitmap.CompressFormat.Png, 60, stream);
                    result = stream.ToArray();
                }
            }
            File.Delete(filename);
            return result;
        }
        
        public byte[] CreateThumbnail(byte[] source, int maxwidth, int maxheight)
        {
            return ScaleImage(source, new Size(maxwidth, maxheight), 60);
        }
        
        public enum ImageOrientation
        {
            Undefined = 0,
            Normal = 1,
            FlipHorizontal = 2,
            Rotate180 = 3,
            FlipVertical = 4,
            Transpose = 5,
            Rotate90 = 6,
            Transverse = 7,
            Rotate270 = 8
        }
        
        public async Task<FileResult> PickPhotoAsync(int? compression, Size? constraints)
        {
            var fileResult = await MediaPicker.PickPhotoAsync();
            if (fileResult == null)
                return null;
            return await ProcessFile(fileResult, compression, constraints);
        }
        
        public async Task<FileResult> CapturePhotoAsync(int? compression, Size? constraints)
        {
            var fileResult = await MediaPicker.CapturePhotoAsync();
            if (fileResult == null)
                return null;
            return await ProcessFile(fileResult, compression, constraints);
        }

        private async Task<FileResult> ProcessFile(FileResult fileResult, int? compression, Size? constraints)
        {
            await using var stream = await fileResult.OpenReadAsync();

            var orientation = GetImageOrientation(stream);

            var source = await BitmapFactory.DecodeStreamAsync(stream);
            var rotated = RotateImage(source, orientation);

            var scaled = ScaleImage(rotated, constraints);

            var jpegFilename = Path.Combine(FileSystem.CacheDirectory, $"{Guid.NewGuid()}.jpg");
            using (var outStream = new MemoryStream())
            {
                await scaled.CompressAsync(Bitmap.CompressFormat.Jpeg, compression ?? 100, outStream);
                outStream.Position = 0;
                await File.WriteAllBytesAsync(jpegFilename, outStream.ToArray());
            }

            return new FileResult(jpegFilename);
        }

        private static Bitmap RotateImage(Bitmap source, ImageOrientation orientation)
        {
            var matrix = new Matrix();
            switch (orientation)
            {
                case ImageOrientation.Normal:
                    break;
                case ImageOrientation.FlipHorizontal:
                    break;
                case ImageOrientation.Rotate180:
                    break;
                case ImageOrientation.FlipVertical:
                    matrix.PreRotate(180);
                    break;
                case ImageOrientation.Transpose:
                    matrix.PreRotate(90);
                    break;
                case ImageOrientation.Rotate90:
                    matrix.PreRotate(90);
                    break;
                case ImageOrientation.Transverse:
                    matrix.PreRotate(-90);
                    break;
                case ImageOrientation.Rotate270:
                    matrix.PreRotate(-90);
                    break;
            }

            return Bitmap.CreateBitmap(
                source,
                0,
                0,
                source.Width,
                source.Height,
                matrix,
                true);
        }

        private ImageOrientation GetImageOrientation(Stream stream)
        {
            var exif = new ExifInterface(stream);
            var tag = exif.GetAttribute(ExifInterface.TagOrientation);
            var orientation = string.IsNullOrEmpty(tag) ?
                ImageOrientation.Undefined :
                (ImageOrientation)Enum.Parse(typeof(ImageOrientation), tag);
            exif.Dispose();
            stream.Position = 0;
            return orientation;
        }

        private static Bitmap ScaleImage(Bitmap source, Size? constraints)
        {
            var maxwidth = constraints?.Width ?? source.Width;
            var maxheight = constraints?.Height ?? source.Height;
            var wRatio = maxwidth < source.Width
                ? maxwidth / (double)source.Width
                : 1.0F;
            var hRatio = maxheight < source.Height
                ? maxheight / (double)source.Height
                : 1.0F;
            var ratio = Math.Min(hRatio, wRatio);
            var result = (ratio < 1.0F)
                ? Bitmap.CreateScaledBitmap(
                    source,
                    (int)(source.Width * ratio),
                    (int)(source.Height * ratio),
                    true)
                : source;
            return result;
        }

        public byte[] RotateImage(byte[] source, float angle, int compression = 100)
        {
            if (angle % 360 == 0)
                return source;
            byte[] result = { };
            using (var image = BitmapFactory.DecodeByteArray(source, 0, source.Length))
            {
                if (image != null)
                {
                    
                    var matrix = new Matrix();
                    matrix.PreRotate(angle);
                    
                    var rotated = Bitmap.CreateBitmap(
                        image,
                        0,
                        0,
                        image.Width,
                        image.Height,
                        matrix,
                        true);
                    
                    if (rotated != null)
                    {
                        using (var ms = new MemoryStream())
                        {
                            rotated.Compress(Bitmap.CompressFormat.Jpeg, compression, ms);
                            result = ms.ToArray();
                        }
                    }
                }
            }
            return result;
        }

        public byte[] ScaleImage(byte[] source, Size? constraints, int compression = 100)
        {
            byte[] result = { };
            using (var image = BitmapFactory.DecodeByteArray(source, 0, source.Length))
            {
                if (image != null)
                {
                    var scaled = ScaleImage(image, constraints);
                    using (var ms = new MemoryStream())
                    {
                        scaled.Compress(Bitmap.CompressFormat.Jpeg, compression, ms);
                        result = ms.ToArray();
                    }
                }
            }
            return result;
        }
    }

}