using System;
using System.Collections.Generic;
using System.Linq;
using OpenCvSharp;
using Tesseract;
using Rect = OpenCvSharp.Rect;

namespace InterpreterLibrary.Processing
{
    static class MatExtensions
    {
        public static Mat Deskew(this Mat image)
        {
            
            using (var pix = Pix.LoadTiffFromMemory(image.ToBytes("*.tiff")))
            {
                using (var deskewed = pix.Deskew())
                {
                    using (var binarized = deskewed.BinarizeSauvola(10, 0, true))
                    {
                        return Mat.FromImageData(binarized.SaveToMemory(ImageFormat.Png), ImreadModes.GrayScale);
                    }
                }
            }
        }

        public static Mat Rotate(this Mat image, int numberOfQuarterTurns)
        {
            switch (numberOfQuarterTurns % 4)
            {
                case 0:
                    return image;
                case 1:
                    return image.RotateClockwise();
                case 2:
                    return image.RotateClockwise().RotateClockwise();
                default:
                    return image.RotateCounterClockwise();
            }
        }
        private static Mat RotateClockwise(this Mat image)
        {
            var ret = new Mat(image.Width, image.Height, image.Type());
            Cv2.Transpose(image, ret);
            Cv2.Flip(ret, ret, FlipMode.Y);
            return ret;
        }
        private static Mat RotateCounterClockwise(this Mat image)
        {
            var ret = new Mat(image.Width, image.Height, image.Type());
            Cv2.Transpose(image, ret);
            Cv2.Flip(ret, ret, FlipMode.X);
            return ret;
        }
        public static double GetMaxFillPercentage(this Mat image, Rect rectangle, int columns, int rows)
        {
            var fillPercentages = GetMaxFillPercentageWithinFrameWithRectangles(image, rectangle, CalculateRectangles(rectangle, columns, rows));
            return fillPercentages.Max(p => p.Value);
        }

        private static Dictionary<Rect, double> GetMaxFillPercentageWithinFrameWithRectangles(this Mat image,
            Rect frame, IEnumerable<Rect> rectangles)
        {
            var fillPercentages = new Dictionary<Rect, double>();

            foreach (var rectangle in rectangles)
            {
                var adjustedRectangle = new Rect(frame.X + rectangle.X, frame.Y + rectangle.Y, rectangle.Width, rectangle.Height);

                lock (fillPercentages)
                {
                    fillPercentages.Add(adjustedRectangle, GetPercentageOfNonWhitePixelsInImage(image, adjustedRectangle));
                }
            }
            return fillPercentages;
        }
        public static double GetFillPercentage(this Mat image)
        {
            return GetPercentageOfNonWhitePixelsInImage(image,
                new Rect(0, 0, image.Width, image.Height));
        }

        private static double GetPercentageOfNonWhitePixelsInImage(this Mat img, Rect rectangle)
        {
            var total = rectangle.Width * rectangle.Height;
            var nonBlackPixels = Cv2.CountNonZero(img.SubMat(rectangle));
            return 100d * (1d - (double)nonBlackPixels / total);
        }
        public static double GetNumberOfNonWhitePixelsInImage(this Mat img, Rect rectangle)
        {
            var total = rectangle.Width * rectangle.Height;
            var nonBlackPixels = Cv2.CountNonZero(img.SubMat(rectangle));
            return total - nonBlackPixels;
        }

        private static IEnumerable<Rect> CalculateRectangles(Rect rectangle, int columns, int rows)
        {
            var rectangles = new List<Rect>();
            var columnWidth = rectangle.Width / columns;
            var rowHeight = rectangle.Height / rows;
            for (var i = 0; i < columns; i++)
                for (var j = 0; j < rows; j++)
                    rectangles.Add(new Rect(i * columnWidth, j * rowHeight,
                        columnWidth, rowHeight));
            return rectangles;
        }

        public static float GetHorizontalMaxDensity(this Mat bitmap)
        {
            var indexer = bitmap.GetGenericIndexer<byte>();
            var black = byte.MinValue;
            var total = bitmap.Width;

            var ret = 0f;
            foreach (var y in new[] { bitmap.Height * 2 / 3, bitmap.Height / 2, bitmap.Height / 3 })
            {
                var blackPixels = 0;

                for (var x = 0; x < bitmap.Width; x++)
                {
                    var pixel = indexer[y, x];
                    if (pixel == black)
                    {
                        ++blackPixels;
                    }
                }
                var density = 100f * blackPixels / total;
                ret = Math.Max(ret, density);
            }
            return ret;
        }

    }
}

