using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace CodeIdentification
{
    class WAPcode
    {
        /// <summary>
        /// 图片识别入口
        /// </summary>
        /// <param name="Picbit"></param>
        /// <returns></returns>
        public static string GetCode(Bitmap Picbit)
        {
            try
            {
                Bitmap bm = WAPcode.CleanPic(WAPcode.Binaryzation(Picbit));
                int[,] position = WAPcode.DividePic(bm);
                string resstr = "";
                for (int i = 0; i < 4; i++)
                {
                    Bitmap newImg = new Bitmap(position[i, 1] - position[i, 0], position[i, 3] - position[i, 2]);
                    Graphics g = Graphics.FromImage(newImg);
                    g.DrawImage(bm, 0, 0, new Rectangle(position[i, 0], position[i, 2], position[i, 1] - position[i, 0], position[i, 3] - position[i, 2]), GraphicsUnit.Pixel);
                    resstr += GetChar(newImg);
                }
                return resstr;
            }
            catch
            {
                return "error";
            }
        }

        /// <summary>
        /// 识别已经分开的单字符
        /// </summary>
        /// <param name="bm"></param>
        /// <returns></returns>
        public static string GetChar(Bitmap bm)
        {
            double max = 0;
            string maxchar = "";

            var file = File.Open("sepcode.txt", FileMode.Open);
            List<string> txt = new List<string>();
            using (var stream = new StreamReader(file))
            {
                while (!stream.EndOfStream)
                {
                    txt.Add(stream.ReadLine());
                }
            }
            int maxindex = 0;
            int tempi = 0;
            foreach (string str in txt)
            {
                tempi++;
                double ts = Similarity(str.Substring(0, str.Length - 2), ConvertToStr(bm));
                //Console.WriteLine(str.Substring(str.Length - 1, 1) +" " + ts.ToString()  + " ");
                if (ts > max)
                {
                    max = ts;
                    maxchar = str.Substring(str.Length - 1, 1);
                    maxindex = tempi;
                }
            }
            //Console.WriteLine("最近似的index为"+maxindex.ToString()+" 概率为：" + max.ToString());
            return maxchar;

        }


        /// <summary>
        /// 图像二值化1：取图片的平均灰度作为阈值，低于该值的全都为0，高于该值的全都为255
        /// </summary>
        /// <param name="bmp"></param>
        /// <returns></returns>
        public static Bitmap Binaryzation(Bitmap bmp)
        {
            int average = 120;
            for (int i = 0; i < bmp.Width; i++)
            {
                for (int j = 0; j < bmp.Height; j++)
                {
                    //获取该点的像素的RGB的颜色
                    Color color = bmp.GetPixel(i, j);
                    int value = (color.R + color.B + color.G) / 3;
                    Color newColor = value < average ? Color.FromArgb(0, 0, 0) : Color.FromArgb(255, 255, 255);
                    bmp.SetPixel(i, j, newColor);
                }
            }
            return bmp;
        }

        /// <summary>
        /// 图片去边界去噪
        /// </summary>
        /// <param name="bm"></param>
        /// <returns></returns>
        public static Bitmap CleanPic(Bitmap bm)
        {
            //教务验证码去除边界
            for (int i = 0; i < bm.Height; i++)
            {
                bm.SetPixel(0, i, Color.White);
                bm.SetPixel(bm.Width - 1, i, Color.White);
            }
            for (int i = 0; i < bm.Width; i++)
            {
                bm.SetPixel(i, 0, Color.White);
                bm.SetPixel(i, bm.Height - 1, Color.White);
            }

            //教务验证码去除干扰点
            for (int i = 1; i < bm.Height - 1; i++)
            {
                for (int j = 1; j < bm.Width - 1; j++)
                {
                    if (bm.GetPixel(j, i).R == 0 && bm.GetPixel(j - 1, i).R != 0 && bm.GetPixel(j, i - 1).R != 0 && bm.GetPixel(j, i + 1).R != 0 && bm.GetPixel(j + 1, i).R != 0)
                    {
                        bm.SetPixel(j, i, Color.White);
                    }
                }
            }
            return bm;

        }

        /// <summary>
        /// 切分图片字符
        /// </summary>
        /// <param name="bm"></param>
        /// <returns></returns>
        public static int[,] DividePic(Bitmap bm)
        {
            int[] arrayInt = new int[bm.Width];
            for (int i = 0; i < bm.Width; i++)
            {
                int count = 0;
                for (int j = 0; j < bm.Height; j++)
                {
                    if (bm.GetPixel(i, j).R == 0)
                        count++;
                }
                arrayInt[i] = count;
                //Console.Write(count.ToString() +" ");
            }
            int start1 = 0, start2 = 0, start3 = 0, start4 = 0;
            int end1 = 0, end2 = 0, end3 = 0, end4 = 0;
            int up1 = 0, up2 = 0, up3 = 0, up4 = 0;
            int down1 = 20, down2 = 20, down3 = 20, down4 = 20;
            int signx = 0;//当前x轴标识
            int signy = 0; //当前y轴标识
            int minwidth = 2;//允许最小宽度
            int minspace = 1;//允许最小宽度
            for (int i = 0; i < bm.Width; i++)
            {
                if (arrayInt[i] > 0 && signx == 0)
                {
                    start1 = i;
                    signx = 1;
                }

                if (arrayInt[i] == 0 && signx == 1)
                {
                    if (i - start1 >= minwidth)
                    {
                        end1 = i;
                        signx = 2;
                    }
                    else
                    {
                        i = start1 + 1;
                        signx = 0;
                    }
                }
                if (arrayInt[i] > 0 && signx == 2)
                {
                    if (i - end1 >= minspace)
                    {
                        start2 = i;
                        signx = 3;
                    }
                    else
                    {
                        i = end1 + 1;
                        signx = 1;
                    }
                }
                if (arrayInt[i] == 0 && signx == 3)
                {
                    if (i - start2 >= minwidth)
                    {
                        end2 = i;
                        signx = 4;
                    }
                    else
                    {
                        i = start2 + 1;
                        signx = 2;
                    }
                }

                if (arrayInt[i] > 0 && signx == 4)
                {
                    if (i - end2 >= minspace)
                    {
                        start3 = i;
                        signx = 5;
                    }
                    else
                    {
                        i = end2 + 1;
                        signx = 3;
                    }
                }

                if (arrayInt[i] == 0 && signx == 5)
                {
                    if (i - start3 >= minwidth)
                    {
                        end3 = i;
                        signx = 6;
                    }
                    else
                    {
                        i = start3 + 1;
                        signx = 3;
                    }
                }

                if (arrayInt[i] > 0 && signx == 6)
                {
                    if (i - end3 >= minspace)
                    {
                        start4 = i;
                        signx = 7;
                    }
                    else
                    {
                        i = end3 + 1;
                        signx = 5;
                    }
                }

                if (arrayInt[i] == 0 && signx == 7)
                {
                    if (i - start4 >= minwidth)
                    {
                        end4 = i;
                        signx = 10;
                    }
                    else
                    {
                        i = start4 + 1;
                        signx = 6;
                    }
                }


                if (signx == 10)//找到10条分界线
                {
                    //对每个字符取上下界

                    //第一个字符
                    signy = 0;
                    int[] arrayIntY1 = new int[bm.Height];
                    for (int x = 0; x < bm.Height; x++)
                    {
                        int count = 0;
                        for (int y = start1; y < end1; y++)
                        {
                            if (bm.GetPixel(y, x).R == 0)
                                count++;
                        }
                        arrayIntY1[x] = count;
                    }

                    for (int x = 0; x < bm.Height - 1; x++)
                    {
                        if (arrayIntY1[x] != 0 && arrayIntY1[x + 1] != 0 && signy == 0)
                        {
                            up1 = x;
                            signy = 1;
                        }
                        if (arrayIntY1[x] == 0 && arrayIntY1[x + 1] == 0 && signy == 1)
                        {
                            down1 = x;
                            signy = 2;
                        }
                    }

                    //第二个字符
                    signy = 0;
                    int[] arrayIntY2 = new int[bm.Height];
                    for (int x = 0; x < bm.Height; x++)
                    {
                        int count = 0;
                        for (int y = start2; y < end2; y++)
                        {
                            if (bm.GetPixel(y, x).R == 0)
                                count++;
                        }
                        arrayIntY2[x] = count;
                    }

                    for (int x = 0; x < bm.Height - 1; x++)
                    {
                        if (arrayIntY2[x] != 0 && arrayIntY2[x + 1] != 0 && signy == 0)
                        {
                            up2 = x;
                            signy = 1;
                        }
                        if (arrayIntY2[x] == 0 && arrayIntY2[x + 1] == 0 && signy == 1)
                        {
                            down2 = x;
                            signy = 2;
                        }
                    }

                    //第三个字符
                    signy = 0;
                    int[] arrayIntY3 = new int[bm.Height];
                    for (int x = 0; x < bm.Height; x++)
                    {
                        int count = 0;
                        for (int y = start3; y < end3; y++)
                        {
                            if (bm.GetPixel(y, x).R == 0)
                                count++;
                        }
                        arrayIntY3[x] = count;
                    }

                    for (int x = 0; x < bm.Height - 1; x++)
                    {
                        if (arrayIntY3[x] != 0 && arrayIntY3[x + 1] != 0 && signy == 0)
                        {
                            up3 = x;
                            signy = 1;
                        }
                        if (arrayIntY3[x] == 0 && arrayIntY3[x + 1] == 0 && signy == 1)
                        {
                            down3 = x;
                            signy = 2;
                        }
                    }

                    //第四个字符
                    signy = 0;
                    int[] arrayIntY4 = new int[bm.Height];
                    for (int x = 0; x < bm.Height; x++)
                    {
                        int count = 0;
                        for (int y = start4; y < end4; y++)
                        {
                            if (bm.GetPixel(y, x).R == 0)
                                count++;
                        }
                        arrayIntY4[x] = count;
                    }

                    for (int x = 0; x < bm.Height - 1; x++)
                    {
                        if (arrayIntY4[x] != 0 && arrayIntY4[x + 1] != 0 && signy == 0)
                        {
                            up4 = x;
                            signy = 1;
                        }
                        if (arrayIntY4[x] == 0 && arrayIntY4[x + 1] == 0 && signy == 1)
                        {
                            down4 = x;
                            signy = 2;
                        }
                    }

                    int[,] position = new int[4, 4];
                    position[0, 0] = start1;
                    position[0, 1] = end1;
                    position[0, 2] = up1;
                    position[0, 3] = down1;
                    position[1, 0] = start2;
                    position[1, 1] = end2;
                    position[1, 2] = up2;
                    position[1, 3] = down2;
                    position[2, 0] = start3;
                    position[2, 1] = end3;
                    position[2, 2] = up3;
                    position[2, 3] = down3;
                    position[3, 0] = start4;
                    position[3, 1] = end4;
                    position[3, 2] = up4;
                    position[3, 3] = down4;
                    return position;

                }
            }
            return null;
        }


        /// <summary>
        /// 将bitmap转换为可输出到文件的string
        /// </summary>
        /// <param name="bm"></param>
        /// <returns></returns>
        public static string ConvertToStr(Bitmap bm)
        {
            string str = "";
            str += bm.Width;
            str += " ";
            str += bm.Height;
            str += " ";
            for (int j = 0; j < bm.Height; j++)
            {
                for (int i = 0; i < bm.Width; i++)
                {
                    str += bm.GetPixel(i, j).R / 255;
                    str += " ";
                }
            }
            str += "#";
            return str;
        }

        /// <summary>
        /// 将文件中的字符串转换为bitmap
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static Bitmap ConvertToBmp(string str)
        {
            string[] arrayStr = Regex.Split(str, " ");
            Bitmap newImg = new Bitmap(Convert.ToInt32(arrayStr[0]), Convert.ToInt32(arrayStr[1]));
            for (int j = 0; j < Convert.ToInt32(arrayStr[1]); j++)
            {
                for (int i = 0; i < Convert.ToInt32(arrayStr[0]); i++)
                {
                    newImg.SetPixel(i, j, Color.FromArgb(Convert.ToInt32(arrayStr[j * Convert.ToInt32(arrayStr[0]) + i + 2]) * 255, Convert.ToInt32(arrayStr[j * Convert.ToInt32(arrayStr[0]) + i + 2]) * 255, Convert.ToInt32(arrayStr[j * Convert.ToInt32(arrayStr[0]) + i + 2]) * 255));
                }
            }
            return newImg;
        }

        /// <summary>
        /// 比较两个bitmap的相似度
        /// </summary>
        /// <param name="bm1"></param>
        /// <param name="bm2"></param>
        /// <returns></returns>
        public static double Similarity(string str1, string str2)
        {
            string[] arrayStr1 = Regex.Split(str1, " ");//[0]为width [1]为height
            string[] arrayStr2 = Regex.Split(str2, " ");

            int w = Convert.ToInt32(arrayStr1[0]) > Convert.ToInt32(arrayStr2[0]) ? Convert.ToInt32(arrayStr2[0]) : Convert.ToInt32(arrayStr1[0]);
            int h = Convert.ToInt32(arrayStr1[1]) > Convert.ToInt32(arrayStr2[1]) ? Convert.ToInt32(arrayStr2[1]) : Convert.ToInt32(arrayStr1[1]);

            int count = 0;

            for (int j = 0; j < h; j++)
            {
                for (int i = 0; i < w; i++)
                {
                    if (arrayStr1[j * Convert.ToInt32(arrayStr1[0]) + i + 2] == arrayStr2[j * Convert.ToInt32(arrayStr2[0]) + i + 2])
                        count++;
                }
            }
            //大小不同的部分默认为不匹配
            w = Convert.ToInt32(arrayStr1[0]) < Convert.ToInt32(arrayStr2[0]) ? Convert.ToInt32(arrayStr2[0]) : Convert.ToInt32(arrayStr1[0]);
            h = Convert.ToInt32(arrayStr1[1]) < Convert.ToInt32(arrayStr2[1]) ? Convert.ToInt32(arrayStr2[1]) : Convert.ToInt32(arrayStr1[1]);
            return ((double)count) / (w * h);
        }


    }
}
