using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

// thanks to Mavamaarten~ for coding this

namespace Quasar.Server.Controls
{
    internal class DotNetBarTabControl : TabControl
    {
        // Cached render bitmap — only rebuilt when layout changes
        private Bitmap _cache;
        private int _cacheSelectedIndex = -1;
        private Size _cacheSize;

        // Pre-allocated brushes and pens
        private static readonly SolidBrush _sidebarBrush      = new SolidBrush(Color.FromArgb(50, 50, 55));
        private static readonly SolidBrush _selectedTabBrush  = new SolidBrush(Color.FromArgb(65, 65, 72));
        private static readonly SolidBrush _textBrush         = new SolidBrush(Color.White);
        private static readonly SolidBrush _unselTextBrush    = new SolidBrush(Color.FromArgb(180, 180, 185));
        private static readonly SolidBrush _transBrush        = new SolidBrush(Color.Transparent);
        private static readonly Pen        _borderPen         = new Pen(Color.FromArgb(70, 70, 78));

        private static readonly StringFormat _centerFormat = new StringFormat
        {
            LineAlignment = StringAlignment.Center,
            Alignment = StringAlignment.Center
        };

        public DotNetBarTabControl()
        {
            SetStyle(
                ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw |
                ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
            SizeMode = TabSizeMode.Fixed;
            ItemSize = new Size(44, 136);
            Alignment = TabAlignment.Left;
            SelectedIndex = 0;
        }

        private bool NeedsRedraw()
        {
            return _cache == null
                || _cacheSize != Size
                || _cacheSelectedIndex != SelectedIndex;
        }

        private void RebuildCache()
        {
            _cache?.Dispose();
            _cache = new Bitmap(Width, Height);
            _cacheSize = Size;
            _cacheSelectedIndex = SelectedIndex;

            using (Graphics g = Graphics.FromImage(_cache))
            {
                g.Clear(Color.Transparent);

                // Sidebar background
                g.FillRectangle(_sidebarBrush, new Rectangle(0, 0, ItemSize.Height + 4, Height));
                g.DrawLine(_borderPen, new Point(ItemSize.Height + 3, 0), new Point(ItemSize.Height + 3, Height));
                g.DrawLine(_borderPen, new Point(0, Height - 1), new Point(Width + 3, Height - 1));

                for (int i = 0; i < TabCount; i++)
                {
                    Rectangle tabRect = GetTabRect(i);
                    Rectangle x2 = new Rectangle(
                        tabRect.Location.X - 2,
                        tabRect.Location.Y - 2,
                        tabRect.Width + 3,
                        tabRect.Height - 1);

                    if (i == SelectedIndex)
                    {
                        g.FillRectangle(_selectedTabBrush, x2);
                        g.DrawRectangle(_borderPen, x2);

                        g.SmoothingMode = SmoothingMode.HighQuality;
                        Point[] arrow =
                        {
                            new Point(ItemSize.Height - 3, tabRect.Location.Y + 20),
                            new Point(ItemSize.Height + 4, tabRect.Location.Y + 14),
                            new Point(ItemSize.Height + 4, tabRect.Location.Y + 27)
                        };
                        g.FillPolygon(_transBrush, arrow);
                        g.DrawPolygon(_borderPen, arrow);
                        g.SmoothingMode = SmoothingMode.Default;

                        Font boldFont = new Font(Font.FontFamily, Font.Size, FontStyle.Bold);
                        if (ImageList != null)
                        {
                            try
                            {
                                g.DrawImage(ImageList.Images[TabPages[i].ImageIndex],
                                    new Point(x2.Location.X + 8, x2.Location.Y + 6));
                                g.DrawString("  " + TabPages[i].Text, boldFont, _textBrush, x2, _centerFormat);
                            }
                            catch
                            {
                                g.DrawString(TabPages[i].Text, boldFont, _textBrush, x2, _centerFormat);
                            }
                        }
                        else
                        {
                            g.DrawString(TabPages[i].Text, boldFont, _textBrush, x2, _centerFormat);
                        }
                        boldFont.Dispose();
                    }
                    else
                    {
                        g.FillRectangle(_sidebarBrush, x2);
                        g.DrawLine(_borderPen, new Point(x2.Right, x2.Top), new Point(x2.Right, x2.Bottom));

                        if (ImageList != null)
                        {
                            try
                            {
                                g.DrawImage(ImageList.Images[TabPages[i].ImageIndex],
                                    new Point(x2.Location.X + 8, x2.Location.Y + 6));
                                g.DrawString("  " + TabPages[i].Text, Font, _unselTextBrush, x2, _centerFormat);
                            }
                            catch
                            {
                                g.DrawString(TabPages[i].Text, Font, _unselTextBrush, x2, _centerFormat);
                            }
                        }
                        else
                        {
                            g.DrawString(TabPages[i].Text, Font, _unselTextBrush, x2, _centerFormat);
                        }
                    }
                }
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (!DesignMode && SelectedTab != null)
                SelectedTab.BackColor = Color.FromArgb(32, 32, 36);

            if (NeedsRedraw())
                RebuildCache();

            e.Graphics.DrawImage(_cache, Point.Empty);
        }

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