using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using Tesseract.Internal;
using Tesseract.Interop;

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

        private bool runRecognitionPhase;
        private Rect regionOfInterest;

        public TesseractEngine Engine { get; private set; }

        
        
        
        public Pix Image { get; private set; }

        
        
        
        
        
        
        public string ImageName { get; private set; }

        
        
        
        public PageSegMode PageSegmentMode { get; private set; }

        internal Page(TesseractEngine engine, Pix image, string imageName, Rect regionOfInterest, PageSegMode pageSegmentMode)
        {
            Engine = engine;
            Image = image;
            ImageName = imageName;
            RegionOfInterest = regionOfInterest;
            PageSegmentMode = pageSegmentMode;
        }

        
        
        
        public Rect RegionOfInterest
        {
            get
            {
                return regionOfInterest;
            }
            set
            {
                if (value.X1 < 0 || value.Y1 < 0 || value.X2 > Image.Width || value.Y2 > Image.Height)
                    throw new ArgumentException("The region of interest to be processed must be within the image bounds.", nameof(value));

                if (regionOfInterest != value) {
                    regionOfInterest = value;

                    
                    TessApi.Native.BaseApiSetRectangle(Engine.Handle, regionOfInterest.X1, regionOfInterest.Y1, regionOfInterest.Width, regionOfInterest.Height);

                    
                    runRecognitionPhase = false;
                }
            }
        }

        
        
        
        
        public Pix GetThresholdedImage()
        {
            Recognize();

            var pixHandle = TessApi.Native.BaseAPIGetThresholdedImage(Engine.Handle);
            if (pixHandle == IntPtr.Zero) {
                throw new TesseractException("Failed to get thresholded image.");
            }

            return Pix.Create(pixHandle);
        }

        
        
        
        
        public PageIterator AnalyseLayout()
        {
            Guard.Verify(PageSegmentMode != PageSegMode.OsdOnly, "Cannot analyse image layout when using OSD only page segmentation, please use DetectBestOrientation instead.");

            var resultIteratorHandle = TessApi.Native.BaseAPIAnalyseLayout(Engine.Handle);
            return new PageIterator(this, resultIteratorHandle);
        }

        
        
        
        
        public ResultIterator GetIterator()
        {
            Recognize();
            var resultIteratorHandle = TessApi.Native.BaseApiGetIterator(Engine.Handle);
            return new ResultIterator(this, resultIteratorHandle);
        }

        
        
        
        
        public string GetText()
        {
            Recognize();
            return TessApi.BaseAPIGetUTF8Text(Engine.Handle);
        }

		
		
		
		
		
		
		public string GetHOCRText(int pageNum, bool useXHtml = false)
        {
			
            Guard.Require("pageNum", pageNum >= 0, "Page number must be greater than or equal to zero (0).");
            Recognize();
			if(useXHtml)
				return TessApi.BaseAPIGetHOCRText2(Engine.Handle, pageNum);
            return TessApi.BaseAPIGetHOCRText(Engine.Handle, pageNum);
        }

        
        
        
        
        public float GetMeanConfidence()
        {
            Recognize();
            return TessApi.Native.BaseAPIMeanTextConf(Engine.Handle) / 100.0f;
        }

        
        
        
        
        
        
        
        
        
        public void DetectBestOrientation(out Orientation orientation, out float confidence)
        {
            OSResult result = new OSResult();
            result.Init();
            if (TessApi.Native.BaseAPIDetectOS(Engine.Handle, ref result) != 0) {
                result.GetBestOrientation(out orientation, out confidence);
            } else {
                throw new TesseractException("Failed to detect image orientation.");
            }
        }

#if Net45

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        private void Recognize()
        {
            Guard.Verify(PageSegmentMode != PageSegMode.OsdOnly, "Cannot OCR image when using OSD only page segmentation, please use DetectBestOrientation instead.");
            if (!runRecognitionPhase) {
                if (TessApi.Native.BaseApiRecognize(Engine.Handle, new HandleRef(this, IntPtr.Zero)) != 0) {
                    throw new InvalidOperationException("Recognition of image failed.");
                }

                runRecognitionPhase = true;

                
                if (Engine.TryGetBoolVariable("tessedit_write_images", out var tesseditWriteImages) && tesseditWriteImages) {
                    using (Pix thresholdedImage = GetThresholdedImage()) {
                        string filePath = Path.Combine(Environment.CurrentDirectory, "tessinput.tif");
                        try {
                            thresholdedImage.Save(filePath, ImageFormat.TiffG4);
                            trace.TraceEvent(TraceEventType.Information, 2, "Successfully saved the thresholded image to '{0}'", filePath);
                        } catch (Exception error) {
                            trace.TraceEvent(TraceEventType.Error, 2, "Failed to save the thresholded image to '{0}'.\nError: {1}", filePath, error.Message);
                        }
                    }
                }
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing) {
                TessApi.Native.BaseAPIClear(Engine.Handle);
            }
        }
    }
}
