using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;

namespace PINBlog
{
    public enum LANGUAGE { ENG = 0 }

    public class OCRResultInfo
    {
        public LANGUAGE Language { get; set; }
        public string Result { get; set; }

        public OCRResultInfo()
        {
            Language = LANGUAGE.ENG;
            Result = "";
        }
    }

    public class Tesseract
    {
        private  string m_TesseractExePath;
        private LANGUAGE m_Language;

        /// <summary>
        /// Initializes a new instance of the <see cref="TesseractService"/> class.
        /// </summary>
        /// <param name="tesseractDir">The path for the Tesseract4 installation folder (C:\Program Files\Tesseract-OCR).</param>
        /// <param name="language">The language used to extract text from images (eng, por, etc)</param>
        /// <param name="dataDir">The data with the trained models (tessdata). Download the models from https://github.com/tesseract-ocr/tessdata_fast</param>
        public Tesseract(LANGUAGE language = LANGUAGE.ENG)
        {
            // Tesseract configs.
            var dir = Path.Combine(".", "tessdata");
            m_TesseractExePath = Path.Combine(dir,"tesseract.exe");
            m_Language = language;
            Environment.SetEnvironmentVariable("TESSDATA_PREFIX", dir);
        }

        public Stream ToStream(Image image, ImageFormat format = null)
        {
            var stream = new System.IO.MemoryStream();
            if (format == null)
            {
                image.Save(stream, image.RawFormat);
            }
            else
            {
                image.Save(stream, format);
            }
            stream.Position = 0;
            return stream;
        }

        /// <summary>
        /// Read text from the images streams.
        /// </summary>
        /// <param name="images">The images streams.</param>
        /// <returns>The images text.</returns>
        public string GetText(params Stream[] images)
        {
            var output = string.Empty;

            if (images.Any())
            {
                var tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
                Directory.CreateDirectory(tempPath);
                var tempInputFile = NewTempFileName(tempPath);
                var tempOutputFile = NewTempFileName(tempPath);

                try
                {
                    WriteInputFiles(images, tempPath, tempInputFile);

                    var info = new ProcessStartInfo
                    {
                        FileName = m_TesseractExePath,
                        Arguments = $"{tempInputFile} {tempOutputFile} -l {m_Language.ToString()}",
                        RedirectStandardError = true,
                        RedirectStandardOutput = true,
                        CreateNoWindow = true,
                        UseShellExecute = false
                    };

                    using (var ps = Process.Start(info))
                    {
                        ps.WaitForExit();

                        var exitCode = ps.ExitCode;

                        if (exitCode == 0)
                        {
                            output = File.ReadAllText(tempOutputFile + ".txt");

                            var charset = new char[] { ' ', '\n', '\f' };
                            output = output.TrimStart(charset).TrimEnd(charset);
                        }
                        else
                        {
                            var stderr = ps.StandardError.ReadToEnd();
                            throw new InvalidOperationException(stderr);
                        }
                    }
                }
                finally
                {
                    Directory.Delete(tempPath, true);
                }
            }

            return output;
        }

        private void WriteInputFiles(Stream[] inputStreams, string tempPath, string tempInputFile)
        {
            // If there is more thant one image file, so build the list file using the images as input files.
            if (inputStreams.Length > 1)
            {
                var imagesListFileContent = new StringBuilder();

                foreach (var inputStream in inputStreams)
                {
                    var imageFile = NewTempFileName(tempPath);

                    using (var tempStream = File.OpenWrite(imageFile))
                    {
                        CopyStream(inputStream, tempStream);
                    }

                    imagesListFileContent.AppendLine(imageFile);
                }

                File.WriteAllText(tempInputFile, imagesListFileContent.ToString());
            }
            else
            {
                // If is only one image file, than use the image file as input file.
                using (var tempStream = File.OpenWrite(tempInputFile))
                {
                    CopyStream(inputStreams.First(), tempStream);
                }
            }
        }

        private void CopyStream(Stream input, Stream output)
        {
            if (input.CanSeek)
                input.Seek(0, SeekOrigin.Begin);

            //input.CopyTo(output);

            Byte[] buffer = new byte[input.Length];
            int len = input.Read(buffer, 0, buffer.Length);
            output.Write(buffer, 0, len);

            input.Close();
        }

        private string NewTempFileName(string tempPath)
        {
            return Path.Combine(tempPath, Guid.NewGuid().ToString());
        }
    }
}
