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

namespace FastReport.Design.PageDesigners.Page
{
#if !DEBUG
    [DesignTimeVisible(false)]
#endif
    internal class RulerBase : Control
    {
        public bool allowPaint = true;
        protected ToolTip toolTip;
        private ReportPageDesigner pageDesigner;
        private float offset;

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

        public ReportWorkspace Workspace
        {
            get { return pageDesigner.Workspace; }
        }

        public ReportPageDesigner PageDesigner
        {
            get { return pageDesigner; }
        }

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

        protected bool CheckGridStep(ref float kx, ref float ky)
        {
            bool al = ReportWorkspace.SnapToGrid;
            if (ModifierKeys == Keys.Alt)
                al = !al;

            bool result = true;
            float grid = ReportWorkspace.Grid.SnapSize;
            if (al)
            {
                result = kx >= grid || kx <= -grid || ky >= grid || ky <= -grid;
                if (result)
                {
                    kx = (int)(kx / grid) * grid;
                    ky = (int)(ky / grid) * grid;
                }
            }
            return result;
        }

#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 RulerBase(ReportPageDesigner pd) : base()
        {
            pageDesigner = pd;
            toolTip = new ToolTip();
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        }
    }

#if !DEBUG
    [DesignTimeVisible(false)]
#endif
    internal class HorzRuler : RulerBase
    {
        private PointF lastMousePoint;
        private bool mouseDown;
        private bool mouseMoved;
        private int activeGuide;
        private bool saveAutoGuides;

        private void DrawRuler(Graphics g, float start, float size)
        {
            Font font = new Font("Tahoma", 6f * this.FontDpiMultiplier());
            Brush brush = SystemBrushes.WindowText;
            Pen pen = SystemPens.WindowText;
            int w5 = 5;
            int w10 = 10;
            float dx = (ReportWorkspace.Grid.GridUnits == PageUnits.Millimeters || ReportWorkspace.Grid.GridUnits == PageUnits.Centimeters ?
                Units.Millimeters : Units.TenthsOfInch) * Designer.ZoomDpi;
            string s = (((int)Math.Round(size / dx)) / 10).ToString();
            float maxw = g.MeasureString(s, font).Width;
            float i = start;
            int i1 = 0;

            while (i < start + size)
            {
                int h = 0;
                if (i1 == 0)
                    h = 0;
                else if (i1 % w10 == 0)
                    h = 6;
                else if (i1 % w5 == 0)
                    h = 4;
                else
                    h = 2;

                if (h == 2 && dx * w10 < this.LogicalToDevice(41))
                    h = 0;
                if (h == 4 && dx * w10 < this.LogicalToDevice(21))
                    h = 0;

                int w = 0;
                if (h == 6)
                {
                    if (maxw > dx * w10 * 1.5f)
                        w = w10 * 4;
                    else if (maxw > dx * w10 * 0.7f)
                        w = w10 * 2;
                    else
                        w = w10;
                }

                if (w != 0 && i1 % w == 0)
                {
                    if (ReportWorkspace.Grid.GridUnits == PageUnits.Millimeters)
                        s = (i1).ToString();
                    else if (ReportWorkspace.Grid.GridUnits == PageUnits.HundrethsOfInch)
                        s = (i1 * 10).ToString();
                    else
                        s = (i1 / 10).ToString();
                    SizeF sz = g.MeasureString(s, font);
                    g.DrawString(s, font, brush, new PointF(Offset + i - sz.Width / 2 + 1, this.LogicalToDevice(7)));
                }
                else if (h != 0)
                {
                    g.DrawLine(pen,
                        Offset + i, this.LogicalToDevice(6 + (13 - h) / 2),
                        Offset + i, this.LogicalToDevice(6 + (13 - h) / 2 + h - 1));
                }
                i += dx;
                i1++;
            }
        }

        private void DrawRulerRtl(Graphics g, float start, float size)
        {
            Font font = new Font("Tahoma", 6f * this.FontDpiMultiplier());
            Brush brush = SystemBrushes.WindowText;
            Pen pen = SystemPens.WindowText;
            int w5 = 5;
            int w10 = 10;
            float dx = (ReportWorkspace.Grid.GridUnits == PageUnits.Millimeters || ReportWorkspace.Grid.GridUnits == PageUnits.Centimeters ?
                Units.Millimeters : Units.TenthsOfInch) * Designer.ZoomDpi;
            string s = (((int)Math.Round(size / dx)) / 10).ToString();
            float maxw = g.MeasureString(s, font).Width;
            float i = start + size;
            int i1 = 0;

            while (i > start)
            {
                int h = 0;
                if (i1 == 0)
                    h = 0;
                else if (i1 % w10 == 0)
                    h = 6;
                else if (i1 % w5 == 0)
                    h = 4;
                else
                    h = 2;

                if (h == 2 && dx * w10 < this.LogicalToDevice(41))
                    h = 0;
                if (h == 4 && dx * w10 < this.LogicalToDevice(21))
                    h = 0;

                int w = 0;
                if (h == 6)
                {
                    if (maxw > dx * w10 * 1.5f)
                        w = w10 * 4;
                    else if (maxw > dx * w10 * 0.7f)
                        w = w10 * 2;
                    else
                        w = w10;
                }

                if (w != 0 && i1 % w == 0)
                {
                    if (ReportWorkspace.Grid.GridUnits == PageUnits.Millimeters)
                        s = (i1).ToString();
                    else if (ReportWorkspace.Grid.GridUnits == PageUnits.HundrethsOfInch)
                        s = (i1 * 10).ToString();
                    else
                        s = (i1 / 10).ToString();
                    SizeF sz = g.MeasureString(s, font);
                    g.DrawString(s, font, brush, new PointF(Offset + i - sz.Width / 2 + 1, this.LogicalToDevice(7)));
                }
                else if (h != 0)
                {
                    g.DrawLine(pen,
                        Offset + i, this.LogicalToDevice(6 + (13 - h) / 2),
                        Offset + i, this.LogicalToDevice(6 + (13 - h) / 2 + h - 1));
                }
                i -= dx;
                i1++;
            }
        }

        private void DrawGuides(Graphics g)
        {
            FloatCollection guides = Workspace.Page.Guides;
            if (guides == null)
                return;

            if (guides.Count > activeGuide && activeGuide != -1)
            {
                toolTip.Show(Converter.ToString((Workspace.Page.LeftMargin * Units.Millimeters + guides[activeGuide]) / ReportWorkspace.Grid.SnapSize * 10 / 4, typeof(PaperConverter))
                        + " | " +
                        Converter.ToString((Workspace.Page.PaperWidth * Units.Millimeters - Workspace.Page.LeftMargin * Units.Millimeters - guides[activeGuide]) / ReportWorkspace.Grid.SnapSize * 10 / 4, typeof(PaperConverter)),
                        this, (int)Math.Round(Offset + guides[activeGuide] * Designer.ZoomDpi - 4), 32
                    );
            }
            if (activeGuide == -1)
                toolTip.Hide(this);

            for (int i = 0; i < guides.Count; i++)
            {
                Image b = this.GetImage(i == activeGuide ? 174 : 74);
                g.DrawImage(b, (int)Math.Round(Offset + guides[i] * Designer.ZoomDpi - 4), 16);
            }
        }

        private void MoveGuide(float kx)
        {
            Workspace.Guides.MoveVGuide(activeGuide, kx);
            float f = Workspace.Page.Guides[activeGuide];
            f += kx;
            Workspace.Page.Guides[activeGuide] = Converter.DecreasePrecision(f, 2);
            Workspace.Refresh();
            Refresh();
        }

        private void FixGuide(bool remove)
        {
            float f = Workspace.Page.Guides[activeGuide];
            if (remove || f < 0 || f > Workspace.Width / Designer.ZoomDpi)
                Workspace.Page.Guides.RemoveAt(activeGuide);
            activeGuide = -1;
            Refresh();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (Workspace.Locked)
                return;
            Graphics g = e.Graphics;
            g.SetClip(new RectangleF(Height, 0, Width - Height, Height));
            g.FillRectangle(SystemBrushes.Window, new RectangleF(
                Offset, this.LogicalToDevice(5),
                Workspace.Width, Height - this.LogicalToDevice(10)));
            if (Config.RightToLeft)
            {
                DrawRulerRtl(g, 0, Workspace.Width);
            }
            else
            {
                DrawRuler(g, 0, Workspace.Width);
            }
            DrawGuides(g);
        }

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

            float scale = Designer.ZoomDpi;
            lastMousePoint = new PointF(e.X / scale, e.Y / scale);
            mouseDown = true;
            mouseMoved = false;
            if (activeGuide != -1)
                Workspace.Guides.BeforeMoveVGuide(activeGuide);
            saveAutoGuides = ReportWorkspace.AutoGuides;
            ReportWorkspace.AutoGuides = false;
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (Workspace.Locked)
                return;

            float scale = Designer.ZoomDpi;
            if (e.Button == MouseButtons.None)
            {
                // find guide
                FloatCollection guides = Workspace.Page.Guides;
                if (guides == null)
                    return;
                float x = (e.X - Offset) / scale;
                activeGuide = -1;
                for (int i = 0; i < guides.Count; i++)
                {
                    if (x > guides[i] - 5 && x < guides[i] + 5)
                    {
                        activeGuide = i;
                        break;
                    }
                }
                Refresh();
            }
            else if (e.Button == MouseButtons.Left)
            {
                if (activeGuide == -1)
                    return;
                float kx = e.X / scale - lastMousePoint.X;
                float ky = e.Y / scale - lastMousePoint.Y;
                if (!CheckGridStep(ref kx, ref ky))
                    return;
                mouseMoved = true;
                MoveGuide(kx);
                lastMousePoint.X += kx;
                lastMousePoint.Y += ky;
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            if (Workspace.Locked || !mouseDown)
                return;

            ReportWorkspace.AutoGuides = saveAutoGuides;
            mouseDown = false;
            if (mouseMoved)
            {
                FixGuide(e.Y < -5 || e.Y > Height + 5);
                Workspace.Designer.SetModified(null, "MoveGuide");
            }
            else
            {
                // create new guide
                float x = (e.X - Offset) / Designer.ZoomDpi;
                if (x < Workspace.Width && e.Y > 5 && e.Y < 20)
                {
                    if (ReportWorkspace.SnapToGrid)
                        x = (int)(x / ReportWorkspace.Grid.SnapSize) * ReportWorkspace.Grid.SnapSize;
                    if (Workspace.Page.Guides == null)
                        Workspace.Page.Guides = new FloatCollection();
                    Workspace.Page.Guides.Add(x);
                    Workspace.Designer.SetModified(null, "AddGuide");
                }
            }
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            if (Workspace.Locked)
                return;

            if (!mouseDown && activeGuide != -1)
            {
                activeGuide = -1;
                Refresh();
            }
        }

        public HorzRuler(ReportPageDesigner pd) : base(pd)
        {
            activeGuide = -1;
        }
    }

#if !DEBUG
    [DesignTimeVisible(false)]
#endif
    internal class VertRuler : RulerBase
    {
        private PointF lastMousePoint;
        private bool mouseDown;
        private bool mouseMoved;
        private int activeGuide;
        private BandBase activeBand;
        private bool resizing;
        private bool saveAutoGuides;

        private void DrawRuler(Graphics g, float start, float size)
        {
            Font font = new Font("Tahoma", 6f * this.FontDpiMultiplier());
            Brush brush = SystemBrushes.WindowText;
            Pen pen = SystemPens.WindowText;
            int w5 = 5;
            int w10 = 10;
            float dx = (ReportWorkspace.Grid.GridUnits == PageUnits.Millimeters || ReportWorkspace.Grid.GridUnits == PageUnits.Centimeters ?
                Units.Millimeters : Units.TenthsOfInch) * Designer.ZoomDpi;
            string s = (((int)Math.Round(size / dx)) / 10).ToString();
            float maxw = g.MeasureString(s, font).Width;
            float i = start;
            int i1 = 0;

            while (i < start + size)
            {
                int h = 0;
                if (i1 == 0)
                    h = 0;
                else if (i1 % w10 == 0)
                    h = 6;
                else if (i1 % w5 == 0)
                    h = 4;
                else
                    h = 2;

                if (h == 2 && dx * w10 < this.LogicalToDevice(41))
                    h = 0;
                if (h == 4 && dx * w10 < this.LogicalToDevice(21))
                    h = 0;

                int w = 0;
                if (h == 6)
                {
                    if (maxw > dx * w10 * 1.5f)
                        w = w10 * 4;
                    else if (maxw > dx * w10 * 0.7f)
                        w = w10 * 2;
                    else
                        w = w10;
                }

                if (w != 0 && i1 % w == 0)
                {
                    if (ReportWorkspace.Grid.GridUnits == PageUnits.Millimeters)
                        s = (i1).ToString();
                    else if (ReportWorkspace.Grid.GridUnits == PageUnits.HundrethsOfInch)
                        s = (i1 * 10).ToString();
                    else
                        s = (i1 / 10).ToString();
                    SizeF sz = g.MeasureString(s, font);
                    PointF p = new PointF(Width - sz.Height - this.LogicalToDevice(7), Offset + i + sz.Width / 2);
                    GraphicsState state = g.Save();
                    g.TranslateTransform(p.X + sz.Height / 2, p.Y + sz.Width / 2);
                    g.RotateTransform(-90);
                    p.X /= 2;
                    p.Y = -sz.Height / 2;
                    g.DrawString(s, font, brush, p);
                    g.Restore(state);
                }
                else if (h != 0)
                {
                    g.DrawLine(pen,
                        this.LogicalToDevice(6 + (13 - h) / 2), Offset + i,
                        this.LogicalToDevice(6 + (13 - h) / 2 + h - 1), Offset + i);
                }
                i += dx;
                i1++;
            }
        }

        private void DrawGuides(Graphics g, BandBase band)
        {
            FloatCollection guides = band.Guides;
            if (guides == null)
                return;

            if (band == activeBand && guides.Count > activeGuide && activeGuide != -1)
            {
                toolTip.Show(Converter.ToString((Workspace.Page.TopMargin * Units.Millimeters + (band.Top + guides[activeGuide])) / ReportWorkspace.Grid.SnapSize * 10 / 4, typeof(PaperConverter))
                    + " | "
                    + Converter.ToString((Workspace.Page.PaperHeight * Units.Millimeters - Workspace.Page.TopMargin * Units.Millimeters - (band.Top + guides[activeGuide])) / ReportWorkspace.Grid.SnapSize * 10 / 4, typeof(PaperConverter)),
                    this, 32, (int)Math.Round(Offset + (band.Top + guides[activeGuide]) * Designer.ZoomDpi + 10)
                );
            }
            if (activeGuide == -1)
                toolTip.Hide(this);

            for (int i = 0; i < guides.Count; i++)
            {
                Image b = this.GetImage(band == activeBand && i == activeGuide ? 173 : 73);
                g.DrawImage(b, 16, (int)Math.Round(Offset + (band.Top + guides[i]) * Designer.ZoomDpi - 4));
            }
        }

        private BandCollection GetBands()
        {
            BandCollection bands = new BandCollection();
            ObjectCollection objects = PageDesigner.Page.AllObjects;

            foreach (Base c in objects)
            {
                if (c is BandBase)
                    bands.Add(c as BandBase);
            }
            return bands;
        }

        private BandBase BandAt(float y)
        {
            BandCollection bands = GetBands();
            foreach (BandBase b in bands)
            {
                if (y >= b.Top && y <= b.Bottom + (ReportWorkspace.ClassicView ? BandBase.HeaderSize : 4))
                    return b;
            }
            return null;
        }

        private void MoveGuide(float ky)
        {
            Workspace.Guides.MoveHGuide(activeBand, activeGuide, ky);
            float f = activeBand.Guides[activeGuide];
            f += ky;
            activeBand.Guides[activeGuide] = Converter.DecreasePrecision(f, 2);
            Workspace.Refresh();
            Refresh();
        }

        private void ResizeBand(float ky)
        {
            activeBand.Height += ky;
            activeBand.FixHeight();
            Workspace.UpdateBands();
        }

        private void FixGuide(bool remove)
        {
            float f = activeBand.Guides[activeGuide];
            if (remove || f < 0 || f > activeBand.Height)
                activeBand.Guides.RemoveAt(activeGuide);
            activeGuide = -1;
            Refresh();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (Workspace.Locked)
                return;
            Graphics g = e.Graphics;

            // highlight bands list
            Hashtable bandsToHighlight = new Hashtable();
            if (ReportWorkspace.EnableBacklight)
            {
                foreach (Base obj in Designer.SelectedObjects)
                {
                    BandBase band = null;
                    if (obj is BandBase)
                        band = obj as BandBase;
                    else if (obj is ReportComponentBase)
                        band = (obj as ReportComponentBase).Band;
                    if (band != null)
                        bandsToHighlight[band] = 1;
                }
            }

            float scale = Designer.ZoomDpi;
            BandCollection bands = GetBands();
            foreach (BandBase b in bands)
            {
                Brush brush = bandsToHighlight.ContainsKey(b) ? Brushes.Gainsboro : SystemBrushes.Window;

                g.FillRectangle(brush, new RectangleF(
                    this.LogicalToDevice(5), Offset + b.Top * scale,
                    Width - this.LogicalToDevice(10), b.Height * scale + 1));
                DrawRuler(g, b.Top * scale, b.Height * scale);
                DrawGuides(g, b);

                if (ReportWorkspace.ClassicView)
                {
                    RectangleF fillRect = new RectangleF(
                        this.LogicalToDevice(5), Offset + (b.Top - (BandBase.HeaderSize - 1)) * scale,
                        Width - this.LogicalToDevice(10), (BandBase.HeaderSize - 1) * scale);
                    if (b.Top == BandBase.HeaderSize)
                    {
                        fillRect.Y = 0;
                        fillRect.Height += scale;
                    }
                    b.DrawBandHeader(g, fillRect, true);

                    if (b.Top > BandBase.HeaderSize)
                    {
                        // draw splitter lines
                        float lineY = fillRect.Top + fillRect.Height / 2 - this.LogicalToDevice(2);
                        for (int i = 0; i < 6; i += 2)
                        {
                            g.DrawLine(SystemPens.ControlDarkDark,
                                this.LogicalToDevice(9), lineY + this.LogicalToDevice(i),
                                Width - this.LogicalToDevice(10), lineY + this.LogicalToDevice(i));
                        }
                    }
                }
            }
        }

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

            float scale = Designer.ZoomDpi;
            lastMousePoint = new PointF(e.X / scale, e.Y / scale);
            mouseDown = true;
            mouseMoved = false;
            if (activeBand != null && activeGuide != -1)
                Workspace.Guides.BeforeMoveHGuide(activeBand, activeGuide);
            saveAutoGuides = ReportWorkspace.AutoGuides;
            ReportWorkspace.AutoGuides = false;
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (Workspace.Locked)
                return;

            float scale = Designer.ZoomDpi;
            if (e.Button == MouseButtons.None)
            {
                // find band
                float y = (e.Y - Offset) / scale;
                Cursor = Cursors.Default;
                resizing = false;
                activeGuide = -1;
                activeBand = BandAt(y);
                if (activeBand != null)
                {
                    // check band resize
                    if (y > activeBand.Bottom - 1 &&
                      y < activeBand.Bottom + (ReportWorkspace.ClassicView ? BandBase.HeaderSize : 4))
                    {
                        resizing = true;
                        Cursor = Cursors.HSplit;
                    }
                    else
                    {
                        // check guides
                        FloatCollection guides = activeBand.Guides;
                        if (guides != null)
                        {
                            for (int i = 0; i < guides.Count; i++)
                            {
                                if (y > activeBand.Top + guides[i] - 5 &&
                                  y < activeBand.Top + guides[i] + 5)
                                {
                                    activeGuide = i;
                                    break;
                                }
                            }
                        }
                    }
                }
                Refresh();
            }
            else if (e.Button == MouseButtons.Left)
            {
                float kx = e.X / scale - lastMousePoint.X;
                float ky = e.Y / scale - lastMousePoint.Y;
                if (!CheckGridStep(ref kx, ref ky))
                    return;

                if (activeBand != null)
                {
                    if (resizing)
                    {
                        mouseMoved = true;
                        ResizeBand(ky);
                    }
                    else if (activeGuide != -1)
                    {
                        mouseMoved = true;
                        MoveGuide(ky);
                    }
                }
                lastMousePoint.X += kx;
                lastMousePoint.Y += ky;
            }
        }

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

            ReportWorkspace.AutoGuides = saveAutoGuides;
            mouseDown = false;
            if (mouseMoved)
            {
                if (resizing)
                    activeBand.FixHeight();
                else
                    FixGuide(e.X < -this.LogicalToDevice(5) || e.X > Width + this.LogicalToDevice(5));
                Workspace.Designer.SetModified(null, resizing ? "ResizeBand" : "MoveGuide");
            }
            else
            {
                // create new guide
                if (e.X > this.LogicalToDevice(5) && e.X < this.LogicalToDevice(20))
                {
                    float y = (e.Y - Offset) / Designer.ZoomDpi;
                    BandBase band = BandAt(y);
                    if (band != null)
                    {
                        y = y - band.Top;
                        if (ReportWorkspace.SnapToGrid)
                            y = (int)(y / ReportWorkspace.Grid.SnapSize) * ReportWorkspace.Grid.SnapSize;
                        if (band.Guides == null)
                            band.Guides = new FloatCollection();
                        band.Guides.Add(y);
                        Workspace.Designer.SetModified(null, "AddGuide");
                    }
                }
            }
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            if (Workspace.Locked)
                return;

            if (!mouseDown && activeGuide != -1)
            {
                activeGuide = -1;
                Refresh();
            }
        }

        public VertRuler(ReportPageDesigner pd) : base(pd)
        {
            activeGuide = -1;
        }
    }

#if !MONO
    /// <summary>
    /// Represent ruler with guides for forms of editors
    /// </summary>
    public class RulerWithGuides : EditRuler
    {
        private bool mouseDown = false;
        private Pen pen;
        private List<RulerElement> rulerElements;
        private RulerElement activElement;
        private int countTabPos;
        private bool leftIndentIsActive = false;
        private bool rightIndentIsActive = false;

    #region Properties
        /// <summary>
        /// Get or set left indent position
        /// </summary>
        public int LeftIndent
        {
            get
            {
                return leftIndent.Indent;
            }
            set
            {
                leftIndent.Indent = value;
                Invalidate();
            }
        }

        /// <summary>
        /// Get or set right indent position
        /// </summary>
        public int RightIndent
        {
            get
            {
                return rightIndent.Indent;
            }
            set
            {
                rightIndent.Indent = value;
                Invalidate();
            }
        }

        /// <summary>
        /// Gets or sets tab positiions
        /// </summary>
        public int[] TabPositions
        {
            get
            {
                List<int> result = new List<int>();
                foreach (var elem in rulerElements)
                {
                    if(elem is TabPosition)
                    {
                        result.Add(elem.Bounds.X + 4);
                    }
                }
                result.Sort();

                return result.ToArray();
            }

            set
            {
                for (int i = 0; i < rulerElements.Count; i++)
                {
                    if (rulerElements[i] is TabPosition)
                    {
                        rulerElements.Remove(rulerElements[i]);
                        i--;
                    }
                }

                for (int i = 0; i < value.Length; i++)
                {
                    rulerElements.Add(new TabPosition(value[i]));
                }
                Invalidate();
            }
        }
    #endregion

    #region Protected method
        /// <inheritdoc/>
        protected override void OnDoubleClick(EventArgs e)
        {
            base.OnDoubleClick(e);
            MouseEventArgs me = e as MouseEventArgs;

            if (me.X > RulerStart && me.X < RulerWidth && me.Button == MouseButtons.Left && !TabPosExist(me.X) &&
                countTabPos + 1 <= 32 && !leftIndentIsActive && !rightIndentIsActive && activElement == null)
            {
                rulerElements.Add(new TabPosition(me.X));
                countTabPos++;
                OnChange(this);
            }
        }

        /// <inheritdoc/>
        protected override Rectangle GetIndentHitRect(RulerIndent Indent)
        {
            Rectangle result = Rectangle.Empty;
            if (Vertical)
            {
                //if (Indent.Orientation == IndentOrientation.Near)
                //    result = new Rectangle(cRulerTop, RulerStart + Indent.Indent - EditConsts.DefaultRulerHitWidth, cRulerHeight, EditConsts.DefaultRulerHitWidth * 2);
                //else
                //    result = new Rectangle(cRulerTop, PageStart + PageWidth - EditConsts.DefaultRulerHitWidth, cRulerHeight, EditConsts.DefaultRulerHitWidth * 2);
            }
            else
            {
                if (Indent.Orientation == IndentOrientation.Near)
                    result = new Rectangle(RulerStart + Indent.Indent - EditConsts.DefaultRulerHitWidth, cRulerTop, EditConsts.DefaultRulerHitWidth * 2, cRulerHeight);
                else
                    result = new Rectangle(RulerStart + RulerWidth - EditConsts.DefaultRulerHitWidth - Indent.Indent, cRulerTop, EditConsts.DefaultRulerHitWidth * 2, cRulerHeight);
            }
            return result;
        }

        /// <inheritdoc/>
        protected override Rectangle GetIndentRect(RulerIndent indent)
        {
            Rectangle result = Rectangle.Empty;
            if (Vertical)
            {
                //if (indent.Orientation == IndentOrientation.Near)
                //    result = new Rectangle(cRulerTop, RulerStart, cRulerHeight, indent.Indent);
                //else
                //    result = new Rectangle(cRulerTop, PageWidth + PageStart, cRulerHeight, indent.Indent);
            }
            else
            {
                if (indent.Orientation == IndentOrientation.Near)
                    result = new Rectangle(RulerStart, cRulerTop, indent.Indent, cRulerHeight);
                else
                    result = new Rectangle(RulerWidth + RulerStart - indent.Indent, cRulerTop, indent.Indent, cRulerHeight);
            }
            return result;
        }

        /// <inheritdoc/>
        protected override void OnPaint(PaintEventArgs pe)
        {
            base.OnPaint(pe);

            foreach(var elem in rulerElements)
            {
                elem.Draw(pe.Graphics);
            }
        }

        /// <inheritdoc/>
        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            mouseDown = true;

            if (RulerWidth - RightIndent + RulerStart + 2 >= e.X && RulerWidth - RightIndent + RulerStart - 4 <= e.X && e.Button != MouseButtons.Right)
                rightIndentIsActive = true;
            else if (LeftIndent + RulerStart - 4 <= e.X && LeftIndent + RulerStart + 2 >= e.X && e.Button != MouseButtons.Right)
                leftIndentIsActive = true;
            else
                foreach (var elem in rulerElements)
                {
                    if (elem.Bounds.Contains(e.Location))
                    {
                        if (e.Button == MouseButtons.Right && elem is TabPosition)
                        {
                            rulerElements.Remove(elem);
                            countTabPos--;
                        }
                        else 
                        {
                            activElement = elem;
                            activElement.IsActive = true;

                            if (e.X > RulerWidth + RulerStart - RightIndent)
                                activElement.Move(new Point(RulerWidth + RulerStart - RightIndent, 0));
                            else if(e.X < RulerStart + LeftIndent)
                                activElement.Move(new Point(RulerStart + LeftIndent, 0));
                        }

                        break;
                    }
                }

            OnChange(this);
        }

        /// <inheritdoc/>
        protected override void OnMouseMove(MouseEventArgs e)
        {
            int oldLeftIndent = LeftIndent;
            base.OnMouseMove(e);

            if (mouseDown && activElement != null && e.X < RulerWidth + RulerStart - RightIndent && e.X > RulerStart + LeftIndent)
            {
                activElement.Move(e.Location);
                OnChange(this);
            }
            else if(mouseDown && leftIndentIsActive) 
            {
                int dx = LeftIndent - oldLeftIndent;
                //if (dx != 0)
                    dx += 4;

                foreach (var elem in rulerElements)
                {
                    elem.Move(new Point(elem.Bounds.X + dx, 0));
                }
            }
        }

        /// <inheritdoc/>
        protected override void OnChange(object sender)
        {
            base.OnChange(sender);
            Invalidate();
        }

        /// <inheritdoc/>
        protected override void OnMouseCaptureChanged(EventArgs e)
        {
            base.OnMouseCaptureChanged(e);
            mouseDown = false;

            if (activElement != null)
                activElement.IsActive = false;

            activElement = null;
            leftIndentIsActive = false;
            rightIndentIsActive = false;
            OnChange(this);
        }

        /// <inheritdoc/>
        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            mouseDown = false;

            if(activElement != null)
                activElement.IsActive = false;

            activElement = null;
            leftIndentIsActive = false;
            rightIndentIsActive = false;

            OnChange(this);
        }

        /// <inheritdoc/>
        protected override void InitLayout()
        {
            base.InitLayout();

            pen = new Pen(Color.Black, 1);
            pen.DashStyle = DashStyle.Dash;
            pen.DashPattern = new float[] { 4, 4 };
            rulerElements = new List<RulerElement>();
        }

        /// <inheritdoc/>
        protected override void OnHandleDestroyed(EventArgs e)
        {
            pen.Dispose();
            base.OnHandleDestroyed(e);
        }
    #endregion

        private bool TabPosExist(int x)
        {
            foreach (var elem in rulerElements)
            {
                if (elem.Bounds.Contains(x, elem.Bounds.Y) && elem is TabPosition)
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Method for drawing guides on element of form.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void DrawGuides(object sender, PaintEventArgs e)
        {
            if (mouseDown)
            {
                if(activElement != null)
                    e.Graphics.DrawLine(pen, new Point(activElement.Bounds.X + 4, e.ClipRectangle.Top), new Point(activElement.Bounds.X + 4, e.ClipRectangle.Bottom));
                else if(leftIndentIsActive)
                    e.Graphics.DrawLine(pen, new Point(LeftIndent + RulerStart, e.ClipRectangle.Top), new Point(LeftIndent + RulerStart, e.ClipRectangle.Bottom));
                else if (rightIndentIsActive)
                    e.Graphics.DrawLine(pen, new Point(RulerWidth - RightIndent + RulerStart + 2, e.ClipRectangle.Top), new Point(RulerWidth - RightIndent + RulerStart + 2, e.ClipRectangle.Bottom));
            }
        }

        /// <summary>
        /// Base class for elements of RulerWithGuides
        /// </summary>
        public class RulerElement
        {
            private bool isActive;
            private Rectangle bounds;

            /// <summary>
            /// Get or set bounds of object
            /// </summary>
            public Rectangle Bounds
            {
                get { return bounds; }
                set { bounds = value; }
            }

            /// <summary>
            /// Get or set state of object
            /// </summary>
            public bool IsActive
            {
                get { return isActive; }
                set { isActive = value; }
            }

            /// <summary>
            /// Draw element on graphics
            /// </summary>
            /// <param name="g"></param>
            public virtual void Draw(Graphics g) { }

            /// <summary>
            /// Method for moving object by means of chenging bounds
            /// </summary>
            /// <param name="location"></param>
            public virtual void Move(Point location) { }

        }

        /// <summary>
        /// Element of RulerWithGuides presenting position of tabs
        /// </summary>
        public class TabPosition : RulerElement
        {
            /// <summary>
            /// Constructor of class TabPosition
            /// </summary>
            /// <param name="x"></param>
            public TabPosition(int x)
            {
                IsActive = false;
                Bounds = new Rectangle(x - 4, 16, 14, 14);
            }

            /// <inheritdoc/>
            public override void Draw(Graphics g)
            {
                base.Draw(g);

                if(IsActive)
                    g.DrawImage(Res.GetImage(174, 96), Bounds);
                else
                    g.DrawImage(Res.GetImage(74, 96), Bounds);
            }

            /// <inheritdoc/>
            public override void Move(Point location)
            {
                base.Move(location);
                Bounds = new Rectangle(location.X - 4, 16, 14, 14);
            }
        }
    }
#endif
}