using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using FastReport.Utils;
using FastReport.Forms;

namespace FastReport.Design.PageDesigners.Page
{
#if !DEBUG
    [DesignTimeVisible(false)]
#endif
    internal class BandStructure : Control
    {
        private BandStructureNode root;
        private ReportPageDesigner pageDesigner;
        private int offset;

        public Button btnConfigure;
        public bool allowPaint = true;

        public ReportPage Page
        {
            get { return pageDesigner.Page as ReportPage; }
        }

        public Designer Designer
        {
            get { return pageDesigner.Designer; }
        }

        public int Offset
        {
            get { return offset; }
            set { offset = value; }
        }

        private void btnConfigure_Click(object sender, EventArgs e)
        {
            using (ConfigureBandsForm form = new ConfigureBandsForm(Designer))
            {
                form.Page = Page;
                form.ShowDialog();
            }
        }

        private void AddBand(BandBase band, BandStructureNode root)
        {
            if (band == null)
                return;
            BandStructureNode node = root.Add();
            node.AddBand(band);
        }

        private void EnumDataBand(DataBand band, BandStructureNode root)
        {
            if (band == null)
                return;
            BandStructureNode node = root.Add();

            node.AddBand(band.Header);

            node.AddBand(band);
            foreach (BandBase b in band.Bands)
            {
                EnumBand(b, node);
            }

            node.AddBand(band.Footer);
        }

        private void EnumGroupHeaderBand(GroupHeaderBand band, BandStructureNode root)
        {
            if (band == null)
                return;
            BandStructureNode node = root.Add();

            node.AddBand(band.Header);
            node.AddBand(band);
            EnumGroupHeaderBand(band.NestedGroup, node);
            EnumDataBand(band.Data, node);
            node.AddBand(band.GroupFooter);
            node.AddBand(band.Footer);
        }

        private void EnumBand(BandBase band, BandStructureNode root)
        {
            if (band is DataBand)
                EnumDataBand(band as DataBand, root);
            else if (band is GroupHeaderBand)
                EnumGroupHeaderBand(band as GroupHeaderBand, root);
        }

        private void EnumPageBands()
        {
            if (Page == null)
                return;

            if (Page.TitleBeforeHeader)
            {
                AddBand(Page.ReportTitle, root);
                AddBand(Page.PageHeader, root);
            }
            else
            {
                AddBand(Page.PageHeader, root);
                AddBand(Page.ReportTitle, root);
            }
            AddBand(Page.ColumnHeader, root);
            foreach (BandBase b in Page.Bands)
            {
                EnumBand(b, root);
            }
            AddBand(Page.ColumnFooter, root);
            AddBand(Page.ReportSummary, root);
            AddBand(Page.PageFooter, root);
            AddBand(Page.Overlay, root);
        }

        private void EnumNodes(List<BandStructureNode> list, BandStructureNode root)
        {
            if (root != this.root)
                list.Add(root);
            foreach (BandStructureNode node in root.childNodes)
            {
                EnumNodes(list, node);
            }
        }

        private void DrawItems(Graphics g)
        {
            List<BandStructureNode> list = new List<BandStructureNode>();
            EnumNodes(list, root);

            foreach (BandStructureNode node in list)
            {
                int offs = this.LogicalToDevice(24) + Offset - 2;

                RectangleF fillRect = new RectangleF(node.left, node.top + offs, Width - node.left - 1, node.height);
                BandBase bandFill = null;

                foreach (BandBase band in node.bands)
                {
                    bandFill = band;
                    if (band is GroupHeaderBand || band is DataBand)
                        break;
                }

                // fill node area
                bandFill.DrawBandHeader(g, fillRect, false);
                g.DrawRectangle(SystemPens.ControlDark, node.left, node.top + offs, Width - node.left - 1, node.height);

                // draw band title
                float scale = Designer.ZoomDpi;
                using (Font f = Designer.LogicalToDevice(DrawUtils.DefaultFont))
                {
                    foreach (BandBase band in node.bands)
                    {
                        g.DrawLine(SystemPens.ControlDark, node.left + 8 * Designer.DpiMultiplier(), band.Top * scale + offs, Width, band.Top * scale + offs);

                        Rectangle textRect = new Rectangle((int)(node.left + 4), (int)(band.Top * scale + offs + 4),
                            (int)(Width - (node.left + 4) - 4), (int)(band.Height * scale - 4));
                        ObjectInfo info = RegisteredObjects.FindObject(band);
                        string text = Res.Get(info.Text);
                        if (band.GetInfoText() != "")
                            text += ": " + band.GetInfoText();

                        int textHeight = TextRenderer.MeasureText(text, f, textRect.Size).Height;
                        TextFormatFlags flags = textHeight > textRect.Height ?
                            TextFormatFlags.WordBreak : TextFormatFlags.VerticalCenter | TextFormatFlags.WordBreak;
                        TextRenderer.DrawText(g, text, f, textRect, SystemColors.WindowText, flags);

                        if (band.IsAncestor)
                            g.DrawImage(this.GetImage(99), (int)(node.left + Width - 10), (int)(band.Top * scale + offs + 3));
                    }
                }
            }
        }

        private bool PointInRect(Point point, Rectangle rect)
        {
            return point.X >= rect.Left && point.X <= rect.Right && point.Y >= rect.Top && point.Y <= rect.Bottom;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (pageDesigner.Locked)
                return;
            root.Clear();
            EnumPageBands();
            DrawItems(e.Graphics);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (pageDesigner.Locked)
                return;

            List<BandStructureNode> list = new List<BandStructureNode>();
            EnumNodes(list, root);

            float scale = Designer.ZoomDpi;
            int offs = 24 + Offset;
            foreach (BandStructureNode node in list)
            {
                foreach (BandBase band in node.bands)
                {
                    if (PointInRect(e.Location, new Rectangle((int)node.left, (int)(band.Top * scale) + offs,
                      Width - (int)node.left, (int)(band.Height * scale))))
                    {
                        pageDesigner.Workspace.Focus();
                        Designer.CancelPaste();
                        Designer.SelectedObjects.Clear();
                        Designer.SelectedObjects.Add(band);
                        Designer.SelectionChanged(null);
                        break;
                    }
                }
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            if (pageDesigner.Locked)
                return;

            if (e.Button == MouseButtons.Right && Designer.SelectedObjects.Count > 0
              && Designer.SelectedObjects[0] is BandBase)
            {
                ContextMenuBase menu = Designer.SelectedObjects[0].GetContextMenu();
                if (menu != null)
                {
                    menu.Show(this, e.Location);
                }
            }
        }

        protected override void OnDoubleClick(EventArgs e)
        {
            base.OnDoubleClick(e);
            if (Designer.SelectedObjects.Count == 1 && Designer.SelectedObjects[0] is BandBase)
            {
                BandBase band = Designer.SelectedObjects[0] as BandBase;
                if (!band.HasRestriction(Restrictions.DontEdit))
                    band.HandleDoubleClick();
            }
        }

#if !MONO
        private const int WM_PAINT = 0x000F;
        protected override void WndProc(ref Message m)
        {
            if ((m.Msg != WM_PAINT) || (allowPaint && m.Msg == WM_PAINT))
            {
                base.WndProc(ref m);
            }
        }
#endif

        public void Localize()
        {
            btnConfigure.Text = Res.Get("Designer,Workspace,Report,ConfigureBands");
        }

        public void UpdateDpiDependencies()
        {
            btnConfigure.Font = this.LogicalToDevice(DrawUtils.DefaultFont);
            btnConfigure.Height = this.LogicalToDevice(24);
        }

        public BandStructure(ReportPageDesigner pd)
        {
            pageDesigner = pd;
            root = new BandStructureNode(Designer);

            btnConfigure = new Button();
            btnConfigure.Dock = DockStyle.Top;
            btnConfigure.FlatStyle = FlatStyle.Flat;
            btnConfigure.FlatAppearance.BorderColor = SystemColors.ButtonFace;
            btnConfigure.FlatAppearance.BorderSize = 0;
            btnConfigure.TextAlign = ContentAlignment.MiddleLeft;
            btnConfigure.Cursor = Cursors.Hand;
            btnConfigure.Click += new EventHandler(btnConfigure_Click);
            Controls.Add(btnConfigure);
            Localize();
            UpdateDpiDependencies();

#if !MONO
            SetStyle(ControlStyles.UserPaint, true);
#endif
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.DoubleBuffer, true);
            SetStyle(ControlStyles.ResizeRedraw, true);
        }


        private class BandStructureNode
        {
            private Designer designer;
            private int level;
            public float left { get { return level * 8 * designer.DpiMultiplier() - 1; } }
            public float top;
            public float height;
            public List<BandBase> bands;
            public List<BandStructureNode> childNodes;
            public BandStructureNode parent;

            public void AdjustHeight(float height)
            {
                this.height += height;
                if (parent != null)
                    parent.AdjustHeight(height);
            }

            public BandStructureNode Add()
            {
                BandStructureNode result = new BandStructureNode(designer);
                childNodes.Add(result);
                result.parent = this;
                result.level = level + 1;
                return result;
            }

            public void AddBand(BandBase band)
            {
                if (band != null)
                {
                    if (band.Child != null && band.Child.FillUnusedSpace)
                    {
                        AddBand(band.Child);
                        AddBandInternal(band);
                    }
                    else
                    {
                        AddBandInternal(band);
                        AddBand(band.Child);
                    }
                }
            }

            private void AddBandInternal(BandBase band)
            {
                if (band != null)
                {
                    bands.Add(band);
                    float scale = designer.ZoomDpi;
                    if (bands.Count == 1)
                        top = band.Top * scale;
                    float height = band.Height * scale + 4;
                    AdjustHeight(height);
                }
            }

            public void Clear()
            {
                bands.Clear();
                while (childNodes.Count > 0)
                {
                    childNodes[0].Clear();
                    childNodes.RemoveAt(0);
                }
            }

            public BandStructureNode(Designer designer)
            {
                this.designer = designer;
                bands = new List<BandBase>();
                childNodes = new List<BandStructureNode>();
                level = -1;
            }
        }
    }
}
