using System;
using System.IO;
using ZionGraphics.Handlers;
using ZionGraphics.Pdf;

namespace ZionGraphics
{
    [Serializable]
    public class Image : IDisposable
    {
        #region Private Attributes
        private int pages;
        private int index;
        private string path;
        private System.Drawing.Image image;
        private System.Drawing.Bitmap currentBitmap;
        private MemoryStream stream;
        private ZionVault.Binder.OCR.Page textLayer;
        private System.Drawing.Imaging.EncoderParameters encoderParameters;
        private bool isPdf
        {
            get
            {
                return (this.path.ToUpper().EndsWith("PDF"));
            }
        }
        private System.Drawing.Imaging.EncoderParameters BasicEncoder
        {
            get
            {
                this.encoderParameters = new System.Drawing.Imaging.EncoderParameters(2);
                encoderParameters.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag, (long)System.Drawing.Imaging.EncoderValue.MultiFrame);
                encoderParameters.Param[1] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Compression, (long)System.Drawing.Imaging.EncoderValue.CompressionLZW);

                return encoderParameters;
            }
        }
        private System.Drawing.Imaging.EncoderParameters NewFrameEncoder
        {
            get
            {
                encoderParameters.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag, (long)System.Drawing.Imaging.EncoderValue.FrameDimensionPage);
                encoderParameters.Param[1] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Compression, (long)System.Drawing.Imaging.EncoderValue.CompressionLZW);

                return encoderParameters;
            }
        }
        #endregion

        #region Public Attributes
        public int Length
        {
            get
            {
                System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf
                    = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                using (var ms = new System.IO.MemoryStream())
                {
                    bf.Serialize(ms, image);
                    return ms.ToArray().Length;
                }
            }
        }
        public int Pages { get => pages; set => pages = value; }
        public int Index { get => index; set => index = value; }
        public System.Drawing.Bitmap Bitmap
        {
            get
            {
                if (this.currentBitmap != null)
                    this.currentBitmap.Dispose();

                currentBitmap = this.At(this.index);

                if (this.currentBitmap is null)
                    return null;

                currentBitmap.SetResolution(this.image.HorizontalResolution, this.image.VerticalResolution);

                using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(currentBitmap))
                {
                    graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
                    graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                    graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
                    graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
                    graphics.DrawImage(currentBitmap, new System.Drawing.Rectangle(0, 0, currentBitmap.Width, currentBitmap.Height));
                }

                return currentBitmap;
            }
        }
        public System.Drawing.Image Img
        {
            get
            {
                return GenerateImage(At(this.index));
            }
        }
        public ZionVault.Binder.OCR.Page TextLayer
        {
            get
            {
                if(!this.isPdf)
                    return null;

                LoadPDF(this.path, index + 1);

                return textLayer;
            }
        }
        #endregion

        #region Private Functions
        private void LoadImage(string path)
        {
            this.stream = new MemoryStream(File.ReadAllBytes(path));
            this.image = System.Drawing.Image.FromStream(stream);
            this.pages = this.image.GetFrameCount(System.Drawing.Imaging.FrameDimension.Page);
        }
        private void LoadPDF(string path, int page = 1)
        {
            using (Importer importer = new Importer(path))
            {
                this.pages = importer.Pages;
                
                this.textLayer = importer.ExtractTextFromPage(page);

                bool hasTextFromPage = this.textLayer.Text != null && this.textLayer.Text != string.Empty;

                using (System.Drawing.Bitmap bitmap = hasTextFromPage ? 
                    importer.GenerateBitmapFromPage(page - 1) : importer.ExtractBitmapFromPage(page))
                {
                    if (this.image != null)
                        this.image.Dispose();
                    if(bitmap != null)
                        this.image = (System.Drawing.Image)bitmap.Clone();
                }
            }
        }
        private System.Drawing.Image GenerateImage(System.Drawing.Bitmap bitmap)
        {
            System.Drawing.Image result;
            using (System.Drawing.Bitmap bitmapTemp = bitmap)
            {
                bitmapTemp.SetResolution(this.image.HorizontalResolution, this.image.VerticalResolution);

                using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bitmapTemp))
                {
                    graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
                    graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                    graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
                    graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
                    graphics.DrawImage(bitmapTemp, new System.Drawing.Rectangle(0, 0, bitmapTemp.Width, bitmapTemp.Height));
                }

                MemoryStream tmp = new MemoryStream();
                bitmapTemp.Save(tmp, System.Drawing.Imaging.ImageFormat.Jpeg);
                result = System.Drawing.Image.FromStream(tmp);
                tmp.Close();
            }

            return result;
        }
        private System.Drawing.Bitmap At(int index)
        {
            try
            {
                if (this.currentBitmap != null)
                    this.currentBitmap.Dispose();
                if (!this.isPdf)
                {
                    this.image.SelectActiveFrame(new System.Drawing.Imaging.FrameDimension(this.image.FrameDimensionsList[0]), index);
                } else
                {
                    this.LoadPDF(this.path, index + 1);
                }
                currentBitmap = new System.Drawing.Bitmap(image);
                currentBitmap.SetResolution(image.HorizontalResolution, image.VerticalResolution);
                return currentBitmap;
            }
            catch
            {
                return null;
            }
        }
        #endregion

        #region Public Functions
        public Image Next()
        {
            if ((this.index + 1) < this.pages)
                this.index++;
            return this;
        }
        public Image Previous()
        {
            if ((this.index - 1) >= 0)
                this.index--;
            return this;
        }
        #endregion

        #region Overrides and Constructors
        public Image(string path)
        {
            if (!File.Exists(path))
                throw new InvalidOperationException(String.Format(@"Não foi possível encontrar o arquivo '{0}'.", path));

            this.path = path;

            try
            {
                if (this.path.Contains(".ini"))
                {

                }
                else if (!this.isPdf) //Image file
                {
                    this.LoadImage(this.path);
                }
                else
                {
                    this.LoadPDF(this.path);
                }
            } catch (Exception ex)
            {
                throw new FileLoadException(String.Format(@"Não foi possível abrir o arquivo '{0}'.", path));
            }
        }
        public void Dispose()
        {
            this.image?.Dispose();
            this.stream?.Close();
            GC.Collect();
            GC.WaitForFullGCComplete();
        }
        #endregion
    }
}