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

namespace Aga.Controls.Tree
{
	[TypeConverter(typeof(TreeColumnConverter)), DesignTimeVisible(false), ToolboxItem(false)]
	public class TreeColumn : Component
	{
		private class TreeColumnConverter : ComponentConverter
		{
			public TreeColumnConverter()
				: base(typeof(TreeColumn))
			{
			}

			public override bool GetPropertiesSupported(ITypeDescriptorContext context)
			{
				return false;
			}
		}

		private const int HeaderLeftMargin = 5;
        private const int HeaderRightMargin = 5;   
		private const int SortOrderMarkMargin = 8;

        private TextFormatFlags _headerFlags;
        private TextFormatFlags _baseHeaderFlags = TextFormatFlags.NoPadding | 
                                                   TextFormatFlags.EndEllipsis |
                                                   TextFormatFlags.VerticalCenter |
												TextFormatFlags.PreserveGraphicsTranslateTransform;

		#region Properties

        private TreeColumnCollection _owner;
		internal TreeColumnCollection Owner
		{
			get { return _owner; }
			set { _owner = value; }
		}

		[Browsable(false)]
		public int Index
		{
			get
            {
                if (Owner != null)
					return Owner.IndexOf(this);
                return -1;
            }
		}

		private string _header;
		[Localizable(true)]
		public string Header
		{
			get { return _header; }
			set 
			{ 
				_header = value;
				OnHeaderChanged();
			}
		}

		private string _tooltipText;
		[Localizable(true)]
		public string TooltipText
		{
			get { return _tooltipText; }
			set { _tooltipText = value; }
		}

		private int _width;
		[DefaultValue(50), Localizable(true)]
		public int Width
		{
			get
            {
                return _width;
            }
			set 
			{
				if (_width != value)
				{
                    _width = Math.Max(MinColumnWidth, value);
                    if (_maxColumnWidth > 0)
                    {
                        _width = Math.Min(_width, MaxColumnWidth);
                    }
					OnWidthChanged();
				}
			}
		}

        private int _minColumnWidth;
        [DefaultValue(0)]
        public int MinColumnWidth
        {
            get { return _minColumnWidth; }
            set
            {
				if (value < 0)
					throw new ArgumentOutOfRangeException("value");

				_minColumnWidth = value;
                Width = Math.Max(value, Width);
            }
        }

        private int _maxColumnWidth;
        [DefaultValue(0)]
        public int MaxColumnWidth
        {
            get { return _maxColumnWidth; }
            set
            {
				if (value < 0)
					throw new ArgumentOutOfRangeException("value");

				_maxColumnWidth = value;
				if (value > 0)
					Width = Math.Min(value, _width);
            }
        }

		private bool _visible = true;
		[DefaultValue(true)]
		public bool IsVisible
		{
			get { return _visible; }
			set 
			{ 
				_visible = value;
				OnIsVisibleChanged();
			}
		}

		private HorizontalAlignment _textAlign = HorizontalAlignment.Left;
		[DefaultValue(HorizontalAlignment.Left)]
		public HorizontalAlignment TextAlign
		{
			get { return _textAlign; }
			set 
			{
				if (value != _textAlign)
				{
					_textAlign = value;
                    _headerFlags = _baseHeaderFlags | TextHelper.TranslateAligmentToFlag(value);
					OnHeaderChanged();
				}
			}
		}

        private bool _sortable;
        [DefaultValue(false)]
        public bool Sortable
        {
            get { return _sortable; }
            set { _sortable = value; }
        }

		private SortOrder _sort_order = SortOrder.None;
		public SortOrder SortOrder
		{
			get { return _sort_order; }
			set
			{
				if (value == _sort_order)
					return;
				_sort_order = value;
				OnSortOrderChanged();
			}
		}

		public Size SortMarkSize
		{
			get
            {
                if (Application.RenderWithVisualStyles)
					return new Size(9, 5);
                return new Size(7, 4);
            }
		}
		#endregion

		public TreeColumn(): 
			this(string.Empty, 50)
		{
		}

        public TreeColumn(string header, int width)
		{
			_header = header;
			_width = width;
            _headerFlags = _baseHeaderFlags | TextFormatFlags.Left;
		}

		public override string ToString()
        {
            if (string.IsNullOrEmpty(Header))
				return GetType().Name;
            return Header;
        }

		protected override void Dispose(bool disposing)
		{
			base.Dispose(disposing);
		}

		#region Draw

		private static VisualStyleRenderer _normalRenderer;
		private static VisualStyleRenderer _pressedRenderer;
		private static VisualStyleRenderer _hotRenderer;

		private static void CreateRenderers()
		{
			if (Application.RenderWithVisualStyles && _normalRenderer == null)
			{
				_normalRenderer = new VisualStyleRenderer(VisualStyleElement.Header.Item.Normal);
				_pressedRenderer = new VisualStyleRenderer(VisualStyleElement.Header.Item.Pressed);
				_hotRenderer = new VisualStyleRenderer(VisualStyleElement.Header.Item.Hot);
			}
		}

		internal Bitmap CreateGhostImage(Rectangle bounds, Font font)
		{
			Bitmap b = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb);
			Graphics gr = Graphics.FromImage(b);
			gr.FillRectangle(SystemBrushes.ControlDark, bounds);
			DrawContent(gr, bounds, font);
			BitmapHelper.SetAlphaChanelValue(b, 150);
			return b;
		}

		internal void Draw(Graphics gr, Rectangle bounds, Font font, bool pressed, bool hot)
		{
			DrawBackground(gr, bounds, pressed, hot);
			DrawContent(gr, bounds, font);
		}

        private void DrawContent(Graphics gr, Rectangle bounds, Font font)
        {
            if (TreeViewAdv.CustomColumnTextRenderFunc != null)
            {
                TreeViewAdv.CustomColumnTextRenderFunc(gr, bounds, font, Header);
            }
            else
            {
                Rectangle innerBounds = new Rectangle(bounds.X + HeaderLeftMargin, bounds.Y,
                                       bounds.Width - HeaderLeftMargin - HeaderRightMargin,
                                       bounds.Height);

                if (SortOrder != SortOrder.None)
                    innerBounds.Width -= SortMarkSize.Width + SortOrderMarkMargin;

                Size maxTextSize = TextRenderer.MeasureText(gr, Header, font, innerBounds.Size, TextFormatFlags.NoPadding);
                Size textSize = TextRenderer.MeasureText(gr, Header, font, innerBounds.Size, _baseHeaderFlags);

                if (SortOrder != SortOrder.None)
                {
                    int tw = Math.Min(textSize.Width, innerBounds.Size.Width);

                    int x = 0;
                    if (TextAlign == HorizontalAlignment.Left)
                        x = innerBounds.X + tw + SortOrderMarkMargin;
                    else if (TextAlign == HorizontalAlignment.Right)
                        x = innerBounds.Right + SortOrderMarkMargin;
                    else
                        x = innerBounds.X + tw + (innerBounds.Width - tw) / 2 + SortOrderMarkMargin;
                    DrawSortMark(gr, bounds, x);
                }

                if (textSize.Width < maxTextSize.Width)
                    TextRenderer.DrawText(gr, Header, font, innerBounds, SystemColors.ControlText, _baseHeaderFlags | TextFormatFlags.Left);
                else
                    TextRenderer.DrawText(gr, Header, font, innerBounds, SystemColors.ControlText, _headerFlags);
            }
        }

		private void DrawSortMark(Graphics gr, Rectangle bounds, int x)
		{
			int y = bounds.Y + bounds.Height / 2 - 2;
			x = Math.Max(x, bounds.X + SortOrderMarkMargin);

            int w2 = SortMarkSize.Width / 2;
            if (SortOrder == SortOrder.Ascending)
            {
                Point[] points = new Point[] { new Point(x, y), new Point(x + SortMarkSize.Width, y), new Point(x + w2, y + SortMarkSize.Height) };
                gr.FillPolygon(SystemBrushes.ControlDark, points);
            }
            else if (SortOrder == SortOrder.Descending)
            {
                Point[] points = new Point[] { new Point(x - 1, y + SortMarkSize.Height), new Point(x + SortMarkSize.Width, y + SortMarkSize.Height), new Point(x + w2, y - 1) };
                gr.FillPolygon(SystemBrushes.ControlDark, points);
            }
		}

		internal static void DrawDropMark(Graphics gr, Rectangle rect)
		{
			gr.FillRectangle(SystemBrushes.HotTrack, rect.X-1, rect.Y, 2, rect.Height);
		}

		internal static void DrawBackground(Graphics gr, Rectangle bounds, bool pressed, bool hot)
		{
            if (TreeViewAdv.CustomColumnBackgroundRenderFunc != null)
            {
                TreeViewAdv.CustomColumnBackgroundRenderFunc(gr, bounds, pressed, hot);
            }
            else
            {
                if (Application.RenderWithVisualStyles)
                {
                    CreateRenderers();
                    if (pressed)
                        _pressedRenderer.DrawBackground(gr, bounds);
                    else if (hot)
                        _hotRenderer.DrawBackground(gr, bounds);
                    else
                        _normalRenderer.DrawBackground(gr, bounds);
                }
                else
                {
                    gr.FillRectangle(SystemBrushes.Control, bounds);
                    Pen p1 = SystemPens.ControlLightLight;
                    Pen p2 = SystemPens.ControlDark;
                    Pen p3 = SystemPens.ControlDarkDark;
                    if (pressed)
                        gr.DrawRectangle(p2, bounds.X, bounds.Y, bounds.Width, bounds.Height);
                    else
                    {
                        gr.DrawLine(p1, bounds.X, bounds.Y, bounds.Right, bounds.Y);
                        gr.DrawLine(p3, bounds.X, bounds.Bottom, bounds.Right, bounds.Bottom);
                        gr.DrawLine(p3, bounds.Right - 1, bounds.Y, bounds.Right - 1, bounds.Bottom - 1);
                        gr.DrawLine(p1, bounds.Left, bounds.Y + 1, bounds.Left, bounds.Bottom - 2);
                        gr.DrawLine(p2, bounds.Right - 2, bounds.Y + 1, bounds.Right - 2, bounds.Bottom - 2);
                        gr.DrawLine(p2, bounds.X, bounds.Bottom - 1, bounds.Right - 2, bounds.Bottom - 1);
                    }
                }
            }
		}

		#endregion

		#region Events

		public event EventHandler HeaderChanged;
		private void OnHeaderChanged()
        {
            HeaderChanged?.Invoke(this, EventArgs.Empty);
        }

		public event EventHandler SortOrderChanged;
		private void OnSortOrderChanged()
        {
            SortOrderChanged?.Invoke(this, EventArgs.Empty);
        }

		public event EventHandler IsVisibleChanged;
		private void OnIsVisibleChanged()
        {
            IsVisibleChanged?.Invoke(this, EventArgs.Empty);
        }

		public event EventHandler WidthChanged;
		private void OnWidthChanged()
        {
            WidthChanged?.Invoke(this, EventArgs.Empty);
        }

		#endregion
	}
}
