using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Windows.Forms;
namespace DevComponents.DotNetBar
{
	/// 
	/// Represents base class for tab display.
	/// 
	internal class TabStripBaseDisplay
	{
		#region Private Variables
		private bool m_AntiAlias=true;
		private bool m_HorizontalText=false;
        private bool m_CloseButtonOnTabs = false;
        private Size m_TabCloseButtonSize = new Size(11, 11);
        private int m_TabCloseButtonSpacing = 3;
		#endregion
		public TabStripBaseDisplay()
		{
		}
		#region Methods
		/// 
		/// Main method for painting.
		/// 
		/// Reference to graphics object
		/// TabStrip to paint
		public virtual void Paint(Graphics g, TabStrip tabStrip)
		{
			if(this.AntiAlias)
			{
				g.SmoothingMode=SmoothingMode.AntiAlias;
                g.TextRenderingHint = DisplayHelp.AntiAliasTextRenderingHint;
			}
		}
		protected virtual void PaintTab(Graphics g, TabItem tab, bool first, bool last)
		{
			if(!tab.Visible)
				return;
            if (tab.Parent!=null && tab.Parent.HasPreRenderTabItem)
            {
                RenderTabItemEventArgs re = new RenderTabItemEventArgs(tab, g);
                tab.Parent.InvokePreRenderTabItem(re);
                if (re.Cancel) return;
            }
            using (GraphicsPath path = GetTabItemPath(tab, first, last))
            {
                TabColors colors = tab.Parent.GetTabColors(tab);
                DrawTabItemBackground(tab, path, colors, g);
                DrawTabText(tab, colors, g);
            }
            if (tab.Parent != null && tab.Parent.HasPostRenderTabItem)
            {
                RenderTabItemEventArgs re = new RenderTabItemEventArgs(tab, g);
                tab.Parent.InvokePostRenderTabItem(re);
            }
		}
		protected virtual Region GetTabsRegion(TabsCollection tabs, TabItem lastTab)
		{
			return null;
		}
		protected virtual void DrawTabItemBackground(TabItem tab, GraphicsPath path, TabColors colors, Graphics g)
		{
			RectangleF rf=path.GetBounds();
			Rectangle tabRect=new Rectangle((int)rf.X, (int)rf.Y, (int)rf.Width, (int)rf.Height);
            SmoothingMode sm = g.SmoothingMode;
            g.SmoothingMode = SmoothingMode.Default;
            eBackgroundColorBlendType blendType = colors.BackgroundColorBlend.GetBlendType();
            int ga = colors.BackColorGradientAngle;
            if (tab.TabAlignment == eTabStripAlignment.Left || tab.TabAlignment == eTabStripAlignment.Right)
                ga -= 90;
            if (blendType != eBackgroundColorBlendType.Invalid)
            {
                if (blendType == eBackgroundColorBlendType.Relative)
                {
                    if (!colors.BackColor.IsEmpty || !colors.BackColor2.IsEmpty)
                    {
                        try
                        {
                            Rectangle rb = Rectangle.Ceiling(rf);
                            rb.Inflate(1, 1);
                            using (LinearGradientBrush brush = DisplayHelp.CreateLinearGradientBrush(rb, colors.BackColor, colors.BackColor2, ga))
                            {
                                brush.InterpolationColors = colors.BackgroundColorBlend.GetColorBlend();
                                g.FillPath(brush, path);
                            }
                        }
                        catch
                        {
                            blendType = eBackgroundColorBlendType.Invalid;
                        }
                    }
                }
                else
                {
                    Rectangle bounds = Rectangle.Ceiling(rf);
                    BackgroundColorBlendCollection bc = colors.BackgroundColorBlend;
                    Region oldClip = g.Clip;
                    g.SetClip(path, CombineMode.Intersect);
                    for (int i = 0; i < bc.Count; i += 2)
                    {
                        BackgroundColorBlend b1 = bc[i];
                        BackgroundColorBlend b2 = null;
                        if (i < bc.Count)
                            b2 = bc[i + 1];
                        if (b1 != null && b2 != null)
                        {
                            Rectangle rb = new Rectangle(bounds.X, bounds.Y + (int)b1.Position, bounds.Width,
                                (b2.Position == 1f ? bounds.Height : (int)b2.Position) - (int)b1.Position);
                            using (LinearGradientBrush brush = DisplayHelp.CreateLinearGradientBrush(rb, b1.Color, b2.Color, ga))
                                g.FillRectangle(brush, rb);
                        }
                    }
                    g.Clip = oldClip;
                }
            }
            if (blendType == eBackgroundColorBlendType.Invalid)
            {
                if (colors.BackColor2.IsEmpty)
                {
                    if (!colors.BackColor.IsEmpty)
                    {
                        using (SolidBrush brush = new SolidBrush(colors.BackColor))
                            g.FillPath(brush, path);
                    }
                }
                else
                {
                    using (SolidBrush brush = new SolidBrush(Color.White))
                        g.FillPath(brush, path);
                    using (LinearGradientBrush brush = CreateTabGradientBrush(tabRect, colors.BackColor, colors.BackColor2, ga))
                        g.FillPath(brush, path);
                }
            }
            g.SmoothingMode = sm;
            DrawTabBorder(tab, path, colors, g);
		}
        protected virtual void DrawTabBorder(TabItem tab, GraphicsPath path, TabColors colors, Graphics g)
        {
            if (!colors.BorderColor.IsEmpty)
            {
                using (Pen pen = new Pen(colors.BorderColor, 1))
                    g.DrawPath(pen, path);
            }
        }
		protected virtual LinearGradientBrush CreateTabGradientBrush(Rectangle r,Color color1,Color color2,int gradientAngle)
		{
			if(r.Width<=0)
				r.Width=1;
			if(r.Height<=0)
				r.Height=1;
			LinearGradientBrush brush=new System.Drawing.Drawing2D.LinearGradientBrush(new Rectangle(r.X,r.Y-1,r.Width,r.Height+1),color1,color2,gradientAngle);
			return brush;
		}
        private Rectangle DrawCloseButton(Graphics g, bool vertical, bool mouseOver, bool isSelected, ref Rectangle tabRect, TabStrip tabStrip)
        {
            return DrawCloseButton(g, vertical, mouseOver, isSelected, ref tabRect, tabStrip, true);
        }
        private Rectangle DrawCloseButton(Graphics g, bool vertical, bool mouseOver, bool isSelected, ref Rectangle tabRect, TabStrip tabStrip, bool performPaint)
        {
            Size closeSize = Dpi.Size(m_TabCloseButtonSize);
            int spacing = m_TabCloseButtonSpacing;
            bool closeOnLeftSide = (tabStrip.CloseButtonPosition == eTabCloseButtonPosition.Left);
            Rectangle close = Rectangle.Empty; //new Rectangle(tabRect.X + spacing, tabRect.Y + (tabRect.Height - closeSize.Height) / 2, closeSize.Width, closeSize.Height);
            if (closeOnLeftSide)
            {
                if (vertical)
                    close = new Rectangle(tabRect.X + (tabRect.Width - closeSize.Width) / 2, tabRect.Y + spacing, closeSize.Width, closeSize.Height);
                else
                    close = new Rectangle(tabRect.X + spacing, tabRect.Y + (tabRect.Height - closeSize.Height) / 2, closeSize.Width, closeSize.Height);
            }
            else
            {
                if (vertical)
                    close = new Rectangle(tabRect.X + (tabRect.Width - closeSize.Width) / 2, tabRect.Bottom - spacing - closeSize.Height, closeSize.Width, closeSize.Height);
                else
                    close = new Rectangle(tabRect.Right - spacing - closeSize.Width, tabRect.Y + (tabRect.Height - closeSize.Height) / 2, closeSize.Width, closeSize.Height);
            }
            AdjustCloseButtonRectangle(ref close, closeOnLeftSide, vertical);
            if (performPaint)
                TabStripBaseDisplay.PaintTabItemCloseButton(g, close, mouseOver, isSelected, tabStrip);
            if (vertical)
            {
                if (closeOnLeftSide)
                    tabRect.Y += close.Height + spacing * 2;
                tabRect.Height -= close.Height + spacing * 2;
            }
            else
            {
                if (closeOnLeftSide)
                    tabRect.X += close.Width + spacing * 2;
                tabRect.Width -= close.Width + spacing * 2;
            }
            return close;
        }
        protected virtual void AdjustCloseButtonRectangle(ref Rectangle close, bool closeOnLeftSide, bool vertical) { }
        protected virtual void DrawTabText(TabItem tab, TabColors colors, Graphics g)
        {
            int MIN_TEXT_WIDTH = 4;
            eTextFormat strFormat = eTextFormat.Default | eTextFormat.SingleLine | eTextFormat.EndEllipsis | eTextFormat.HorizontalCenter | eTextFormat.VerticalCenter;
            eTabStripAlignment align = tab.Parent.TabAlignment;
            Rectangle rText = tab.DisplayRectangle;
            bool isVertical = ((align == eTabStripAlignment.Left || align == eTabStripAlignment.Right) && !m_HorizontalText);
            if (m_CloseButtonOnTabs && tab.CloseButtonVisible)
            {
                if (isVertical)
                {
                    rText.Y++;
                    rText.Height--;
                }
                else
                {
                    rText.X += 2;
                    rText.Width -= 2;
                }
                bool renderCloseButton = tab.IsSelected || tab.IsMouseOver || tab.Parent.CloseButtonOnTabsAlwaysDisplayed;
                tab.CloseButtonBounds = DrawCloseButton(g, isVertical, tab.CloseButtonMouseOver, tab.IsSelected, ref rText, tab.Parent, renderCloseButton);
            }
            // Draw image
            CompositeImage image = GetTabImage(tab);
            if (image != null && image.Width + 4 <= rText.Width)
            {
                if (align == eTabStripAlignment.Top || align == eTabStripAlignment.Bottom || m_HorizontalText)
                {
                    image.DrawImage(g, new Rectangle(rText.X + 3, rText.Y + (rText.Height - image.Height) / 2, image.Width, image.Height));
                    int offset = image.Width + 2;
                    rText.X += offset;
                    rText.Width -= offset;
                }
                else
                {
                    image.DrawImage(g, new Rectangle(rText.X + (rText.Width - image.Width) / 2, rText.Y + 3, image.Width, image.Height));
                    int offset = image.Height + 2;
                    rText.Y += offset;
                    rText.Height -= offset;
                }
            }
            if (image != null) image.Dispose();
            // Draw text
            bool isSelected = tab == tab.Parent.SelectedTab;
            if (!tab.Parent.DisplaySelectedTextOnly || isSelected)
            {
                if (!tab.Parent.AntiAlias)
                    g.TextRenderingHint = TextRenderingHint.SystemDefault;
                Font font = tab.Parent.Font;
                if (isSelected && tab.Parent.SelectedTabFont != null)
                    font = tab.Parent.SelectedTabFont;
                AdjustTextRectangle(ref rText, align);
                if (isVertical)
                {
                    g.RotateTransform(90);
                    rText = new Rectangle(rText.Top, -rText.Right, rText.Height, rText.Width);
                }
                if (rText.Width > MIN_TEXT_WIDTH)
                {
                    if ((align == eTabStripAlignment.Left || align == eTabStripAlignment.Right) && !m_HorizontalText)
                        TextDrawing.DrawStringLegacy(g, tab.Text, font, colors.TextColor, rText, strFormat);
                    else
                        TextDrawing.DrawString(g, tab.Text, font, colors.TextColor, rText, strFormat);
                }
                //using (Pen pen = new Pen(Color.Red))
                //    g.DrawRectangle(pen, rText);
                if (isVertical)
                    g.ResetTransform();
                if (!tab.Parent.AntiAlias)
                    g.TextRenderingHint = DisplayHelp.AntiAliasTextRenderingHint;
                if (tab.Parent.ShowFocusRectangle && tab.Parent.Focused && isSelected)
                    ControlPaint.DrawFocusRectangle(g, GetFocusRectangle(tab.DisplayRectangle));
            }
        }
        protected virtual void AdjustTextRectangle(ref Rectangle rText, eTabStripAlignment tabAlignment) {}
		protected virtual CompositeImage GetTabImage(TabItem tab)
		{
			Image image=tab.GetImage();
			if(image!=null)
				return new CompositeImage(image,false);
			Icon icon=tab.Icon;
			if(icon!=null)
				return new CompositeImage(icon,false,tab.IconSize);
			return null;
		}
		protected virtual Rectangle GetFocusRectangle(Rectangle rText)
		{
			rText.Inflate(-1,-1);
			return rText;
		}
		protected virtual TabItem GetLastVisibleTab(TabsCollection tabs)
		{
			int c=tabs.Count-1;
			for(int i=c;i>=0;i--)
			{
				if(tabs[i].Visible)
					return tabs[i];
			}
			return null;
		}
        protected virtual void DrawBackground(TabStrip tabStrip, Rectangle tabStripRect, Graphics g, TabColorScheme colors, Region tabsRegion, eTabStripAlignment tabAlignment, Rectangle selectedTabRect)
		{
			if(colors.TabBackground2.IsEmpty)
			{
                if (!colors.TabBackground.IsEmpty)
                {
                    using (SolidBrush brush = new SolidBrush(colors.TabBackground))
                        g.FillRegion(brush, tabsRegion);
                }
			}
			else
			{
				using(LinearGradientBrush brush=BarFunctions.CreateLinearGradientBrush(tabsRegion.GetBounds(g),colors.TabBackground,colors.TabBackground2,colors.TabBackgroundGradientAngle))
					g.FillRegion(brush, tabsRegion);
			}
		}
		#endregion
		#region Tab Path Functions
		protected virtual GraphicsPath GetTabItemPath(TabItem tab, bool bFirst, bool bLast)
		{
			return null;
		}
		protected virtual ArcData GetCornerArc(Rectangle bounds, int cornerDiameter, eCornerArc corner)
		{
			ArcData a;
			int diameter=cornerDiameter*2;
			switch(corner)
			{
				case eCornerArc.TopLeft:
					a=new ArcData(bounds.X,bounds.Y,diameter,diameter,180,90);
					break;
				case eCornerArc.TopRight:
					a=new ArcData(bounds.Right-diameter,bounds.Y,diameter,diameter,270,90);
					break;
				case eCornerArc.BottomLeft:
					a=new ArcData(bounds.X,bounds.Bottom-diameter,diameter,diameter,90,90);
					break;
				default: // eCornerArc.BottomRight:
					a=new ArcData(bounds.Right-diameter,bounds.Bottom-diameter,diameter,diameter,0,90);
					break;
			}
			return a; 
		}
		#endregion
		#region Properties
		/// 
		/// Gets or sets whether anti-alias is used for painting the tabs. Default value is true.
		/// 
		public virtual bool AntiAlias
		{
			get {return m_AntiAlias;}
			set {m_AntiAlias=value;}
		}
		/// 
		/// Gets or sets whether text is drawn horizontaly regardless of tab orientation.
		/// 
		public bool HorizontalText
		{
			get {return m_HorizontalText;}
			set {m_HorizontalText=value;}
		}
        /// 
        /// Gets or sets whether close button is painted on each tab.
        /// 
        public bool CloseButtonOnTabs
        {
            get { return m_CloseButtonOnTabs; }
            set { m_CloseButtonOnTabs = value; }
        }
        public int TabCloseButtonSpacing
        {
            get { return m_TabCloseButtonSpacing; }
            set { m_TabCloseButtonSpacing=value; }
        }
		#endregion
        #region Static Methods
        public static void PaintTabItemCloseButton(Graphics g, Rectangle r, bool mouseOver, bool isSelected, TabStrip tabStrip)
        {
            if (tabStrip.TabCloseButtonNormal != null)
            {
                Image image = tabStrip.TabCloseButtonNormal;
                if (mouseOver && tabStrip.TabCloseButtonHot != null)
                    image = tabStrip.TabCloseButtonHot;
                g.DrawImageUnscaled(image, r);
                return;
            }
            
            Color fillColor = tabStrip.ColorScheme.TabItemBorder;
            Color lineColor = tabStrip.ColorScheme.TabItemBorderLight;
            if (!fillColor.IsEmpty && Math.Abs(fillColor.GetBrightness() - lineColor.GetBrightness()) <= .38)
            {
                lineColor = BarFunctions.Ligten(lineColor, 60);
                fillColor = BarFunctions.Darken(fillColor, 40);
            }
            if (fillColor.IsEmpty)
            {
                if(tabStrip.Style == eTabStripStyle.Office2003)
                {
                    fillColor = tabStrip.ColorScheme.TabPanelBackground;
                    lineColor = tabStrip.ColorScheme.TabItemText;
                }
                else if (!tabStrip.ColorScheme.TabItemSelectedBorder.IsEmpty)
                {
                    fillColor = tabStrip.ColorScheme.TabItemSelectedBorder;
                    if (!tabStrip.ColorScheme.TabItemSelectedBorderLight.IsEmpty)
                        lineColor = tabStrip.ColorScheme.TabItemSelectedBorderLight;
                    else if (isSelected && fillColor == tabStrip.ColorScheme.TabItemSelectedBackground && !tabStrip.ColorScheme.TabItemSelectedText.IsEmpty)
                        lineColor = tabStrip.ColorScheme.TabItemSelectedText;
                    else if (tabStrip.Style == eTabStripStyle.Metro)
                        lineColor = tabStrip.ColorScheme.TabBackground;
                }
                else
                {
                    fillColor = SystemColors.ControlDark;
                    lineColor = SystemColors.ControlLightLight;
                }
            }
            if(lineColor.IsEmpty)
                lineColor = SystemColors.ControlLightLight;
            //if (!fillColor.IsEmpty && Math.Abs(fillColor.GetBrightness() - lineColor.GetBrightness()) <= .38)
            //{
            //    if (lineColor.GetBrightness() < .5)
            //    {
            //        fillColor = lineColor;
            //        lineColor = SystemColors.ControlLightLight;
            //    }
            //    else
            //        fillColor = (lineColor.GetBrightness() < .5 ? SystemColors.ControlLightLight : SystemColors.ControlDarkDark);
            //}
            fillColor = Color.FromArgb(mouseOver ? 255 : 200, fillColor);
            if ((tabStrip.Style == eTabStripStyle.Metro || tabStrip.Style == eTabStripStyle.Office2003) && !mouseOver)
            {
                if (StyleManager.Style == eStyle.Metro || StyleManager.Style == eStyle.OfficeMobile2014 || StyleManager.Style == eStyle.Office2016)
                    lineColor = fillColor;
                fillColor = Color.Empty;
            }
            if (!fillColor.IsEmpty)
            {
                SmoothingMode sm = g.SmoothingMode;
                g.SmoothingMode = SmoothingMode.AntiAlias;
                using (Pen pen = new Pen(Color.White, Dpi.Width1))
                    g.DrawEllipse(pen, r);
                using (Pen pen = new Pen(fillColor, Dpi.Width1))
                {
                    g.DrawEllipse(pen, r);
                }
                using (SolidBrush brush = new SolidBrush(Color.White))
                    g.FillEllipse(brush, r);
                using (SolidBrush brush = new SolidBrush(fillColor))
                {
                    g.FillEllipse(brush, r);
                }
                g.SmoothingMode = sm;
            }
            using (Pen pen = new Pen(lineColor, Dpi.Width1))
            {
                Rectangle close = r;
                close.Inflate(-Dpi.Width3, -Dpi.Height3);
                close.Width--;
                g.DrawLine(pen, close.X, close.Y, close.Right, close.Bottom);
                g.DrawLine(pen, close.X, close.Bottom, close.Right, close.Y);
                close.Offset(1, 0);
                g.DrawLine(pen, close.X, close.Y, close.Right, close.Bottom);
                g.DrawLine(pen, close.X, close.Bottom, close.Right, close.Y);
            }
        }
        #endregion
    }
}