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

namespace Quasar.Server.Controls.HexEditor
{
    /*
    * Derived and Adapted from Bernhard Elbl 
    * Be.HexEditor v1.6 (Last Update: Dec 27, 2013).
    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    * This code (and the rest that is avaliable in the
    * HexEditor-folder) has been derived from Bernhard
    * ElblBe's Be.HexEditor v1.6, modifications have 
    * been made to make it better fit the new application
    * area. 
    * First Modified by StingRaptor on Febuary 7, 2016
    */
    /* [Original License, ONLY for Current HexEditor Folder]
     * This license is applied only to the files in the current
     * HexEditor Folder
     
     * The MIT License

        Copyright (c) 2011 Bernhard Elbl

        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:

        The above copyright notice and this permission notice shall be included in
        all copies or substantial portions of the Software.

        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
        THE SOFTWARE.
    */
    /*
    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    * Unmodified Source:
    * http://sourceforge.net/projects/hexbox/?source=navbar
    */

    public class HexEditor : Control
    {
        #region Locks

        private object _caretLock = new object();

        private object _hexTableLock = new object();

        #endregion

        #region IKeyMouseEventHandlers

        private IKeyMouseEventHandler _handler;

        #endregion

        #region EditView

        private EditView _editView;

        #endregion

        #region Fields

        /// <summary>
        /// Contains all of the bytes that
        /// are to be displayed in the 
        /// HexEditor
        /// </summary>
        ByteCollection _hexTable;

        /// <summary>
        /// Contains the type of counter
        /// for the linecount
        /// </summary>
        string _lineCountCaps = "X";

        /// <summary>
        /// Contains the number of chars
        /// to be used in the line count
        /// </summary>
        int _nrCharsLineCount = 4;

        /// <summary>
        /// Contains the caret for the
        /// control
        /// </summary>
        Caret _caret;

        #region Boundarys

        /// <summary>
        /// Contains the bound for everything that
        /// is to be displayed in the control
        /// </summary>
        Rectangle _recContent;

        /// <summary>
        /// Contains the boundary for every 
        /// line count for the control
        /// </summary>
        Rectangle _recLineCount;

        #endregion

        #region String Format

        /// <summary>
        /// Contains the format of the line count
        /// string that is presented in this control
        /// </summary>
        StringFormat _stringFormat;

        #endregion

        #region Byte specific

        /// <summary>
        /// Contains the index of the first 
        /// visible byte
        /// </summary>
        int _firstByte;

        /// <summary>
        /// Contains the index of the last 
        /// visible byte
        /// </summary>
        int _lastByte;

        /// <summary>
        /// Contains the maximum bytes that
        /// can be visible horizontally
        /// </summary>
        int _maxBytesH;

        /// <summary>
        /// Contains the maximum bytes that
        /// can be visible vertically
        /// </summary>
        int _maxBytesV;

        /// <summary>
        /// Contains the maximum number of 
        /// bytes that can be visible at a 
        /// time.
        /// </summary>
        int _maxBytes;

        /// <summary>
        /// Contains the maximum number of 
        /// rows with bytes that are fully 
        /// visible.
        /// </summary>
        int _maxVisibleBytesV;

        #endregion

        #region Scrollbar

        /// <summary>
        /// Contains the vertical scroll
        /// bar for the control
        /// </summary>
        VScrollBar _vScrollBar;

        /// <summary>
        /// Contains the with of the scrollbar
        /// in pixels
        /// </summary>
        int _vScrollBarWidth = 20;

        /// <summary>
        /// Contains the current position of the 
        /// scrollbar
        /// </summary>
        int _vScrollPos;

        /// <summary>
        /// Contains the maximum value that
        /// the scrollbar can have
        /// </summary>
        int _vScrollMax;

        /// <summary>
        /// Contains the minimum value
        /// that the scrollbar may have
        /// </summary>
        int _vScrollMin;

        /// <summary>
        /// Contains the value for the 
        /// size of a  smallchange in the
        /// scrollbar
        /// </summary>
        int _vScrollSmall;

        /// <summary>
        /// Contains the value for the 
        /// size of a largechange in the
        /// scrollbar
        /// </summary>
        int _vScrollLarge;

        #endregion

        #endregion

        #region Properties

        #region enums

        public enum CaseStyle { LowerCase, UpperCase }

        #endregion

        #region Overriden

        //Override font to trigger update on font change
        public override Font Font
        {
            set
            {
                base.Font = value;

                UpdateRectanglePositioning();
                Invalidate();
            }
        }

        //Hides the property Text that is not used for the control
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public override string Text
        {
            get
            {
                return base.Text;
            }
            set
            {
                base.Text = value;
            }
        }

        #endregion

        #region Hidden

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public byte[] HexTable
        {
            get
            {
                lock (_hexTableLock)
                {
                    return _hexTable.ToArray();
                }
            }
            set
            {
                lock (_hexTableLock)
                {
                    if (value == _hexTable.ToArray())
                        return;

                    _hexTable = new ByteCollection(value);
                }
                UpdateRectanglePositioning();
                Invalidate();
            }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public SizeF CharSize
        {
            get { return _charSize; }
            private set
            {
                if (_charSize == value)
                    return;

                _charSize = value;

                if (CharSizeChanged != null)
                    CharSizeChanged(this, EventArgs.Empty);
            }
        }SizeF _charSize;

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int MaxBytesV
        {
            get { return _maxBytesV; }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int FirstVisibleByte
        {
            get { return _firstByte; }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int LastVisibleByte
        {
            get { return _lastByte; }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool VScrollBarHidden
        {
            get { return _isVScrollHidden; }
            set
            {
                if (_isVScrollHidden == value)
                    return;

                _isVScrollHidden = value;

                if (!_isVScrollHidden)
                    Controls.Add(_vScrollBar);
                else
                    Controls.Remove(_vScrollBar);

                UpdateRectanglePositioning();
                Invalidate();
            }
        }bool _isVScrollHidden = true;

        #endregion

        #region Visible

        /// <summary>
        /// Contains the number of bytes to
        /// display per line
        /// </summary>
        [DefaultValue(8), Category("Hex"), Description("Property that specifies the number of bytes to display per line.")]
        public int BytesPerLine
        {
            get { return _bytesPerLine; }
            set
            {
                if (_bytesPerLine == value)
                    return;

                _bytesPerLine = value;
                UpdateRectanglePositioning();
                Invalidate();
            }
        }int _bytesPerLine = 8;


        /// <summary>
        /// Contains the margin between each
        /// of the entitys
        /// </summary>
        [DefaultValue(10), Category("Hex"), Description("Property that specifies the margin between each of the entitys in the control.")]
        public int EntityMargin
        {
            get { return _entityMargin; }
            set
            {
                if (_entityMargin == value)
                    return;

                _entityMargin = value;
                UpdateRectanglePositioning();
                Invalidate();
            }
        }int _entityMargin = 10;

        /// <summary>
        /// Contains the type of border 
        /// that is used for the control
        /// </summary>
        [DefaultValue(BorderStyle.Fixed3D), Category("Appearance"), Description("Indicates where the control should have a border.")]
        public BorderStyle BorderStyle
        {
            get { return _borderStyle; }
            set
            {
                if (_borderStyle == value)
                    return;

                if (value != BorderStyle.FixedSingle)
                    _borderColor = Color.Empty;

                _borderStyle = value;
                UpdateRectanglePositioning();
                Invalidate();
            }
        }BorderStyle _borderStyle = BorderStyle.Fixed3D;

        /// <summary>
        /// Contains the color for the border 
        /// that is used for the control 
        /// (Only used when BorderStyle is FixedSingle)
        /// </summary>
        [DefaultValue(typeof(Color), "Empty"), Category("Appearance"), Description("Indicates the color to be used when displaying a FixedSingle border.")]
        public Color BorderColor
        {
            get { return _borderColor; }
            set
            {
                if (BorderStyle != BorderStyle.FixedSingle || _borderColor == value)
                    return;

                _borderColor = value;
                Invalidate();
            }
        }Color _borderColor = Color.Empty;

        /// <summary>
        /// Contains the color of the selected
        /// background area in the control
        /// </summary>
        [DefaultValue(typeof(Color), "Blue"), Category("Hex"), Description("Property for the background color of the selected text areas.")]
        public Color SelectionBackColor
        {
            get { return _selectionBackColor; }
            set
            {
                if (_selectionBackColor == value)
                    return;

                _selectionBackColor = value;
            }
        }Color _selectionBackColor = Color.Blue;

        /// <summary>
        /// Contains the color of the selected
        /// foreground area in the control
        /// </summary>
        [DefaultValue(typeof(Color), "White"), Category("Hex"), Description("Property for the foreground color of the selected text areas.")]
        public Color SelectionForeColor
        {
            get { return _selectionForeColor; }
            set
            {
                if (_selectionForeColor == value)
                    return;

                _selectionForeColor = value;
            }
        }Color _selectionForeColor = Color.White;

        /// <summary>
        /// Contains the case type for the
        /// line counter
        /// </summary>
        [DefaultValue(CaseStyle.UpperCase), Category("Hex"), Description("Property for the case type to use on the line counter.")]
        public CaseStyle LineCountCaseStyle
        {
            get { return _lineCountCaseStyle; }
            set
            {
                if (_lineCountCaseStyle == value)
                    return;

                _lineCountCaseStyle = value;

                if (_lineCountCaseStyle == CaseStyle.LowerCase)
                    _lineCountCaps = "x";
                else
                    _lineCountCaps = "X";

                Invalidate();
            }
        }CaseStyle _lineCountCaseStyle = CaseStyle.UpperCase;

        /// <summary>
        /// Contains the case type for the
        /// hex view.
        /// </summary>
        [DefaultValue(CaseStyle.UpperCase), Category("Hex"), Description("Property for the case type to use for the hexadecimal values view.")]
        public CaseStyle HexViewCaseStyle
        {
            get { return _hexViewCaseStyle; }
            set
            {
                if (_hexViewCaseStyle == value)
                    return;

                _hexViewCaseStyle = value;

                if (_hexViewCaseStyle == CaseStyle.LowerCase)
                    _editView.SetLowerCase();
                else
                    _editView.SetUpperCase();

                Invalidate();
            }
        }CaseStyle _hexViewCaseStyle = CaseStyle.UpperCase;

        /// <summary>
        /// Property that contains if the
        /// vertical scrollbar should be 
        /// visible or not
        /// </summary>
        [DefaultValue(false), Category("Hex"), Description("Property for the visibility of the vertical scrollbar.")]
        public bool VScrollBarVisisble
        {
            get { return _isVScrollVisible; }
            set
            {
                if (_isVScrollVisible == value)
                    return;

                _isVScrollVisible = value;

                UpdateRectanglePositioning();
                Invalidate();
            }
        }bool _isVScrollVisible = false;

        #endregion

        #endregion

        #region EventHandlers

        [Description("Event that is triggered whenever the hextable has been changed.")]
        public event EventHandler HexTableChanged;

        [Description("Event that is triggered whenever the SelectionStart value is changed.")]
        public event EventHandler SelectionStartChanged;

        [Description("Event that is triggered whenever the SelectionLength value is changed.")]
        public event EventHandler SelectionLengthChanged;

        [Description("Event that is triggered whenever the size of the char is changed.")]
        public event EventHandler CharSizeChanged;

        #endregion

        #region Events

        protected void OnVScrollBarScroll(object sender, ScrollEventArgs e)
        {
            switch (e.Type)
            {
                case ScrollEventType.SmallIncrement:
                    ScrollLineDown(1);
                    break;
                case ScrollEventType.SmallDecrement:
                    ScrollLineUp(1);
                    break;
                case ScrollEventType.LargeIncrement:
                    ScrollLineDown(_vScrollLarge);
                    break;
                case ScrollEventType.LargeDecrement:
                    ScrollLineUp(_vScrollLarge);
                    break;
                case ScrollEventType.ThumbTrack:
                    ScrollThumbTrack(e.NewValue - e.OldValue);
                    break;
            }
            Invalidate();
        }

        protected void CaretSelectionStartChanged(object sender, EventArgs e)
        {
            if (SelectionStartChanged != null)
                SelectionStartChanged(this, e);
        }

        protected void CaretSelectionLengthChanged(object sender, EventArgs e)
        {
            if (SelectionLengthChanged != null)
                SelectionLengthChanged(this, e);
        }

        #region Overriden

        protected override void OnMarginChanged(EventArgs e)
        {
            base.OnMarginChanged(e);
            UpdateRectanglePositioning();
            Invalidate();
        }

        protected override void OnGotFocus(EventArgs e)
        {
            if (_handler != null)
                _handler.OnGotFocus(e);

            UpdateRectanglePositioning();
            Invalidate();
            base.OnGotFocus(e);
        }

        protected override void OnLostFocus(EventArgs e)
        {
            _dragging = false;
            DestroyCaret();
            base.OnLostFocus(e);
        }

        #endregion

        #endregion

        #region KeyEvents

        protected override bool IsInputKey(Keys keyData)
        {
            switch (keyData)
            {
                case Keys.Right:
                case Keys.Left:
                case Keys.Up:
                case Keys.Down:
                case Keys.Shift | Keys.Right:
                case Keys.Shift | Keys.Left:
                case Keys.Shift | Keys.Up:
                case Keys.Shift | Keys.Down:
                    return true;
            }
            return base.IsInputKey(keyData);
        }

        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            if (_handler != null)
                _handler.OnKeyPress(e);

            base.OnKeyPress(e);
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (e.KeyCode == Keys.PageDown)
            {
                if (!_isVScrollHidden)
                {
                    ScrollLineDown(_vScrollLarge);
                    Invalidate();
                }
            }
            else if (e.KeyCode == Keys.PageUp)
            {
                if (!_isVScrollHidden)
                {
                    ScrollLineUp(_vScrollLarge);
                    Invalidate();
                }
            }
            else
            {
                if (_handler != null)
                    _handler.OnKeyDown(e);
            }

            base.OnKeyDown(e);
        }

        protected override void OnKeyUp(KeyEventArgs e)
        {
            if (_handler != null)
                _handler.OnKeyUp(e);

            base.OnKeyUp(e);
        }

        #endregion

        #region MouseEvents

        //Indicates if the mouse is currently being dragged or not
        private bool _dragging;

        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (this.Focused)
            {
                if (_handler != null)
                    _handler.OnMouseDown(e);

                if (e.Button == MouseButtons.Left)
                {
                    _dragging = true;
                    Invalidate();
                }
            }
            else
            {
                this.Focus();
            }

            base.OnMouseDown(e);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (this.Focused)
            {
                if (_dragging)
                {
                    if (_handler != null)
                        _handler.OnMouseDragged(e);

                    Invalidate();
                }
            }

            base.OnMouseMove(e);
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            _dragging = false;

            if (this.Focused)
            {
                if (_handler != null)
                    _handler.OnMouseUp(e);
            }

            base.OnMouseUp(e);
        }

        protected override void OnMouseDoubleClick(MouseEventArgs e)
        {
            if (this.Focused)
            {
                if (_handler != null)
                    _handler.OnMouseDoubleClick(e);
            }

            base.OnMouseDoubleClick(e);
        }

        #endregion

        #region Caret

        #region Properties

        public int CaretPosX
        {
            get
            {
                lock (_caretLock)
                {
                    return _caret.Location.X;
                }
            }
        }

        public int CaretPosY
        {
            get
            {
                lock (_caretLock)
                {
                    return _caret.Location.Y;
                }
            }
        }

        public int SelectionStart
        {
            get
            {
                lock (_caretLock)
                {
                    return _caret.SelectionStart;
                }
            }
        }

        public int SelectionLength
        {
            get
            {
                lock (_caretLock)
                {
                    return _caret.SelectionLength;
                }
            }
        }

        public int CaretIndex
        {
            get
            {
                lock (_caretLock)
                {
                    return _caret.CurrentIndex;
                }
            }
        }

        public bool CaretFocused
        {
            get
            {
                lock (_caretLock)
                {
                    return _caret.Focused;
                }
            }
        }

        #endregion

        #region Methods

        public void SetCaretStart(int index, Point location)
        {
            location = ScrollToCaret(index, location);

            lock (_caretLock)
            {
                _caret.SetStartIndex(index);
                _caret.SetCaretLocation(location);
            }

            Invalidate();
        }

        public void SetCaretEnd(int index, Point location)
        {
            location = ScrollToCaret(index, location);

            lock (_caretLock)
            {
                _caret.SetEndIndex(index);
                _caret.SetCaretLocation(location);
            }

            Invalidate();
        }

        public bool IsSelected(int byteIndex)
        {
            lock (_caretLock)
            {
                return _caret.IsSelected(byteIndex);
            }
        }

        public void DestroyCaret()
        {
            lock (_caretLock)
            {
                _caret.Destroy();
            }
        }

        #endregion

        #endregion

        #region HexTable

        #region Properties

        public int HexTableLength
        {
            get
            {
                lock (_hexTableLock)
                {
                    return _hexTable.Length;
                }
            }
        }

        #endregion

        #region Methods

        public void RemoveSelectedBytes()
        {
            int index = SelectionStart;
            int length = SelectionLength;
            if (length > 0)
            {
                lock (_hexTableLock)
                {
                    _hexTable.RemoveRange(index, length);
                }

                UpdateRectanglePositioning();
                Invalidate();

                if (HexTableChanged != null)
                    HexTableChanged(this, EventArgs.Empty);
            }
        }

        public void RemoveByteAt(int index)
        {
            lock (_hexTableLock)
            {
                _hexTable.RemoveAt(index);
            }

            UpdateRectanglePositioning();
            Invalidate();

            if (HexTableChanged != null)
                HexTableChanged(this, EventArgs.Empty);
        }

        public void AppendByte(byte item)
        {
            lock (_hexTableLock)
            {
                _hexTable.Add(item);
            }

            UpdateRectanglePositioning();
            Invalidate();

            if (HexTableChanged != null)
                HexTableChanged(this, EventArgs.Empty);
        }

        public void InsertByte(int index, byte item)
        {
            lock (_hexTableLock)
            {
                _hexTable.Insert(index, item);
            }

            UpdateRectanglePositioning();
            Invalidate();

            if (HexTableChanged != null)
                HexTableChanged(this, EventArgs.Empty);
        }

        public char GetByteAsChar(int index)
        {
            lock (_hexTableLock)
            {
                return _hexTable.GetCharAt(index);
            }
        }

        public byte GetByte(int index)
        {
            lock (_hexTableLock)
            {
                return _hexTable.GetAt(index);
            }
        }

        public void SetByte(int index, byte item)
        {
            lock (_hexTableLock)
            {
                _hexTable.SetAt(index, item);
            }

            Invalidate();

            if (HexTableChanged != null)
                HexTableChanged(this, EventArgs.Empty);
        }

        #endregion

        #endregion

        #region Scrollbar

        public void ScrollLineUp(int lines)
        {
            if (_firstByte > 0)
            {
                lines = lines > _vScrollPos ? _vScrollPos : lines;
                //Scroll up
                _vScrollPos -= _vScrollSmall * lines;
                //Update the visible bytes
                UpdateVisibleByteIndex();
                UpdateScrollValues();
                //Update the Caret
                if (CaretFocused)
                {
                    Point caretLocation = new Point(CaretPosX, CaretPosY);
                    caretLocation.Y += _recLineCount.Height * lines;

                    lock (_caretLock)
                    {
                        _caret.SetCaretLocation(caretLocation);
                    }
                }
            }
        }

        public void ScrollLineDown(int lines)
        {
            if (_vScrollPos <= _vScrollMax - _vScrollLarge)
            {
                lines = (lines + _vScrollPos) > (_vScrollMax - _vScrollLarge) ? (_vScrollMax - _vScrollLarge) - _vScrollPos + 1 : lines;
                //Scroll up
                _vScrollPos += _vScrollSmall * lines;
                //Update the visible bytes
                UpdateVisibleByteIndex();
                UpdateScrollValues();
                //Update the Caret
                if (CaretFocused)
                {
                    Point caretLocation = new Point(CaretPosX, CaretPosY);
                    caretLocation.Y -= _recLineCount.Height * lines;

                    lock (_caretLock)
                    {
                        _caret.SetCaretLocation(caretLocation);
                        if (caretLocation.Y < _recContent.Y)
                            _caret.Hide(this.Handle);
                    }
                }
            }
        }

        public void ScrollThumbTrack(int lines)
        {
            if (lines == 0)
                return;

            if (lines < 0)
            {
                ScrollLineUp((-1) * lines);
            }
            else
            {
                ScrollLineDown(lines);
            }
        }

        /// <summary>
        /// Performs a scroll to the given caretIndex
        /// </summary>
        /// <param name="caretIndex">The caret index to scroll to</param>
        public Point ScrollToCaret(int caretIndex, Point position)
        {
            if (position.Y < 0)
            {
                //Need to scroll up until caret is visible
                _vScrollPos -= Math.Abs((position.Y - _recContent.Y) / _recLineCount.Height) * _vScrollSmall;
                UpdateVisibleByteIndex();
                UpdateScrollValues();

                if (CaretFocused)
                {
                    position.Y = _recContent.Y;
                }
            }
            else if (position.Y > _maxVisibleBytesV * _recLineCount.Height)
            {
                //Need to scroll down until caret is visible
                _vScrollPos += ((position.Y) / _recLineCount.Height - (_maxVisibleBytesV - 1)) * _vScrollSmall;

                if (_vScrollPos > (_vScrollMax - (_vScrollLarge - 1)))
                    _vScrollPos = (_vScrollMax - (_vScrollLarge - 1));

                UpdateVisibleByteIndex();
                UpdateScrollValues();

                if (CaretFocused)
                {
                    position.Y = (_maxVisibleBytesV - 1) * _recLineCount.Height + _recContent.Y;
                }
            }
            return position;
        }

        #endregion

        #region Update Actions

        private void UpdateRectanglePositioning()
        {
            if (ClientRectangle.Width == 0)
                return;

            //Start by calculating the size of a char
            SizeF charSize;
            using (var graphics = this.CreateGraphics())
            {
                charSize = graphics.MeasureString("D", Font, 100, _stringFormat);
            }
            CharSize = new SizeF((float)Math.Ceiling(charSize.Width), (float)Math.Ceiling(charSize.Height));

            //Set the main content bounds (remove margins)
            _recContent = ClientRectangle;
            _recContent.X += Margin.Left;
            _recContent.Y += Margin.Top;
            _recContent.Width -= Margin.Right;
            _recContent.Height -= Margin.Bottom;

            //Check if the border is active and decrease the bounds
            if (BorderStyle == BorderStyle.Fixed3D)
            {
                _recContent.X += 2;
                _recContent.Y += 2;
                _recContent.Width -= 1;
                _recContent.Height -= 1;
            }
            else if (BorderStyle == BorderStyle.FixedSingle)
            {
                _recContent.X += 1;
                _recContent.Y += 1;
                _recContent.Width -= 1;
                _recContent.Height -= 1;
            }

            //Handle if the scrollbar is visible
            if (!VScrollBarHidden)
            {
                _recContent.Width -= _vScrollBarWidth;
                _vScrollBar.Left = _recContent.X + _recContent.Width - Margin.Left;
                _vScrollBar.Top = _recContent.Y - Margin.Top;
                _vScrollBar.Width = _vScrollBarWidth;
                _vScrollBar.Height = _recContent.Height;
            }

            _recLineCount = new Rectangle(_recContent.X, _recContent.Y, (int)(_charSize.Width * 4), (int)(_charSize.Height) - 2);

            _editView.Update(_recLineCount.X + _recLineCount.Width + _entityMargin / 2, _recContent);

            //Calculate needed maximums for the bytes
            _maxBytesH = _bytesPerLine;
            _maxBytesV = (int)Math.Ceiling(((float)_recContent.Height / (float)_recLineCount.Height));
            _maxBytes = _maxBytesH * _maxBytesV;
            _maxVisibleBytesV = (int)Math.Floor(((float)_recContent.Height / (float)_recLineCount.Height));

            UpdateScrollBarSize();
        }

        private void UpdateVisibleByteIndex()
        {
            if (_hexTable.Length == 0)
            {
                _firstByte = 0;
                _lastByte = 0;
            }
            else
            {
                _firstByte = _vScrollPos * _maxBytesH;
                _lastByte = (int)Math.Min(HexTableLength, _firstByte + _maxBytes);
            }
        }

        private void UpdateScrollValues()
        {
            if (!_isVScrollHidden)
            {
                _vScrollBar.Minimum = _vScrollMin;
                _vScrollBar.Maximum = _vScrollMax;
                _vScrollBar.Value = _vScrollPos;

                _vScrollBar.SmallChange = _vScrollSmall;
                _vScrollBar.LargeChange = _vScrollLarge;

                _vScrollBar.Visible = true;
            }
            else
            {
                _vScrollBar.Visible = false;
            }
        }

        private void UpdateScrollBarSize()
        {
            if (VScrollBarVisisble && _maxVisibleBytesV > 0 && _maxBytesH > 0)
            {
                //Holds the size of a page (number of rows per page)
                int largeScroll = _maxVisibleBytesV;
                //Holds the row size (1)
                int smallScroll = 1;
                //Holds the minimum value of the scrollbar
                int minScroll = 0;
                //Holds the maximum value of the scrollbar
                int maxScroll = HexTableLength / _maxBytesH;
                //Holds the current positon on the scrollbar
                int posScroll = _firstByte / _maxBytesH;

                if (largeScroll != _vScrollLarge || smallScroll != _vScrollSmall)
                {
                    _vScrollLarge = largeScroll;
                    _vScrollSmall = smallScroll;
                }

                if (maxScroll >= largeScroll)
                {
                    if (maxScroll != _vScrollMax || posScroll != _vScrollPos)
                    {
                        _vScrollMin = minScroll;
                        _vScrollMax = maxScroll;
                        _vScrollPos = posScroll;
                    }

                    VScrollBarHidden = false;

                    UpdateScrollValues();
                }
                else
                {
                    VScrollBarHidden = true;
                }
            }
            else
            {
                VScrollBarHidden = true;
            }
        }

        #endregion

        #region Constructor

        public HexEditor()
            : this(new ByteCollection())
        { }

        public HexEditor(ByteCollection collection)
        {
            //Set String format for the hex values
            _stringFormat = new StringFormat(StringFormat.GenericTypographic);
            _stringFormat.Alignment = StringAlignment.Center;
            _stringFormat.LineAlignment = StringAlignment.Center;

            //Set the provided byte collection
            _hexTable = collection;

            //Set the vertical scrollbar
            _vScrollBar = new VScrollBar();
            _vScrollBar.Scroll += new ScrollEventHandler(OnVScrollBarScroll);

            //Redraw whenever the control is resized
            SetStyle(ControlStyles.ResizeRedraw, true);

            //Enable double buffering
            SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);

            //Enable selectable
            SetStyle(ControlStyles.Selectable, true);

            //Handle initialization of Caret
            _caret = new Caret(this);
            _caret.SelectionStartChanged += new EventHandler(CaretSelectionStartChanged);
            _caret.SelectionLengthChanged += new EventHandler(CaretSelectionLengthChanged);

            //Create the needed edit view
            _editView = new EditView(this);
            _handler = _editView;

            //Set defualt cursor
            this.Cursor = Cursors.IBeam;
        }

        #endregion

        #region Boundary Calculator

        private RectangleF GetLineCountBound(int index)
        {
            RectangleF ret = new RectangleF(
                _recLineCount.X,
                _recLineCount.Y + (_recLineCount.Height * index),
                _recLineCount.Width,
                _recLineCount.Height
                );

            return ret;
        }

        #endregion

        #region OnPaint - Functions

        protected override void OnPaintBackground(PaintEventArgs pevent)
        {

            if (BorderStyle == BorderStyle.Fixed3D)
            {
                SolidBrush brush = new SolidBrush(BackColor);
                Rectangle rect = ClientRectangle;
                pevent.Graphics.FillRectangle(brush, rect);
                ControlPaint.DrawBorder3D(pevent.Graphics, ClientRectangle, Border3DStyle.Sunken);
            }
            else if (BorderStyle == BorderStyle.FixedSingle)
            {
                SolidBrush brush = new SolidBrush(BackColor);
                Rectangle rect = ClientRectangle;
                pevent.Graphics.FillRectangle(brush, rect);
                ControlPaint.DrawBorder(pevent.Graphics, ClientRectangle, BorderColor, ButtonBorderStyle.Solid);
            }
            else
            {
                base.OnPaintBackground(pevent);
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            Region r = new Region(ClientRectangle);
            r.Exclude(_recContent);
            e.Graphics.ExcludeClip(r);

            UpdateVisibleByteIndex();

            PaintLineCount(e.Graphics, _firstByte, _lastByte);

            _editView.Paint(e.Graphics, _firstByte, _lastByte);
        }

        private void PaintLineCount(Graphics g, int startIndex, int lastIndex)
        {
            SolidBrush brush = new SolidBrush(ForeColor);

            for (int i = 0; ((i * _maxBytesH) + startIndex) <= lastIndex; i++)
            {
                RectangleF drawSurface = GetLineCountBound(i);
                string lineCount = (startIndex + (i * _maxBytesH)).ToString(_lineCountCaps);
                //Calculate how many '0' need to be added to the current count
                int zeros = _nrCharsLineCount - lineCount.Length;

                string lineStr;
                if (zeros > -1)
                {
                    lineStr = new string('0', zeros) + lineCount;
                }
                else
                {
                    lineStr = new string('~', _nrCharsLineCount);
                }
                g.DrawString(lineStr, Font, brush, drawSurface, _stringFormat);
            }
        }

        #endregion

        #region OnRezie

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);

            UpdateRectanglePositioning();
            Invalidate();
        }

        #endregion
    }
}
