using System;
using System.Collections.Generic;
using System.Drawing;

namespace NeuralNetwork.PictureWork
{
    public static class PictureWork
    {
        /// <summary>
        /// Заменяет прозрачные области белым цветом.
        /// </summary>
        /// <param name="src">Исходное изображение</param>
        /// <returns></returns>
        public static Bitmap RemoveTransparency(Bitmap src)
        {
            Bitmap target = new Bitmap(src.Size.Width, src.Size.Height);
            Graphics g = Graphics.FromImage(target);
            g.Clear(Color.White);
            g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
            g.DrawImage(src, 0, 0);
            return target;
        }

        /// <summary>
        /// Выполняет поиск прямоугольных областей на рисунке, содержащих объекты.
        /// </summary>
        /// <param name="m">Матрица яркости пикселей значениями от 0 до 1.</param>
        /// <returns>Возвращает массив прямоугольных областей, где были найдены объекты.</returns>
        public static Rectangle[] FindObjects(float[][] m)
        {
            int y1 = -1, y2 = -1;
            for (int i = 0; i < m.Length && y1 == -1; i++)
                for (int j = 0; j < m[i].Length; j++)
                    if ((int)Math.Round(m[i][j]) == 1)
                    {
                        y1 = i;
                        break;
                    }

            for (int i = m.Length - 1; i > y1 && y2 == -1; i--)
                for (int j = 0; j < m[i].Length; j++)
                    if ((int)Math.Round(m[i][j]) == 1)
                    {
                        y2 = i;
                        break;
                    }
            int xEnd = 0;
            List<Rectangle> digits = new List<Rectangle>();
            Rectangle current;
            while (true)
            {
                current = FindObject(m, y1, y2, xEnd, ref xEnd);
                if (current != Rectangle.Empty)
                    digits.Add(current);
                else
                    break;
            }
            return digits.ToArray();
        }

        /// <summary>
        /// Выполняет поиск объекта среди всей матрицы яркости в заданных границах
        /// </summary>
        /// <param name="m">Матрица яркости пикселей значениями от 0 до 1.</param>
        /// <param name="y1">Верхняя граница поиска по высоте.</param>
        /// <param name="y2">Нижняя граница поиска по высоте.</param>
        /// <param name="xStart">Крайняя левая граница поиска по ширине.</param>
        /// <param name="xEnd">Крайняя правая граница поиска по ширине.</param>
        /// <returns>Возвращает прямоугольную область - границы найденного изображения.</returns>
        public static Rectangle FindObject(float[][] m, int y1, int y2, int xStart, ref int xEnd)
        {
            int x1 = -1, x2 = -1;
            for (int j = xStart; j < m[0].Length && x1 == -1; j++)
                for (int i = y1; i < y2; i++)
                    if ((int)Math.Round(m[i][j]) == 1)
                    {
                        x1 = j;
                        break;
                    }
            if (x1 != -1)
            {
                for (int j = x1 + 1, k; j < m[0].Length; j++)
                {
                    k = 0;
                    for (int i = y1; i < y2; i++)
                        k += (int)Math.Round(m[i][j]);
                    if (k == 0)
                    {
                        x2 = j - 1;
                        break;
                    }
                }
                if (x2 == -1)
                    x2 = m.Length - 1;
                xEnd = x2 + 1;
                return new Rectangle(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
            }
            return Rectangle.Empty;
        }

        /// <summary>
        /// Выполняет конвертацию изображения в матрицу яркости.
        /// </summary>
        /// <param name="bmp">Исходное изображение.</param>
        /// <returns>Возвращает матрицу яркости пикселя значениями от 0 до 1.</returns>
        public unsafe static float[][] BitmapToFloatMatrixRgbQ(Bitmap bmp)
        {
            int width = bmp.Width,
                height = bmp.Height;
            System.Drawing.Imaging.BitmapData bd = bmp.LockBits(
                new Rectangle(0, 0, width, height),
                System.Drawing.Imaging.ImageLockMode.ReadOnly,
                System.Drawing.Imaging.PixelFormat.Format32bppRgb);

            float[][] matrix = new float[height][];
            try
            {
                unsafe
                {
                    for (int h = 0; h < height; ++h)
                    {
                        matrix[h] = new float[width];
                        byte* row = (byte*)bd.Scan0 + (h * bd.Stride);
                        int columnOffset = 0;
                        for (int w = 0; w < width; ++w)
                        {
                            byte B = row[columnOffset];
                            byte G = row[columnOffset + 1];
                            byte R = row[columnOffset + 2];
                            columnOffset += 4;

                            matrix[h][w] = (255f - (float)Math.Round((R + G + B) / 3f)) / 256f;
                        }
                    }
                }

            }
            finally
            {
                bmp.UnlockBits(bd);
            }

            return matrix;
        }

        /// <summary>
        /// Выполняет конвертацию изображения в матрицу яркости.
        /// </summary>
        /// <param name="bmp">Исходное изображение.</param>
        /// <returns>Возвращает матрицу яркости пикселя значениями от 0 до 1.</returns>
        public unsafe static float[] BitmapToFloatArrayRgbQ(Bitmap bmp)
        {
            int width = bmp.Width,
                height = bmp.Height;
            System.Drawing.Imaging.BitmapData bd = bmp.LockBits(
                new Rectangle(0, 0, width, height),
                System.Drawing.Imaging.ImageLockMode.ReadOnly,
                System.Drawing.Imaging.PixelFormat.Format32bppRgb);

            float[] _array = new float[height * width];
            try
            {
                unsafe
                {
                    for (int h = 0; h < height; ++h)
                    {
                        byte* row = (byte*)bd.Scan0 + (h * bd.Stride);
                        int columnOffset = 0;
                        for (int w = 0; w < width; ++w)
                        {
                            byte B = row[columnOffset];
                            byte G = row[columnOffset + 1];
                            byte R = row[columnOffset + 2];
                            columnOffset += 4;

                            _array[h * height + w] = (255f - (float)Math.Round((R + G + B) / 3f)) / 256f;
                        }
                    }
                }

            }
            finally
            {
                bmp.UnlockBits(bd);
            }
            return _array;
        }
    }
}
