using FastReport.Utils;
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Windows.Forms;

namespace FastReport.Forms
{
    internal partial class PictureEditorAdvancedForm : BaseForm
    {
        public Image Image
        {
            get
            {
                return pictureBox.Image;
            }
        }

        private Image imageOriginal;
        private Image imageGrayscale;
        private Image imageMonochrome;

        private ToolStripLabel lblSize;
        private ToolStripRadioButton rbZoomed;
        private ToolStripRadioButton rbFullSize;
        
        public PictureEditorAdvancedForm(Image image)
        {
            InitializeComponent();

            this.imageOriginal = image;
            pictureBox.Image = image;

            setToolbar();
            reset();

            if (image.Width < panelMiddle.Width && image.Height < panelMiddle.Height)
                rbFullSize.PerformClick();
            else
                rbZoomed.PerformClick();

            rbPercentResize.CheckedChanged += rbPercentResize_CheckedChanged;
            nudHor.ValueChanged += resize;
            nudVer.ValueChanged += resize;

            rbPercentCrop.CheckedChanged += rbPercentCrop_CheckedChanged;
            nudTop.ValueChanged += crop;
            nudLeft.ValueChanged += crop;
            nudRight.ValueChanged += crop;
            nudBottom.ValueChanged += crop;

            this.SizeChanged += delegate(object s, EventArgs e)
            {
                centerImage();
            };
            
            Localize();
            UIUtils.CheckRTL(this);
            UpdateDpiDependencies();
        }

        #region Init & Reset
        public override void Localize()
        {
            MyRes res = new MyRes("Forms,PictureEditorAdvanced");
            Text = res.Get("");
            btnOK.Text = Res.Get("Buttons,Ok");
            btnCancel.Text = Res.Get("Buttons,Cancel");
            btnReset.Text = res.Get("Reset");
            gbResize.Text = res.Get("Resize");
            gbCrop.Text = res.Get("Crop");
            gbColor.Text = res.Get("Color");
            lblChange.Text = res.Get("Change");
            lblChange2.Text = res.Get("Change");
            rbPercentResize.Text = res.Get("Percentage");
            rbPercentCrop.Text = res.Get("Percentage");
            rbPixelsResize.Text = res.Get("Pixels");
            rbPixelsCrop.Text = res.Get("Pixels");
            lblHor.Text = res.Get("Horizontal");
            lblVer.Text = res.Get("Vertical");
            cbAspectRatio.Text = res.Get("AspectRatio");
            rbNone.Text = Res.Get("Misc,None");
            rbGrayscale.Text = res.Get("Grayscale");
            rbMonochrome.Text = res.Get("Monochrome");
        }

        public override void UpdateDpiDependencies()
        {
            base.UpdateDpiDependencies();
            rbZoomed.Image = GetImage(235);
            rbFullSize.Image = GetImage(236);
        }

        private void setToolbar()
        {
            lblSize = new ToolStripLabel();
            lblSize.Font = new Font("Segoe UI", 9);
            setSizeInfo(pictureBox.Image.Width, pictureBox.Image.Height);
            statusBar.Items.Add(lblSize);

            rbZoomed = new ToolStripRadioButton();
            rbZoomed.Click += delegate(object s, EventArgs e)
            {
                pictureBox.SizeMode = PictureBoxSizeMode.Zoom;
                pictureBox.Dock = DockStyle.Fill;
                centerImage();
            };
            statusBar.Items.Add(rbZoomed);

            rbFullSize = new ToolStripRadioButton();
            rbFullSize.Click += delegate(object s, EventArgs e)
            {
                pictureBox.SizeMode = PictureBoxSizeMode.AutoSize;
                pictureBox.Dock = DockStyle.None;
                centerImage();
            };
            statusBar.Items.Add(rbFullSize);
        }

        private void reset()
        {
            resetResize();
            resetCrop();
            resetColor();
        }

        private void resetResize()
        {
            rbPercentResize.Checked = true;
            cbAspectRatio.Checked = true;

            nudHor.Minimum = 1;
            nudHor.Maximum = 100;
            nudHor.Value = 100;

            nudVer.Minimum = 1;
            nudVer.Maximum = 100;
            nudVer.Value = 100;
        }

        private void resetCrop()
        {
            rbPercentCrop.Checked = true;

            nudTop.Minimum = 0;
            nudTop.Maximum = 99;
            nudTop.Value = 0;

            nudLeft.Minimum = 0;
            nudLeft.Maximum = 99;
            nudLeft.Value = 0;

            nudRight.Minimum = 0;
            nudRight.Maximum = 99;
            nudRight.Value = 0;

            nudBottom.Minimum = 0;
            nudBottom.Maximum = 99;
            nudBottom.Value = 0;
        }

        private void resetColor()
        {
            rbNone.Checked = true;
        }
        #endregion

        #region Resize
        void rbPercentResize_CheckedChanged(object sender, EventArgs e)
        {
            nudHor.ValueChanged -= resize;
            nudVer.ValueChanged -= resize;

            if (rbPercentResize.Checked)
            {
                nudHor.Maximum = 100;
                nudVer.Maximum = 100;

                nudHor.Value = 100;
                nudVer.Value = 100;
            }
            else
            {
                nudHor.Maximum = imageOriginal.Width;
                nudVer.Maximum = imageOriginal.Height;

                nudHor.Value = imageOriginal.Width;
                nudVer.Value = imageOriginal.Height;
            }

            nudHor.ValueChanged += resize;
            nudVer.ValueChanged += resize;

            redraw();
        }

        private int getWidth()
        {
            if(rbPercentResize.Checked)
            {
                return (int)Math.Round((float)nudHor.Value / 100 * imageOriginal.Width);
            }
            else
            {
                return (int)nudHor.Value;
            }
        }

        private int getHeight()
        {
            if (rbPercentResize.Checked)
            {
                return (int)Math.Round((float)nudVer.Value / 100 * imageOriginal.Height);
            }
            else
            {
                return (int)nudVer.Value;
            }
        }

        private void resize(object sender, EventArgs e)
        {
            if(cbAspectRatio.Checked)
            {
                float x = (float)getWidth() / (float)imageOriginal.Width;
                float y = (float)getHeight() / (float)imageOriginal.Height;

                if(sender == nudHor)
                {
                    if(rbPercentResize.Checked)
                    {
                        nudVer.Value = (int)Math.Round(x * 100);
                    }
                    else
                    {
                        nudVer.Value = (int)Math.Round(x * imageOriginal.Height);
                    }
                }
                else if(sender == nudVer)
                {
                    if (rbPercentResize.Checked)
                    {
                        nudHor.Value = (int)Math.Round(y * 100);
                    }
                    else
                    {
                        nudHor.Value = (int)Math.Round(y * imageOriginal.Width);
                    }
                }
            }

            redraw();
        }
        #endregion

        #region Crop
        private int cropTop()
        {
            if (rbPercentCrop.Checked)
            {
                return (int)Math.Round((float)nudTop.Value / 100 * getHeight());
            }
            else
            {
                return (int)nudTop.Value;
            }
        }

        private int cropLeft()
        {
            if (rbPercentCrop.Checked)
            {
                return (int)Math.Round((float)nudLeft.Value / 100 * getWidth());
            }
            else
            {
                return (int)nudLeft.Value;
            }
        }

        private int cropRight()
        {
            if (rbPercentCrop.Checked)
            {
                return (int)Math.Round((float)nudRight.Value / 100 * getWidth());
            }
            else
            {
                return (int)nudRight.Value;
            }
        }

        private int cropBottom()
        {
            if (rbPercentCrop.Checked)
            {
                return (int)Math.Round((float)nudBottom.Value / 100 * getHeight());
            }
            else
            {
                return (int)nudBottom.Value;
            }
        }

        private void rbPercentCrop_CheckedChanged(object sender, EventArgs e)
        {
            nudTop.ValueChanged -= crop;
            nudLeft.ValueChanged -= crop;
            nudRight.ValueChanged -= crop;
            nudBottom.ValueChanged -= crop;

            if(rbPercentCrop.Checked)
            {
                nudTop.Maximum    = 99;
                nudBottom.Maximum = 99;
                nudLeft.Maximum   = 99;
                nudRight.Maximum  = 99;

                nudTop.Value    = 0;
                nudBottom.Value = 0;
                nudLeft.Value   = 0;
                nudRight.Value  = 0;
            }
            else
            {
                nudTop.Maximum    = getHeight() - 1;
                nudBottom.Maximum = getHeight() - 1;
                nudLeft.Maximum   = getWidth()  - 1;
                nudRight.Maximum  = getWidth()  - 1;

                nudTop.Value    = 0;
                nudBottom.Value = 0;
                nudLeft.Value   = 0;
                nudRight.Value  = 0;
            }

            nudTop.ValueChanged += crop;
            nudLeft.ValueChanged += crop;
            nudRight.ValueChanged += crop;
            nudBottom.ValueChanged += crop;

            redraw();
        }

        private void crop(object sender, EventArgs e)
        {
            if(rbPercentCrop.Checked)
            {
                nudTop.Maximum    = 99 - nudBottom.Value;
                nudBottom.Maximum = 99 - nudTop.Value;
                nudLeft.Maximum   = 99 - nudRight.Value;
                nudRight.Maximum  = 99 - nudLeft.Value;
            }
            else
            {
                nudTop.Maximum    = getHeight() - nudBottom.Value - 1;
                nudBottom.Maximum = getHeight() - nudTop.Value    - 1;
                nudLeft.Maximum   = getWidth()  - nudRight.Value  - 1;
                nudRight.Maximum  = getWidth()  - nudLeft.Value   - 1;
            }

            redraw();
        }
        #endregion

        #region Color
        private Image monochrome(Image image)
        {
            Bitmap mono = ((Bitmap)image).Clone(new Rectangle(0, 0, image.Width, image.Height), image.PixelFormat);

            for (int x = 0; x < mono.Width; x++)
            {
                for (int y = 0; y < mono.Height; y++)
                {
                    Color c = mono.GetPixel(x, y);
                    
                    if (c.GetBrightness() >= 0.5)
                    {
                        c = Color.FromArgb(c.A, 255, 255, 255);
                    }
                    else
                    {
                        c = Color.FromArgb(c.A, 0, 0, 0);
                    }

                    mono.SetPixel(x, y, c);
                }
            }

            return mono;
        }

        private Bitmap __monochrome(Bitmap image)
        {
            int w = image.Width;
            int h = image.Height;

            Bitmap bmp = new Bitmap(w, h, PixelFormat.Format1bppIndexed);
            BitmapData data = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);
            
            for (int y = 0; y < h; y++)
            {
                byte[] scan = new byte[(w + 7) / 8];

                for (int x = 0; x < w; x++)
                {
                    Color c = image.GetPixel(x, y);
                    if (c.GetBrightness() >= 0.5) scan[x / 8] |= (byte)(0x80 >> (x % 8));
                }

                System.Runtime.InteropServices.Marshal.Copy(scan, 0, (IntPtr)((int)data.Scan0 + data.Stride * y), scan.Length);
            }

            bmp.UnlockBits(data);

            return bmp;
        }
        #endregion

        #region Main & Etc
        private void redraw()
        {
            pictureBox.Image = update(getWidth(), getHeight());
            setSizeInfo(Image.Width, Image.Height);
            centerImage();
        }

        private void centerImage()
        {
            //if (rbFullSize.Checked)
            {
                pictureBox.Left = Math.Max(0, (pictureBox.Parent.Width  - pictureBox.Width)  / 2);
                pictureBox.Top  = Math.Max(0, (pictureBox.Parent.Height - pictureBox.Height) / 2);
            }
        }

        private void setSizeInfo(int x, int y)
        {
            lblSize.Text = " " + x + " × " + y + " ";
        }

        private Image update(int width, int height)
        {
            Image image = this.imageOriginal;

            if (rbGrayscale.Checked)
            {
                if (imageGrayscale == null)
                    imageGrayscale = ImageHelper.GetGrayscaleBitmap(image);

                image = imageGrayscale;
            }
            else if (rbMonochrome.Checked)
            {
                if (imageMonochrome == null)
                    imageMonochrome = monochrome(image);

                image = imageMonochrome;
            }

            Rectangle destRect = new Rectangle(0, 0, width, height);
            Bitmap destImage = new Bitmap(width, height);

            destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

            using (Graphics graphics = Graphics.FromImage(destImage))
            {
                graphics.CompositingMode = CompositingMode.SourceCopy;
                graphics.CompositingQuality = CompositingQuality.HighQuality;
                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                graphics.SmoothingMode = SmoothingMode.HighQuality;
                graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

                using (ImageAttributes wrapMode = new ImageAttributes())
                {
                    wrapMode.SetWrapMode(WrapMode.TileFlipXY);
                    graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
                }
            }

            Rectangle crop = new Rectangle((int)cropLeft(),
                                     (int)cropTop (),
                                     Math.Max(1, width  - (int)cropLeft() - (int)cropRight()),
                                     Math.Max(1, height - (int)cropTop()  - (int)cropBottom()));

            try
            {
                destImage = destImage.Clone(crop, destImage.PixelFormat);
            }
            catch
            {
                resetCrop();
            }

            return destImage;
        }

        private void btnReset_Click(object sender, EventArgs e)
        {
            reset();
            redraw();
        }

        private void rbNone_CheckedChanged(object sender, EventArgs e)
        {
            RadioButton rb = sender as RadioButton;

            if (rb != null && rb.Checked)
            {
                redraw();
            }
        }
        #endregion

        protected override void OnFormClosed(FormClosedEventArgs e)
        {
            base.OnFormClosed(e);
            this.SizeChanged -= delegate (object s, EventArgs ea)
            {
                centerImage();
            };
        }
    }

    internal class ToolStripRadioButton : ToolStripButton
    {
        private int radioButtonGroupId = 0;
        private bool updateButtonGroup = true;

        private Color checkedColor1 = Color.FromArgb(150, 200, 230);
        private Color checkedColor2 = Color.FromArgb(150, 200, 230);

        public ToolStripRadioButton()
        {
            this.CheckOnClick = true;
        }

        [Category("Behavior")]
        public int RadioButtonGroupId
        {
            get
            {
                return radioButtonGroupId;
            }
            set
            {
                radioButtonGroupId = value;

                // Make sure no two radio buttons are checked at the same time
                UpdateGroup();
            }
        }

        [Category("Appearance")]
        public Color CheckedColor1
        {
            get { return checkedColor1; }
            set { checkedColor1 = value; }
        }

        [Category("Appearance")]
        public Color CheckedColor2
        {
            get { return checkedColor2; }
            set { checkedColor2 = value; }
        }

        // Set check value without updating (disabling) other radio buttons in the group
        private void SetCheckValue(bool checkValue)
        {
            updateButtonGroup = false;
            this.Checked = checkValue;
            updateButtonGroup = true;
        }

        // To make sure no two radio buttons are checked at the same time
        private void UpdateGroup()
        {
            if (this.Parent != null)
            {
                // Get number of checked radio buttons in group
                int checkedCount = 0;

                foreach (ToolStripItem item in Parent.Items)
                {
                    ToolStripRadioButton radio = item as ToolStripRadioButton;

                    if (radio != null && radio.RadioButtonGroupId == RadioButtonGroupId && radio.Checked)
                        checkedCount++;
                }

                if (checkedCount > 1)
                {
                    this.Checked = false;
                }
            }
        }

        protected override void OnClick(EventArgs e)
        {
            base.OnClick(e);
            this.Checked = true;
        }

        protected override void OnCheckedChanged(EventArgs e)
        {
            if (this.Parent != null && updateButtonGroup)
            {
                foreach (ToolStripItem item in Parent.Items)
                {
                    ToolStripRadioButton radioButton = item as ToolStripRadioButton;

                    // Disable all other radio buttons with same group id
                    if (radioButton != null && radioButton != this && radioButton.RadioButtonGroupId == this.RadioButtonGroupId)
                    {
                        radioButton.SetCheckValue(false);
                    }
                }
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (this.Checked)
            {
                LinearGradientBrush checkedBackgroundBrush = new LinearGradientBrush(new Point(0, 0), new Point(0, this.Height), CheckedColor1, CheckedColor2);
                e.Graphics.FillRectangle(checkedBackgroundBrush, new Rectangle(new Point(0, 0), this.Size));
            }

            base.OnPaint(e);
        }
    }

}
