using System;
using System.Windows.Forms;
using System.Drawing;
using System.Reflection;

namespace FastReport.Utils
{
    /// <summary>
    /// A static class that contains methods to auto-convert rtl layout.
    /// </summary>
    public static class UIUtils
    {
        #region RTL support methods

        /// <summary>
        /// Changes control's layout to rtl.
        /// </summary>
        /// <param name="parent"></param>
        public static void CheckRTL(Control parent)
        {
            if (!Config.RightToLeft)
                return;
            
            parent.RightToLeft = RightToLeft.Yes;
            parent.SuspendLayout();
            foreach (Control item in parent.Controls)
            {
                try
                {
                    AnchorStyles anchor = AnchorStyles.None;
                    if ((item.Anchor & AnchorStyles.Left) != 0)
                        anchor |= AnchorStyles.Right;
                    if ((item.Anchor & AnchorStyles.Right) != 0)
                        anchor |= AnchorStyles.Left;
                    if ((item.Anchor & AnchorStyles.Top) != 0)
                        anchor |= AnchorStyles.Top;
                    if ((item.Anchor & AnchorStyles.Bottom) != 0)
                        anchor |= AnchorStyles.Bottom;
                    
                    DockStyle dock = item.Dock;
                    if (item.Dock == DockStyle.Left)
                        dock = DockStyle.Right;
                    if (item.Dock == DockStyle.Right)
                        dock = DockStyle.Left;
                    
                    int width = parent is Form ? (parent as Form).ClientSize.Width : parent.Width;
                    if (!(item is SplitterPanel))
                    {
                        item.Location = new Point(width - item.Size.Width - item.Location.X, item.Location.Y);
                        item.Anchor = anchor;
                        item.Dock = dock;
                    }
                    if (item is TreeView)
                    {
                        (item as TreeView).RightToLeftLayout = true;
                    }
                    CheckRTL(item);
                }
                catch 
                { 
                }
            }
            parent.ResumeLayout();
        }

        #endregion

        #region Draw extension methods

        /// <summary>
        /// Draws an image and a text.
        /// </summary>
        /// <param name="control">The control which is used to determine RTL and DPI settings.</param>
        /// <param name="e">The draw event args.</param>
        /// <param name="img">The image.</param>
        /// <param name="text">The text.</param>
        /// <remarks>This method is used to draw items in an owner-drawn listboxes and comboboxes. It respects RTL and DPI settings of a control.</remarks>
        public static void DrawImageAndText(this Control control, DrawItemEventArgs e, Image img, string text)
        {
            Graphics g = e.Graphics;
            int offsX = 2;
            if (img != null)
            {
                Rectangle imgRect = new Rectangle(
                    control.RightToLeft == RightToLeft.Yes ? e.Bounds.Right - img.Width - control.LogicalToDevice(4) : e.Bounds.X + control.LogicalToDevice(4),
                    e.Bounds.Y + (e.Bounds.Height - img.Height) / 2,
                    img.Width,
                    img.Height);
                g.DrawImage(img, imgRect);
                offsX = img.Width + control.LogicalToDevice(8);
            }

            Rectangle textRect = new Rectangle(
                control.RightToLeft == RightToLeft.Yes ? e.Bounds.X : e.Bounds.X + offsX,
                e.Bounds.Y,
                e.Bounds.Width - offsX,
                e.Bounds.Height);
            TextFormatFlags flags = TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis;
            if (control.RightToLeft == RightToLeft.Yes)
                flags |= TextFormatFlags.RightToLeft | TextFormatFlags.Right;
            TextRenderer.DrawText(g, text, e.Font, textRect, e.ForeColor, flags);
        }

        #endregion

        #region DPI Extension methods

        // Per-monitor DPI support extension methods.
        // In .Net 4.7 MS introduced new properties and methods such as Control.DeviceDpi and Control.LogicalToDeviceUnits.
        // Since we are targeting .Net 4.0, emulate these methods in earlier versions of fw (also on Mono),
        // and use reflection to call these methods if they exist.

        /// <summary>
        /// Gets current dpi value for the control.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <returns>The dpi value.</returns>
        public static int Dpi(this Control control)
        {
            try
            {
                // the reason why we use a form instead of a control: the control may not be updated yet
                // if we call this code from WM_DPICHANGED handler
                Form form = control.FindForm();
                if (form != null)
                    control = form;
                // introduced in .Net 4.7
                PropertyInfo p = control.GetType().GetProperty("DeviceDpi");
                if (p != null)
                    return (int)(p.GetValue(control, null));
            }
            catch
            { }
            
            return DrawUtils.ScreenDpi;
        }

        /// <summary>
        /// Gets current dpi multiplier for the control (1.0 for 96dpi).
        /// </summary>
        /// <param name="control">The control.</param>
        /// <returns>The dpi multiplier.</returns>
        public static float DpiMultiplier(this Control control)
        {
            return control.Dpi() / 96f;
        }

        /// <summary>
        /// Gets current font dpi multiplier for the control (1.0 for 96dpi).
        /// </summary>
        /// <remarks>The return value depends on the base resolution of the main screen.</remarks>
        /// <param name="control">The control.</param>
        /// <returns>The font dpi multiplier.</returns>
        public static float FontDpiMultiplier(this Control control)
        {
            return (float)control.Dpi() / DrawUtils.ScreenDpi;
        }

        /// <summary>
        /// Converts logical units to device units (pixels).
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="value">Logical units.</param>
        /// <returns>Device units.</returns>
        public static int LogicalToDevice(this Control control, int value)
        {
            return (int)(value * control.DpiMultiplier());
        }

        /// <summary>
        /// Converts logical units to device units (pixels).
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="value">Logical units.</param>
        /// <returns>Device units.</returns>
        public static float LogicalToDevice(this Control control, float value)
        {
            return value * control.DpiMultiplier();
        }

        /// <summary>
        /// Converts logical units to device units (pixels).
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="value">Logical units.</param>
        /// <returns>Device units.</returns>
        public static Rectangle LogicalToDevice(this Control control, Rectangle value)
        {
            return new Rectangle(
                control.LogicalToDevice(value.Left),
                control.LogicalToDevice(value.Top),
                control.LogicalToDevice(value.Width),
                control.LogicalToDevice(value.Height));
        }

        /// <summary>
        /// Converts logical units to device units (pixels).
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="value">Logical units.</param>
        /// <returns>Device units.</returns>
        public static Point LogicalToDevice(this Control control, Point value)
        {
            return new Point(
                control.LogicalToDevice(value.X),
                control.LogicalToDevice(value.Y));
        }

        /// <summary>
        /// Converts logical units to device units (pixels).
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="value">Logical units.</param>
        /// <returns>Device units.</returns>
        public static Size LogicalToDevice(this Control control, Size value)
        {
            return new Size(
                control.LogicalToDevice(value.Width),
                control.LogicalToDevice(value.Height));
        }

        /// <summary>
        /// Converts logical font to device font.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="value">Logical font.</param>
        /// <param name="disposeOriginal">Determines whether to dispose the original font or not.</param>
        /// <returns>Device font.</returns>
        public static Font LogicalToDevice(this Control control, Font value, bool disposeOriginal = false)
        {
            float mult = (float)control.Dpi() / DrawUtils.ScreenDpi;
            Font result = new Font(value.Name, value.Size * mult, value.Style);
            if (disposeOriginal)
                value.Dispose();
            return result;
        }


        #endregion

        #region Image helper extension methods

        /// <summary>
        /// Returns an image from resources using control's dpi value.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="index">Image index.</param>
        /// <returns>An image with specified index from "buttons.png" resource.</returns>
        public static Image GetImage(this Control control, int index)
        {
            return Res.GetImage(index, control.Dpi());
        }

        /// <summary>
        /// Returns an image from resources using control's dpi value.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="name">Image name.</param>
        /// <returns>An image with specified index from "buttons.png" resource.</returns>
        public static Image GetImage(this Control control, string name)
        {
            return Res.GetImage(name, control.Dpi());
        }

        /// <summary>
        /// Returns an imagelist from resources using control's dpi value.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <returns>An imagelist from "buttons.png" resource.</returns>
        public static ImageList GetImages(this Control control)
        {
            return Res.GetImages(control.Dpi());
        }

        #endregion


    }

}
