using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Security;
using Tesseract.Internal;
using Tesseract.Interop;

namespace Tesseract
{
    
    
    
    public class TesseractEngine : DisposableBase
    {
        private static readonly TraceSource trace = new TraceSource("Tesseract");

        private int processCount;

        
        
        
        
        
        
        
        
        
        
        
        
        
        public TesseractEngine(string datapath, string language)
            : this(datapath, language, EngineMode.Default, new string[0], new Dictionary<string, object>(), false)
        {
        }

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        public TesseractEngine(string datapath, string language, string configFile)
            : this(datapath, language, EngineMode.Default, configFile != null ? new[] { configFile } : new string[0], new Dictionary<string, object>(), false)
        {
        }

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        public TesseractEngine(string datapath, string language, IEnumerable<string> configFiles)
            : this(datapath, language, EngineMode.Default, configFiles, new Dictionary<string, object>(), false)
        {
        }

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        public TesseractEngine(string datapath, string language, EngineMode engineMode)
            : this(datapath, language, engineMode, new string[0], new Dictionary<string, object>(), false)
        {
        }

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        public TesseractEngine(string datapath, string language, EngineMode engineMode, string configFile)
            : this(datapath, language, engineMode, configFile != null ? new[] { configFile } : new string[0], new Dictionary<string, object>(), false)
        {
        }

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        public TesseractEngine(string datapath, string language, EngineMode engineMode, IEnumerable<string> configFiles)
            : this(datapath, language, engineMode, configFiles, new Dictionary<string, object>(), false)
        {
        }

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        public TesseractEngine(string datapath, string language, EngineMode engineMode, IEnumerable<string> configFiles, IDictionary<string, object> initialOptions, bool setOnlyNonDebugVariables)
        {
            Guard.RequireNotNullOrEmpty("language", language);

            DefaultPageSegMode = PageSegMode.Auto;
            Handle = new HandleRef(this, TessApi.Native.BaseApiCreate());

            Initialise(datapath, language, engineMode, configFiles, initialOptions, setOnlyNonDebugVariables);
        }

        public string Version
        {
            get
            {
                
                
                return "3.03";

                
            }
        }

        internal HandleRef Handle { get; private set; }

        
        
        
        
        
        
        
        
        public Page Process(Pix image, PageSegMode? pageSegMode = null)
        {
            return Process(image, null, new Rect(0, 0, image.Width, image.Height), pageSegMode);
        }

        
        
        
        
        
        
        
        
        
        
        public Page Process(Pix image, Rect region, PageSegMode? pageSegMode = null)
        {
            return Process(image, null, region, pageSegMode);
        }

        
        
        
        
        
        
        
        
        
        public Page Process(Pix image, string inputName, PageSegMode? pageSegMode = null)
        {
            return Process(image, inputName, new Rect(0, 0, image.Width, image.Height), pageSegMode);
        }

        
        
        
        
        
        
        
        
        
        
        
        public Page Process(Pix image, string inputName, Rect region, PageSegMode? pageSegMode = null)
        {
            if (image == null) throw new ArgumentNullException(nameof(image));
            if (region.X1 < 0 || region.Y1 < 0 || region.X2 > image.Width || region.Y2 > image.Height)
                throw new ArgumentException("The image region to be processed must be within the image bounds.", nameof(region));
            if (processCount > 0) throw new InvalidOperationException("Only one image can be processed at once. Please make sure you dispose of the page once your finished with it.");

            processCount++;

            var actualPageSegmentMode = pageSegMode ?? DefaultPageSegMode;
            TessApi.Native.BaseAPISetPageSegMode(Handle, actualPageSegmentMode);
            TessApi.Native.BaseApiSetImage(Handle, image.Handle);
            if (!String.IsNullOrEmpty(inputName)) {
                TessApi.Native.BaseApiSetInputName(Handle, inputName);
            }
            var page = new Page(this, image, inputName, region, actualPageSegmentMode);
            page.Disposed += OnIteratorDisposed;
            return page;
        }


        protected override void Dispose(bool disposing)
        {
            if (Handle.Handle != IntPtr.Zero) {
                TessApi.Native.BaseApiDelete(Handle);
                Handle = new HandleRef(this, IntPtr.Zero);
            }
        }

        private string GetTessDataPrefix()
        {
            try {
                return Environment.GetEnvironmentVariable("TESSDATA_PREFIX");
            } catch (SecurityException e) {
                trace.TraceEvent(TraceEventType.Error, 0, "Failed to detect if the environment variable 'TESSDATA_PREFIX' is set: {0}", e.Message);
                return null;
            }
        }

        private void Initialise(string datapath, string language, EngineMode engineMode, IEnumerable<string> configFiles, IDictionary<string, object> initialValues, bool setOnlyNonDebugVariables)
        {
            const string TessDataDirectory = "tessdata";
            Guard.RequireNotNullOrEmpty("language", language);

            
            if (!String.IsNullOrEmpty(datapath)) {
                
                datapath = datapath.Trim();

                
                if (datapath.EndsWith("\\", StringComparison.Ordinal) || datapath.EndsWith("/", StringComparison.Ordinal)) {
                    datapath = datapath.Substring(0, datapath.Length - 1);
                }
                
                if (datapath.EndsWith("tessdata", StringComparison.OrdinalIgnoreCase)) {
                    datapath = datapath.Substring(0, datapath.Length - TessDataDirectory.Length);
                }
            }

            if (TessApi.BaseApiInit(Handle, datapath, language, (int)engineMode, configFiles ?? new List<string>(), initialValues ?? new Dictionary<string, object>(), setOnlyNonDebugVariables) != 0) {
                
                Handle = new HandleRef(this, IntPtr.Zero);
                GC.SuppressFinalize(this);

                throw new TesseractException(ErrorMessage.Format(1, "Failed to initialise tesseract engine."));
            }
        }

        
        
        
        private class PageDisposalHandle
        {
            private readonly Page page;
            private readonly Pix pix;

            public PageDisposalHandle(Page page, Pix pix)
            {
                this.page = page;
                this.pix = pix;
                page.Disposed += OnPageDisposed;
            }

            private void OnPageDisposed(object sender, EventArgs e)
            {
                page.Disposed -= OnPageDisposed;
                
                pix.Dispose();
            }
        }

        #region Config

        
        
        
        public PageSegMode DefaultPageSegMode
        {
            get;
            set;
        }

        public bool SetDebugVariable(string name, string value)
        {
            return TessApi.BaseApiSetDebugVariable(Handle, name, value) != 0;
        }

        
        
        
        
        
        
        public bool SetVariable(string name, string value)
        {
            return TessApi.BaseApiSetVariable(Handle, name, value) != 0;
        }

        
        
        
        
        
        
        public bool SetVariable(string name, bool value)
        {
            var strEncodedValue = value ? "TRUE" : "FALSE";
            return TessApi.BaseApiSetVariable(Handle, name, strEncodedValue) != 0;
        }

        
        
        
        
        
        
        public bool SetVariable(string name, int value)
        {
            var strEncodedValue = value.ToString("D", CultureInfo.InvariantCulture.NumberFormat);
            return TessApi.BaseApiSetVariable(Handle, name, strEncodedValue) != 0;
        }

        
        
        
        
        
        
        public bool SetVariable(string name, double value)
        {
            var strEncodedValue = value.ToString("R", CultureInfo.InvariantCulture.NumberFormat);
            return TessApi.BaseApiSetVariable(Handle, name, strEncodedValue) != 0;
        }

        
        
        
        
        
        
        public bool TryGetBoolVariable(string name, out bool value)
        {
            if (TessApi.Native.BaseApiGetBoolVariable(Handle, name, out var val) != 0) {
                value = (val != 0);
                return true;
            }

            value = false;
            return false;
        }

        
        
        
        
        
        
        public bool TryGetDoubleVariable(string name, out double value)
        {
            return TessApi.Native.BaseApiGetDoubleVariable(Handle, name, out value) != 0;
        }

        
        
        
        
        
        
        public bool TryGetIntVariable(string name, out int value)
        {
            return TessApi.Native.BaseApiGetIntVariable(Handle, name, out value) != 0;
        }

        
        
        
        
        
        
        public bool TryGetStringVariable(string name, out string value)
        {
            value = TessApi.BaseApiGetStringVariable(Handle, name);
            return value != null;
        }

        #endregion Config

        #region Event Handlers

        private void OnIteratorDisposed(object sender, EventArgs e)
        {
            processCount--;
        }

        #endregion Event Handlers
    }
}

