using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Media.Imaging;
using Valg.EVA.Skanning.Contracts.CommonEnums;
using Valg.EVA.Skanning.Identity;
using Valg.EVA.Skanning.Services.Verification;
using Valg.EVA.Skanning.ServicesModel.Internal.SystemSettings;
using Valg.EVA.Skanning.ServicesModel.ServiceContracts.Verification.Read;
using Point = System.Windows.Point;
using Size = System.Windows.Size;

namespace Valg.EVA.Skanning.UserInterfaceElements.BallotPaperImage
{
    public class BallotPaperImageLoader : IBallotPaperImageLoader
    {
        [DllImport("gdi32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool DeleteObject(IntPtr value);

        private readonly IVerificationService verificationService;
        private readonly IIdentityProvider identityProvider;

        public BallotPaperImageLoader(IVerificationService verificationService, IIdentityProvider identityProvider)
        {
            this.verificationService = verificationService;
            this.identityProvider = identityProvider;
        }

        public BallotPaperImageInformation GetBallotPaperBitmaps(int ballotPaperId, BallotPaperType? ballotPaperType)
        {
            var parameters = new GetBallotPaperImagesParameters(ballotPaperId, ballotPaperType, identityProvider.CurrentSelectedRole.CountingEmlAreaContextId);
            var imagesResult = verificationService.GetBallotPaperImages(parameters);

            return new BallotPaperImageInformation(
                GetBitmapFromTif(imagesResult.ImageFront, imagesResult.RotationFront),
                GetBitmapFromTif(imagesResult.ImageBack, imagesResult.RotationBack),
                imagesResult.BallotPaperLayoutDescription);
        }

        private BitmapFrame GetBitmapFromTif(byte[] imageBytes)
        {
            var tifStream = new MemoryStream(imageBytes);
            var decoder = new TiffBitmapDecoder(tifStream, BitmapCreateOptions.None, BitmapCacheOption.Default);
            var bitmapFrame = decoder.Frames[0];

            return bitmapFrame;
        }

        private BitmapSource GetBitmapFromTif(byte[] imageBytes, int rotation)
        {
            using (var stream = new MemoryStream(imageBytes))
            {
                var rotatedImage = new Bitmap(Image.FromStream(stream));
                RotateFlipType rotateFlipType;
                switch (rotation)
                {
                    case 90:
                        rotateFlipType = RotateFlipType.Rotate270FlipNone;
                        break;
                    case 180:
                        rotateFlipType = RotateFlipType.Rotate180FlipNone;
                        break;
                    case 270:
                        rotateFlipType = RotateFlipType.Rotate90FlipNone;
                        break;
                    default:
                        rotateFlipType = RotateFlipType.RotateNoneFlipNone;
                        break;
                }
                rotatedImage.RotateFlip(rotateFlipType);

                IntPtr bmpPt = rotatedImage.GetHbitmap();
                BitmapSource bitmapSource =
                    System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                        bmpPt,
                        IntPtr.Zero,
                        Int32Rect.Empty,
                        BitmapSizeOptions.FromEmptyOptions());

                
                bitmapSource.Freeze();
                DeleteObject(bmpPt);

                return bitmapSource;
            }
        }

        public CroppedBitmap CreateCroppedBitmap(BallotPaperContentAreaDescription contentAreaDescription, BallotPaperImageInformation bitmapInfo, int? totalNumberOfSections = null)
        {
            var source = contentAreaDescription.IsOnFrontPage ? bitmapInfo.Front : bitmapInfo.Back;
            var sizeOfImage = new Size(source.PixelWidth, source.PixelHeight);

            if (contentAreaDescription.TopLeftXInPercentRelativeToEntireSize.HasValue)
            {
                var sourceRect = GetRectangleBoundedClipGeometry(contentAreaDescription, sizeOfImage);
                return new CroppedBitmap(source, sourceRect);
            }
            else
            {
                var sectionFoldingAdjustmentSegment = bitmapInfo.LayoutDescription.ContentAreaDescriptions.SingleOrDefault(s => s.ContentAreaType == BallotPaperContentAreaType.SectionFoldingAdjustment);
                var foldingAdjustmentWidth = GetFoldingAdjustmentWidth(sectionFoldingAdjustmentSegment, contentAreaDescription);

                var rect = GetSectionDividedClipGeometry(totalNumberOfSections.Value, sizeOfImage, contentAreaDescription.SectionNumber.Value, foldingAdjustmentWidth);
                return new CroppedBitmap(source, rect);
            }
        }

        private static double GetFoldingAdjustmentWidth(BallotPaperContentAreaDescription sectionFoldingAdjustmentAreaDescription, BallotPaperContentAreaDescription contentAreaToDisplay)
        {
            double foldingAdjustmentWidth = 0.0;
            if (sectionFoldingAdjustmentAreaDescription != null)
            {
                foldingAdjustmentWidth = sectionFoldingAdjustmentAreaDescription.CropRegionWidthInPercentRelativeToEntireSize.Value;
            }

            if (contentAreaToDisplay.IsOnFrontPage)
            {
                return foldingAdjustmentWidth;
            }

            return foldingAdjustmentWidth * -1;
        }

        public Int32Rect GetRectangleBoundedClipGeometry(BallotPaperContentAreaDescription contentAreaDescription, Size sizeOfImage)
        {
            var x = (sizeOfImage.Width * (contentAreaDescription.TopLeftXInPercentRelativeToEntireSize / 100.0));
            var y = (sizeOfImage.Height * (contentAreaDescription.TopLeftYInPercentRelativeToEntireSize / 100.0));
            var height = (sizeOfImage.Height * (contentAreaDescription.CropRegionHeightInPercentRelativeToEntireSize / 100.0));
            var width = (sizeOfImage.Width * (contentAreaDescription.CropRegionWidthInPercentRelativeToEntireSize / 100.0));
            var sourceRect = new Int32Rect
            {
                X = (int)x,
                Y = (int)y,
                Height = (int)height,
                Width = (int)width
            };
            return sourceRect;
        }

        public Int32Rect GetSectionDividedClipGeometry(int numberOfSectionsOnBallotPaper, Size sizeOfOriginalImage, int sectionNumberRequested, double sectionFoldingAdjustmentWidth)
        {
            var sectionWidth = sizeOfOriginalImage.Width / numberOfSectionsOnBallotPaper;

            var topLeftCorner = GetTopLeftCorner(sectionNumberRequested, sectionWidth);

            var width = sectionWidth;
            var x = topLeftCorner.X;

            if (sectionFoldingAdjustmentWidth != 0)
            {
                var actualSizeOfFoldingSegment = sizeOfOriginalImage.Width * (sectionFoldingAdjustmentWidth / 100);

                var sectionRequestedIsLastOrNextToLast = (numberOfSectionsOnBallotPaper - sectionNumberRequested) < 2;

                if (!sectionRequestedIsLastOrNextToLast)
                {
                    
                    width = width - actualSizeOfFoldingSegment;
                }

                
                
                if (sectionRequestedIsLastOrNextToLast)
                {
                    x = x - actualSizeOfFoldingSegment;
                }
            }

            var rect = new Int32Rect
            {
                X = (int)x,
                Y = (int)topLeftCorner.Y,
                Height = (int)sizeOfOriginalImage.Height,
                Width = (int)width
            };

            return rect;
        }

        private static Point GetTopLeftCorner(int sectionNumberRequested, double sectionWidth)
        {
            var topLeftXCoordinate = (sectionNumberRequested - 1) * sectionWidth;
            return new Point(topLeftXCoordinate, 0);
        }
    }
}
