// This is a part of the Smart PropertyGrid Library
// Copyright (C) 2001-2006 VisualHint
// All rights reserved.
//
// This source code can be used, distributed or modified
// only under terms and conditions
// of the accompanying license agreement.

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;

namespace VisualHint.SmartPropertyGrid
{
	/// <summary>
	/// Base class for drawing manager classes that are used to modify the overall appearance of the PropertyGrid.
	/// </summary>
	public class CustomDrawManager
	{
		private PropertyGrid _grid;

        public char PasswordChar
        {
            get
            {
                return (Environment.OSVersion.Version.Major > 4 ? '\u25CF' /*'\u2022'*/ : '*');
            }
        }

        public virtual int CommentsGapHeight
        {
            get { return 6; }
        }

        public PropertyGrid Grid
		{
			set { _grid = value; }
			get { return _grid; }
		}

		public virtual void DrawCommentsGap(Graphics graphics, Rectangle commentRect) {}

		public virtual void DrawCommentsBackground(Graphics graphics, Rectangle commentRect) {}

		public virtual void DrawCommentText(Graphics graphics, Rectangle rect, PropertyVisibleDeepEnumerator enumerator)
		{
            Font font = Grid.Font;

            Font boldFont = new Font(font, FontStyle.Bold);
            Win32Calls.DrawText(graphics, enumerator.Property.DisplayName, ref rect, boldFont, Grid.CommentsForeColor,
                Win32Calls.DT_LEFT | Win32Calls.DT_SINGLELINE | Win32Calls.DT_TOP | Win32Calls.DT_NOPREFIX);

            Size size = Win32Calls.GetTextExtent(graphics, enumerator.Property.DisplayName, boldFont);
            rect.Y += size.Height + 3;
            rect.Height -= size.Height + 3;

            string comment = enumerator.Property.Comment;
#if _SPGEVAL
            comment += "\nEvaluation version from VisualHint.\nPurchase it at http://www.visualhint.com";
#endif
            Win32Calls.DrawText(graphics, comment, ref rect, font, Grid.CommentsForeColor,
                Win32Calls.DT_LEFT | Win32Calls.DT_WORDBREAK | Win32Calls.DT_TOP | Win32Calls.DT_NOPREFIX);
        }

        public virtual void DrawButtonsGap(Graphics graphics, Rectangle commentRect) {}

        public virtual void DrawButtonsBackground(Graphics graphics, Rectangle rect) {}

        public virtual void DrawGridBackground(Graphics graphics, Rectangle rect) { }

        public virtual void DrawPropertyValueBackground(Graphics graphics, Rectangle rect, PropertyEnumerator enumerator)
        {
            Rectangle fillRect = Rectangle.Intersect(rect, Rectangle.Round(graphics.ClipBounds));
            if (fillRect.IsEmpty)
                return;

            Brush brush = new SolidBrush(enumerator.Property.Value.BackColor);
            graphics.FillRectangle(brush, fillRect);
            brush.Dispose();
        }

        public virtual void DrawLeftColumnBackground(Graphics graphics, Rectangle rect, PropertyEnumerator propEnum) { }

        public virtual void NcCalcSize(ref Rectangle rect) { }

        public virtual void DrawGridNonClientArea(Graphics graphics, Rectangle rect) { }

        public virtual void DrawSeparationLines(Graphics graphics, Rectangle itemRect, Rectangle labelRect, PropertyEnumerator enumSelf)
        {
			Pen pen = new Pen(Grid.GridColor);

			// Draw the lines
			Property property = enumSelf.Property;
			if ((property.TreeLevel > 0) || ShowCategoryVerticalLine)
				graphics.DrawLine(pen, labelRect.Right, labelRect.Top, labelRect.Right, labelRect.Bottom - 1);
			graphics.DrawLine(pen, itemRect.Left + Grid.LeftColumnWidth + 1, itemRect.Bottom - 1,
				itemRect.Right, itemRect.Bottom - 1);

            pen.Dispose();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="itemRect"></param>
        /// <param name="propEnum"></param>
        public virtual void DrawPlusMinusSign(Graphics graphics, Rectangle itemRect, PropertyEnumerator propEnum)
        {
            Property property = propEnum.Property;

            Rectangle signRect = property.GetSignRect(itemRect);
            if (Rectangle.Intersect(signRect, Rectangle.Round(graphics.ClipBounds)).IsEmpty)
                return;

            if (Application.RenderWithVisualStyles)
            {
                signRect.Inflate(2, 2);
                VisualStyleRenderer renderer = new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Closed);
                if (property.Expanded)
                    renderer.SetParameters(VisualStyleElement.TreeView.Glyph.Opened);

                renderer.DrawBackground(graphics, signRect);
            }
            else
            {
                // Draw a square
                Color color;
                if (property.Selected && (propEnum.Property.TreeLevel > 0))
//                if (property.Selected && enumerator.HasParent())
                {
                    if (Grid.GridContainsFocus)
                        color = Grid.HighlightedTextColor;
                    else
                        color = property.ForeColor;
                }
                else
                    color = property.ForeColor;

                Pen pen = new Pen(color);
                graphics.DrawRectangle(pen, signRect);

                // Find the center of this square
                Point ptCenter = new Point(signRect.X + signRect.Width / 2, signRect.Y + signRect.Height / 2);

                // Draw a horizontal bar (minus)
                graphics.DrawLine(pen, ptCenter.X - 2, ptCenter.Y, ptCenter.X + 2, ptCenter.Y);

                // If the children are hidden, we have to draw the +
                if (!property.Expanded)
                {
                    // Draw a vertical bar (plus)
                    graphics.DrawLine(pen, ptCenter.X, ptCenter.Y - 2, ptCenter.X, ptCenter.Y + 2);
                }

                pen.Dispose();
            }
        }

        public virtual void DrawCategoryLabelBackground(Graphics graphics, Rectangle rect, PropertyEnumerator enumSelf)
        {
		}

        public virtual void DrawCategoryLabelText(Graphics graphics, Rectangle rect, Color textColor, PropertyEnumerator enumSelf)
        {
            // Set the font to bold for categories
            // We use the font of the properties control for that
            Font boldFont = new Font(Grid.Font, FontStyle.Bold);

            // Draw the category label
            //------------------------

            Win32Calls.DrawText(graphics, enumSelf.Property.DisplayName, ref rect, boldFont, textColor,
                Win32Calls.DT_SINGLELINE | Win32Calls.DT_VCENTER | Win32Calls.DT_END_ELLIPSIS | Win32Calls.DT_NOPREFIX);
        }

        public virtual void DrawCategoryValue(Graphics graphics, Rectangle valueRect, Color textColor, PropertyEnumerator enumSelf)
        {
            // Set the font to bold for categories
            // We use the font of the properties control for that
            Font boldFont = new Font(Grid.Font, FontStyle.Bold);

            // Draw the category info text
            //----------------------------

            Win32Calls.DrawText(graphics, ((RootCategory)enumSelf.Property).ValueText,
                ref valueRect, boldFont, textColor,
                Win32Calls.DT_SINGLELINE | Win32Calls.DT_VCENTER | Win32Calls.DT_NOPREFIX |
                ((_grid.EllipsisMode & PropertyGrid.EllipsisModes.EllipsisOnValues) != 0 ? Win32Calls.DT_END_ELLIPSIS : 0));
        }

        public virtual void DrawSubCategoryLabelBackground(Graphics graphics, int labelRectX, Rectangle rect, PropertyEnumerator enumSelf)
        {
        }

        public virtual void DrawSubCategoryLabelText(Graphics graphics, Rectangle rect, Color textColor, PropertyEnumerator enumSelf)
        {
            Property property = enumSelf.Property;
/*
            // Choose the colors of the background and text regarding the item selection
            Color textColor;
	        if (property.Selected)
	        {
		        // 2 possible colors regarding the focus state

                if (Grid.GridContainsFocus)
                    textColor = SystemColors.HighlightText;
		        else
                    textColor = SystemColors.WindowText;
	        }
	        else
	        {
		        // Unlike main categories, we draw in normal text even in .NET mode
                textColor = SystemColors.WindowText;
	        }
*/
	        // Draw the category label
	        //------------------------

            Win32Calls.DrawText(graphics, property.DisplayName, ref rect, Grid.Font, textColor,
                Win32Calls.DT_SINGLELINE | Win32Calls.DT_VCENTER | Win32Calls.DT_END_ELLIPSIS | Win32Calls.DT_NOPREFIX);
        }

		public virtual bool ShowCategoryVerticalLine
		{
			get { return true; }
		}


        public virtual void DrawPropertyLabelBackground(Graphics graphics, Rectangle rect, PropertyVisibleDeepEnumerator enumSelf)
        {
            Rectangle fillRect = Rectangle.Intersect(rect, Rectangle.Round(graphics.ClipBounds));
            if (fillRect.IsEmpty)
                return;

            Brush brush;

            if (enumSelf.Property.Selected) 	// if selected
            {
                Color bkgColor;

                Control topLevelWindow = Control.FromHandle(Win32Calls.GetForegroundWindow());
                bool b = Grid.Grid.ContainsFocus;

                if ((Grid.GridContainsFocus) || (enumSelf.Property.InPlaceCtrlInAction != null))
                    bkgColor = Grid.SelectedBackColor;
                else
                {
                    // If the control has not the focus, the background is light
                    bkgColor = Grid.SelectedNotFocusedBackColor;
                }

                brush = new SolidBrush(bkgColor);
            }
            else
                brush = new SolidBrush(enumSelf.Property.BackColor);

            graphics.FillRectangle(brush, fillRect);

            brush.Dispose();
        }

        public virtual void DrawPropertyLabelText(Graphics graphics, Rectangle rect, Color textColor, PropertyVisibleDeepEnumerator enumSelf)
        {
            if (Rectangle.Intersect(rect, Rectangle.Round(graphics.ClipBounds)).IsEmpty)
                return;

            rect.Height = rect.Height / enumSelf.Property.HeightMultiplier;

            // Draw the text, clipped by the given area
            Win32Calls.DrawText(graphics, enumSelf.Property.DisplayName, ref rect, enumSelf.Property.Font, textColor,
                Win32Calls.DT_SINGLELINE | Win32Calls.DT_VCENTER | Win32Calls.DT_NOPREFIX |
                ((_grid.EllipsisMode & PropertyGrid.EllipsisModes.EllipsisOnLabels) != 0 ? Win32Calls.DT_END_ELLIPSIS : 0));
        }

        public virtual void DrawRadioButton(Graphics graphics, Rectangle radioRect, bool drawChecked, bool drawEnabled)
        {
            if (Application.RenderWithVisualStyles)
            {
                VisualStyleRenderer renderer = null;
                if (drawChecked && drawEnabled)
                    renderer = new VisualStyleRenderer(VisualStyleElement.Button.RadioButton.CheckedNormal);
                else if (!drawChecked && drawEnabled)
                    renderer = new VisualStyleRenderer(VisualStyleElement.Button.RadioButton.UncheckedNormal);
                else if (drawChecked && !drawEnabled)
                    renderer = new VisualStyleRenderer(VisualStyleElement.Button.RadioButton.CheckedDisabled);
                else if (!drawChecked && !drawEnabled)
                    renderer = new VisualStyleRenderer(VisualStyleElement.Button.RadioButton.UncheckedDisabled);

                renderer.DrawBackground(graphics, radioRect);
            }
            else
            {
                ControlPaint.DrawRadioButton(graphics, radioRect, drawChecked ? ButtonState.Checked : ButtonState.Normal);
            }
        }

        public virtual void DrawCheckBox(Graphics graphics, Rectangle buttonRect, bool drawChecked, bool drawEnabled)
        {
            if (Rectangle.Intersect(buttonRect, Rectangle.Round(graphics.ClipBounds)).IsEmpty)
                return;

            if (Application.RenderWithVisualStyles)
            {
                VisualStyleRenderer renderer = null;
                if (drawChecked && drawEnabled)
                    renderer = new VisualStyleRenderer(VisualStyleElement.Button.CheckBox.CheckedNormal);
                else if (!drawChecked && drawEnabled)
                    renderer = new VisualStyleRenderer(VisualStyleElement.Button.CheckBox.UncheckedNormal);
                else if (drawChecked && !drawEnabled)
                    renderer = new VisualStyleRenderer(VisualStyleElement.Button.CheckBox.CheckedDisabled);
                else if (!drawChecked && !drawEnabled)
                    renderer = new VisualStyleRenderer(VisualStyleElement.Button.CheckBox.UncheckedDisabled);

                renderer.DrawBackground(graphics, buttonRect);
            }
            else
            {
                ControlPaint.DrawCheckBox(graphics, buttonRect, drawChecked ? ButtonState.Checked : ButtonState.Normal);
            }
        }

        public virtual void DrawCheckBoxIndeterminate(Graphics graphics, Rectangle buttonRect, bool drawEnabled)
        {
            if (Rectangle.Intersect(buttonRect, Rectangle.Round(graphics.ClipBounds)).IsEmpty)
                return;

            if (Application.RenderWithVisualStyles)
            {
                VisualStyleRenderer renderer = new VisualStyleRenderer(
                    (drawEnabled ? VisualStyleElement.Button.CheckBox.MixedNormal :
                    VisualStyleElement.Button.CheckBox.MixedDisabled));
                renderer.DrawBackground(graphics, buttonRect);
            }
            else
                ControlPaint.DrawCheckBox(graphics, buttonRect, ButtonState.Inactive);
        }

        public virtual void DrawProgressBar(Graphics graphics, Rectangle valueRect, bool drawFrame, int value, int minValue, int maxValue)
        {
            if (Application.RenderWithVisualStyles)
            {
                if (drawFrame)
                {
                    VisualStyleRenderer frameRenderer = new VisualStyleRenderer(VisualStyleElement.ProgressBar.Bar.Normal);
                    frameRenderer.DrawBackground(graphics, valueRect);
                    valueRect = frameRenderer.GetBackgroundContentRectangle(graphics, valueRect);
                }

                valueRect.Width = (int)Math.Round((double)(value - minValue) / (double)(maxValue - minValue) * valueRect.Width);
                VisualStyleRenderer renderer = new VisualStyleRenderer(VisualStyleElement.ProgressBar.Chunk.Normal);
                renderer.DrawBackground(graphics, valueRect);
            }
            else
            {
                Rectangle barRect;
                if (drawFrame)
                    barRect = Rectangle.Inflate(valueRect, -2, -2);
                else
                {
                    valueRect.Width -= 2;
                    barRect = Rectangle.Inflate(valueRect, 0, -2);
                }
                
                int maxRight = barRect.Right - 1;

                int segmentWidth = (int)Math.Round(valueRect.Height * .6666);
                int newSegmentCount = (int)Math.Ceiling(((double)barRect.Width / (double)segmentWidth) *
                    value / (maxValue - minValue));

                Brush barBrush = new SolidBrush(_grid.SelectedBackColor);

                for(int i = 0; i < newSegmentCount; i++)
				{
					barRect.Width = Math.Min(maxRight - barRect.X, segmentWidth - 2);
					graphics.FillRectangle(barBrush, barRect);
					barRect.X += segmentWidth;
				}

                barBrush.Dispose();

                if (drawFrame)
                    ControlPaint.DrawBorder3D(graphics, valueRect, Border3DStyle.Flat);
            }
        }
    }
}
