using System.Collections.Generic;
using OpenCvSharp;

namespace OCR
{
    internal static class RunLengthSmearing
    {
        /// <summary>
        /// Finds text regions
        /// </summary>
        /// <param name="src">binary image</param>
        /// <returns>list of rectangles containing text</returns>
        public static List<Rect> GetRLSABoundingBoxes(Mat src)
        {
            //morphology dilate to get rid of one pixel borders of reverted areas
            Mat morphology = src.Clone();
            Mat structuringElement = Cv2.GetStructuringElement(MorphShapes.Cross, new Size(2,2));
            Cv2.MorphologyEx(src.Clone(), morphology, MorphTypes.Dilate, structuringElement, null, 1, BorderTypes.Constant, null);

            // add borders to remove joining text with edges of image
            Mat morphWithBorder = new Mat();
            Cv2.CopyMakeBorder(morphology, morphWithBorder, 10, 10, 15, 15, BorderTypes.Constant, 255);

            //apply RLSA algorithm on dilated image and remove borders
            Mat rlsaImage = ApplyRLSA(morphWithBorder, 10, 5);
            Mat image = rlsaImage.SubMat(10, rlsaImage.Rows - 10, 15, rlsaImage.Cols - 15);

            Mat edges = new Mat(image.Size(), image.Type());
            Cv2.Canny(image, edges, 50.0, 200.0, 3, true);

            Mat hierarchy = new Mat();
            Cv2.FindContours(image, out Mat[] contours, hierarchy, RetrievalModes.CComp, ContourApproximationModes.ApproxSimple, null);
            Mat showRetanglesOnOriginal = src.Clone().CvtColor(ColorConversionCodes.GRAY2BGR);
            List<Rect> boundingBoxes = new List<Rect>(contours.Length);
            foreach (Mat contour in contours) {
                Rect box = Cv2.BoundingRect(contour);
                if (box.Width > 4 && box.Height > 10 && box.Width < 0.9 * image.Width && box.Height < 0.9 * image.Height) {
                    boundingBoxes.Add(box);
                    Cv2.Rectangle(showRetanglesOnOriginal, box.TopLeft, box.BottomRight, Scalar.Red);
                }
            }
            new Window("rectangles", showRetanglesOnOriginal);
            Cv2.WaitKey(1);
            return boundingBoxes;
        }


        /// <summary>
        /// Applies Run Length Smearing Algorithm on binary image
        /// </summary>
        /// <param name="src">binary image</param>
        /// <param name="horThresh">threshold for horizontal RLSA, usually horThr>vertThr</param>
        /// <param name="vertThresh">threshold for vertical RLSA</param>
        /// <returns>binary image after RLSA application</returns>
        private static Mat ApplyRLSA(Mat src, int horThresh, int vertThresh)
        {
            Mat image = src.Clone();
            image = HorizontalRLSA(image, horThresh);
            return VerticalRLSA(image, vertThresh);
        }


        private static Mat HorizontalRLSA(Mat src, int horThresh)
        {
            Mat image = src;
            int consecOnes = 0;
            int flag = 0;
            for (int i = 0; i < src.Rows; i++) {
                for (int j = 0; j < src.Cols; j++) {
                    if (src.At<byte>(i, j) == 0) {
                        if (flag == 1) {
                            if (consecOnes <= horThresh)
                                image[new Range(i, i + 1), new Range(j - consecOnes, j)].SetTo(Scalar.All(0.0), null);
                            flag = 0;
                            consecOnes = 0;
                        }
                    } else {
                        flag = 1;
                        consecOnes++;
                    }
                }
                consecOnes = 0;
                flag = 0;
            }
            return image;
        }


        private static Mat VerticalRLSA(Mat src, int vertThresh)
        {
            Mat image = src;
            int consecOnes = 0;
            int flag = 0;
            for (int i = 0; i < src.Cols; i++) {
                for (int j = 0; j < src.Rows; j++) {
                    if (src.At<byte>(j, i) == 0) {
                        if (flag == 1) {
                            if (consecOnes <= vertThresh)
                                image[new Range(j - consecOnes, j), new Range(i, i + 1)].SetTo(Scalar.All(0.0), null);
                            flag = 0;
                            consecOnes = 0;
                        }
                    } else {
                        flag = 1;
                        consecOnes++;
                    }
                }
                consecOnes = 0;
                flag = 0;
            }
            return image;
        }
    }
}