using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace arFrame.Control
{
    public partial class GridView : System.Windows.Forms.Control
    {
        private int bordersize = 0;
        private int menubordersize = 1;
        private int menugap = 5;
        private System.Windows.Forms.Padding padding = new Padding(3, 3, 3, 3);
        private System.Drawing.Color backcolor = System.Drawing.Color.White;
        private System.Drawing.Color bordercolor = System.Drawing.Color.Black;
        private Boolean textattachtoimage = true;
        private Boolean _showIndexString = true;

        private System.Drawing.Color _shadowColor = System.Drawing.Color.Transparent;
        private System.Drawing.Color foreColorPin = System.Drawing.Color.WhiteSmoke;
        private System.Drawing.Font fontPin = new Font("Consolas", 8, FontStyle.Bold);

        private Point _matrixsize = new Point(8, 2);
        public Point MatrixSize { get { return _matrixsize; } set { _matrixsize = value; ResetArray(); RemakeChartRect(); } }

        [Browsable(false)]
        public int ColumnCount { get { return _matrixsize.X; } }
        [Browsable(false)]
        public int RowCount { get { return _matrixsize.Y; } }
        public int ItemCount { get { return ColumnCount * RowCount; } }

        private GridViewItem[] Items;

        private UInt16[] _values;
        private string[] _titles;
        private string[] _tags;
        private string[] _names;
        private ColorListItem[] _colorlist;
        public ColorListItem[] ColorList { get { return _colorlist; } set { _colorlist = value; this.Invalidate(); } }

        public string[] Names { get { return _names; } set { _names = value; Invalidate(); } }
        public string[] Titles { get { return _titles; } set { _titles = value; Invalidate(); } }
        public UInt16[] Values { get { return _values; } set { _values = value; Invalidate(); } }
        public string[] Tags { get { return _tags; } set { _tags = value; } }

        private bool _showdebuginfo = false;
        public Boolean showDebugInfo { get { return _showdebuginfo; } set { _showdebuginfo = value; Invalidate(); } }

        public void setNames(string[] value)
        {
            _names = value;
        }

        public void setTitle(string[] value)
        {
            List<string> titlerows = new List<string>();
            List<string> tagrows = new List<string>();

            for (int i = 0; i < value.Length; i++)
            {
                var r = (int)(Math.Floor((double)(i / ColumnCount)));
                var c = i % ColumnCount;
                
                if (titlerows.Count < r + 1) titlerows.Add(string.Empty);
                if (tagrows.Count < r + 1) tagrows.Add(string.Empty);

                var prestr = titlerows[r];
                if (prestr != "") prestr += "|";
                titlerows[r] = prestr + value[i];

                var prestr_t = tagrows[r];
                if (prestr_t != "") prestr_t += "|";
                tagrows[r] = prestr_t + "";

                if (i < itemCount) this.Items[i].Enable = true;
            }
            this._titles = titlerows.ToArray();
            this._tags = tagrows.ToArray();
        }
     
        public Boolean setTitle(int row, int col, string value, string itemtag = "")
        {
            if (_titles == null) _titles = new string[0];
            if (_tags == null) _tags = new string[0];

            if (  row >= _titles.Length) Array.Resize(ref _titles, row + 1);
            if(  row >= _tags.Length) Array.Resize(ref _tags, row+1);

            if (_titles[row] == null) _titles[row] = string.Empty;
            if (_tags[row] == null) _tags[row] = string.Empty;

            var linebuf = _titles[row].Split('|');
            var linebuf_t = _tags[row].Split('|');

            if (col >= linebuf.Length) Array.Resize(ref linebuf, col + 1);
            if (col >= linebuf_t.Length) Array.Resize(ref linebuf_t, col + 1);

            linebuf[col] = value;
            linebuf_t[col] = itemtag;

            _titles[row] = string.Join("|", linebuf);
            _tags[row] = string.Join("|", linebuf_t);
            return true;
            //var idx = row * this.ColumnCount + col;
            //return setTitle(idx, value);
        }
        public Boolean setTitle(int idx, string value)
        {
            if (idx < ColumnCount) return setTitle(0, idx, value);
            else
            {
                //줄값이 필요하다
                var row = (int)(Math.Floor((double)(idx / ColumnCount)));
                var col = idx % ColumnCount;
                return setTitle(row, col, value);
            }
        }


        public void setValue(bool[] value)
        {
            var v = new UInt16[value.Length];
            for (int i = 0; i < value.Length; i++)
                v[i] = (UInt16)(value[i] ? 1 : 0);
            _values = v;
        }
        public void setValue(UInt16[] value)
        {
            _values = value;
        }
        public Boolean setValue(int idx, ushort value)
        {
            if (this._values == null) _values = new ushort[idx + 1];
            if (idx >= this._values.Length) Array.Resize(ref _values, idx + 1);
            this._values[idx] = value;
            return true;
        }
        public Boolean setTag(string[] value)
        {
            this._tags = value;
            return true;
        }
        public Boolean setTag(int idx, string value)
        {
            if (this._tags == null) Tags = new string[idx + 1];
            if (idx >= this.Tags.Length) Array.Resize(ref _tags, idx + 1);
            this._tags[idx] = value;
            return true;
        }
        public void ClearValue(ushort defaultValue = 0)
        {
            if (_values != null)
                for(int i = 0; i < _values.Length;i++)
                    _values[i] = defaultValue;
        }
        public void ClearTitle(string defaultValue = "")
        {
            if (_values != null)
                for (int i = 0; i < _titles.Length; i++)
                    _titles[i] = defaultValue;
        }
        public void ClearTag(string defaultValue = "")
        {
            if (_tags != null)
                for (int i = 0; i < _tags.Length; i++)
                    _tags[i] = defaultValue;
        }
        public void setValue(ushort value)
        {
            for(int i = 0; i < _values.Length;i++)
                this._values[i] = value;
        }
        public void setItemEnable(int idx, bool value)
        {
            if (idx >= _values.Length || idx >= this.Items.Length) return;
            this.Items[idx].Enable = value;
        }

        /// <summary>
        /// 지정된 컬러태그값을 입력한다.
        /// </summary>
        /// <param name="idx"></param>
        /// <param name="tagString"></param>
        /// <returns></returns>
        public Boolean setValue(int idx, string tagString)
        {
            //동일태그값을 찾는다
            if (idx >= _values.Length) return false;

            int value = -1;
            for (int i = 0; i < ColorList.Length; i++)
                if (ColorList[i].Remark.ToLower() == tagString.ToLower())
                {
                    value = i;
                    break;
                }

            if (value != -1)
            {
                this._values[idx] = (ushort)value;
                this.Items[idx].Enable = true;
                return true;
            }
            else return false;


        }
        public Boolean setValue(int idx, bool value)
        {
            return setValue(idx, (ushort)(value ? 1 : 0));
        }
        public Boolean setValue(int row, int col, ushort value)
        {
            var idx = row * this.ColumnCount + col;
            return setValue(idx, value);
        }
        public Boolean setValue(int row, int col, int value)
        {
            var idx = row * this.ColumnCount + col;
            return setValue(idx, (ushort)value);
        }
        public Boolean setValueToggle(int row, int col, ushort value1,ushort value2)
        {
            var idx = row * this.ColumnCount + col;
            if(getValue(idx) == value1)   return setValue(idx, value2);
            else return setValue(idx, value1);
        }
        public Boolean setValue(int row, int col, bool value)
        {
            var idx = row * this.ColumnCount + col;
            return setValue(idx, (ushort)(value ? 1 : 0));
        }
        public Boolean setValue(int row, int col, string value)
        {
            var idx = row * this.ColumnCount + col;
            return setValue(idx, value);
        }

        public ushort getValue(int idx)
        {
            if (idx >= _values.Length) return 0;
            return _values[idx];
        }
        public ushort getValue(int row, int col)
        {
            var idx = row * this.ColumnCount + col;
            return getValue(idx);
        }

        [Category("arFrame")]
        public bool ShowIndexString { get { return _showIndexString; } set { _showIndexString = value; Invalidate(); } }

        [Category("arFrame"), DisplayName("테두리 굵기")]
        public int BorderSize { get { return bordersize; } set { this.bordersize = value; Invalidate(); } }
        [Category("arFrame"), DisplayName("메뉴 테두리 굵기")]
        public int MenuBorderSize { get { return menubordersize; } set { this.menubordersize = value; Invalidate(); } }
        [Category("arFrame"), DisplayName("메뉴 간격")]
        public int MenuGap { get { return menugap; } set { this.menugap = value; RemakeChartRect(); Invalidate(); } }
        [Category("arFrame"), DisplayName("글자를 이미지 다음에 표시"), Description("이미지가 있는 경우 해당 이미지 옆에 글자를 붙입니다")]
        public Boolean TextAttachToImage { get { return textattachtoimage; } set { this.textattachtoimage = value; Invalidate(); } }

        [Category("arFrame"), DisplayName("색상-테두리")]
        public System.Drawing.Color BorderColor { get { return bordercolor; } set { this.bordercolor = value; Invalidate(); } }
        [Category("arFrame"), DisplayName("내부 여백")]
        public new System.Windows.Forms.Padding Padding { get { return padding; } set { this.padding = value; RemakeChartRect(); Invalidate(); } }

        [Category("arFrame"), DisplayName("색상-전체배경색")]
        public override System.Drawing.Color BackColor { get { return backcolor; } set { this.backcolor = value; Invalidate(); } }

        [Category("arFrame"), DisplayName("색상-글자(그림자)")]
        public System.Drawing.Color ShadowColor { get { return _shadowColor; } set { _shadowColor = value; this.Invalidate(); } }

        [Category("arFrame"), DisplayName("색상-글자")]
        public override Color ForeColor { get { return base.ForeColor; } set { base.ForeColor = value; } }

        [Category("arFrame"), DisplayName("색상-글자(번호)")]
        public Color ForeColorPin { get { return foreColorPin; } set { foreColorPin = value; } }

        [Category("arFrame"), DisplayName("글꼴-번호")]
        public Font FontPin { get { return fontPin; } set { fontPin = value; Invalidate(); } }
        [Category("arFrame"), DisplayName("글꼴-항목")]
        public override Font Font { get { return base.Font; } set { base.Font = value; Invalidate(); } }

        private int mouseOverItemIndex = -1;
        public GridView()
        {
            InitializeComponent();

            // Set Optimized Double Buffer to reduce flickering
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

            // Redraw when resized
            this.SetStyle(ControlStyles.ResizeRedraw, true);

            //값과 이름은 외부의 값을 사용한다
            ResetArray();

            if (MinimumSize.Width == 0 || MinimumSize.Height == 0)
                MinimumSize = new Size(100, 50);
        }


        void ResetArray()
        {
            if (this._values != null) Array.Resize(ref this._values, itemCount);// = new UInt16[itemCount];
            // if (this._titles != null) Array.Resize(ref this._titles, itemCount);//
            // if (this._names != null) Array.Resize(ref this._names, itemCount);//
        }

        int itemCount { get { return ColumnCount * RowCount; } }
        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            RemakeChartRect();
        }

        public event EventHandler<ItemClickEventArgs> ItemClick;
        public class ItemClickEventArgs : EventArgs
        {
            public int idx { get; set; }
            public GridViewItem Item { get; set; }
            public ItemClickEventArgs(int idx_, GridViewItem item)
            {
                this.Item = item;
                this.idx = idx_;
            }
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            //마우스클릭시 해당 버튼을 찾아서 반환한다.
            if (Items == null || Items.Length < 1) return;
            for (int i = 0; i < Items.Length; i++)
            {
                var rect = Items[i].rect;//[i];
                if (rect.Contains(e.Location))
                {
                    var menu = Items[i];

                    //미사용개체는 이벤트를 아에 발생하지 않는다
                    if (menu.Enable == true && ItemClick != null)
                        ItemClick(this, new ItemClickEventArgs(i, menu));
                    break;
                }
            }
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            this.mouseOverItemIndex = -1;
            this.Invalidate();
        }
        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (Items == null || Items.Length < 1)
            {
                this.mouseOverItemIndex = -1;
                return;
            }

            for (int i = 0; i < Items.Length; i++)
            {
                var rect = Items[i].rect;// rects[i];
                if (rect.Contains(e.Location))
                {
                    if (i != mouseOverItemIndex)
                    {
                        mouseOverItemIndex = i;
                        this.Invalidate();
                    }

                    break;
                }
            }
        }
        public void setItemTextAlign(int row, int col, System.Drawing.ContentAlignment TextAlign)
        {
            var item = this.Items.Where(t => t.row == row && t.col == col).FirstOrDefault();
            if (item != null) item.TextAlign = TextAlign;
        }
        public void setItemTextAlign(System.Drawing.ContentAlignment TextAlign)
        {
            foreach (var item in this.Items)
                item.TextAlign = TextAlign;
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            //배경그리기
            //using (var sb = new System.Drawing.Drawing2D.LinearGradientBrush(DisplayRectangle, BackColor, BackColor2On, System.Drawing.Drawing2D.LinearGradientMode.Vertical))
            pe.Graphics.FillRectangle(new SolidBrush(BackColor), DisplayRectangle);

            if (Items == null)
            {
                pe.Graphics.DrawString("no items", this.Font, Brushes.Red, 100, 100);
                return;
            }

            var items = Items.OrderBy(t => t.No);
            foreach (var menu in items)
            {
                drawItem(menu.idx, pe.Graphics);
            }

            //테두리 그리기
            if (BorderSize > 0)
            {
                pe.Graphics.DrawRectangle(new Pen(this.BorderColor, BorderSize),
                this.DisplayRectangle.Left,
                this.DisplayRectangle.Top,
                this.DisplayRectangle.Width - 1,
                this.DisplayRectangle.Height - 1);
            }
        }

        public void drawItem(int itemIndex, Graphics g = null)
        {
            if (g == null) g = this.CreateGraphics();
            var menu = this.Items[itemIndex];

            if (menu.rect == RectangleF.Empty) return;

            var rect = menu.rect;// rects[i];

            var diplayText = string.Empty;

            //타이틀이 줄번호별로 처리됨
            if (_titles != null &&  menu.row < _titles.Length && _titles[menu.row] != null )
            {
                var linebif = _titles[menu.row].Split('|');
                if (menu.col < linebif.Length)
                    diplayText = linebif[menu.col];
            }

            UInt16 Value = 0;
            if (_values != null && menu.idx < _values.Length) Value = _values[menu.idx];

            //배경이 투명이 아니라면 그린다.            
            var bgColor1 = Color.FromArgb(30, 30, 30);// BackColor1Off;
            var bgColor2 = Color.FromArgb(30, 30, 30);// BackColor2Off;

            //해당 값에 따른 컬러값을 읽는다.
            if (ColorList != null && Value < ColorList.Length)
            {
                bgColor1 = this.ColorList[Value].BackColor1;
                bgColor2 = this.ColorList[Value].BackColor2;
            }

            using (var sb = new System.Drawing.Drawing2D.LinearGradientBrush(rect, bgColor1, bgColor2, System.Drawing.Drawing2D.LinearGradientMode.Vertical))
                g.FillRectangle(sb, rect);

            // if (mouseOverItemIndex == menu.idx)
            //     this.Cursor = Cursors.Hand;
            // else
            //    this.Cursor = Cursors.Arrow;

            //테두리를 그리는 속성과 트기가 설정된 경우에만 표시
            //if (mouseOverItemIndex == i)
            // {
            //    pe.Graphics.DrawRectangle(new Pen(Color.DeepSkyBlue), rect.Left, rect.Top, rect.Width, rect.Height);
            //}
            //else
            {
                if (MenuBorderSize > 0)
                {
                    using (var p = new Pen(BorderColor, MenuBorderSize))
                        g.DrawRectangle(p, rect.Left, rect.Top, rect.Width, rect.Height);
                }
            }

            //인덱스번호 출력
            if (ShowIndexString && _names != null && menu.idx < _names.Length)
            {
                //표시글자
                var idxstr = string.Format("[{0}] {1}", menu.idx, _names[menu.idx]);

                //그림자 추가
                if (ShadowColor != System.Drawing.Color.Transparent)
                    g.DrawString(idxstr, FontPin, new SolidBrush(ShadowColor), menu.rect.Left + 4, menu.rect.Top + 4);

                //일반글자표시
                g.DrawString(idxstr, FontPin, new SolidBrush(this.ForeColorPin), menu.rect.Left + 3, menu.rect.Top + 3);
            }

            if (diplayText != "")
            {
                using (StringFormat sf = new StringFormat(StringFormatFlags.NoClip))
                {
                    //글자를 텍스트 이후에 붙이는 거라면?
                    if (menu.TextAlign == ContentAlignment.BottomCenter || menu.TextAlign == ContentAlignment.BottomLeft ||
                         menu.TextAlign == ContentAlignment.BottomRight) sf.LineAlignment = StringAlignment.Far;
                    else if (menu.TextAlign == ContentAlignment.MiddleCenter || menu.TextAlign == ContentAlignment.MiddleLeft ||
                        menu.TextAlign == ContentAlignment.MiddleRight) sf.LineAlignment = StringAlignment.Center;
                    else if (menu.TextAlign == ContentAlignment.TopCenter || menu.TextAlign == ContentAlignment.TopLeft ||
                        menu.TextAlign == ContentAlignment.TopRight) sf.LineAlignment = StringAlignment.Near;

                    if (menu.TextAlign == ContentAlignment.BottomCenter || menu.TextAlign == ContentAlignment.MiddleCenter ||
                    menu.TextAlign == ContentAlignment.TopCenter) sf.Alignment = StringAlignment.Center;
                    else if (menu.TextAlign == ContentAlignment.BottomLeft || menu.TextAlign == ContentAlignment.MiddleLeft ||
                        menu.TextAlign == ContentAlignment.TopLeft) sf.Alignment = StringAlignment.Near;
                    else if (menu.TextAlign == ContentAlignment.BottomRight || menu.TextAlign == ContentAlignment.MiddleRight ||
                        menu.TextAlign == ContentAlignment.TopRight) sf.Alignment = StringAlignment.Far;

                    //그림자 추가
                    if (ShadowColor != System.Drawing.Color.Transparent)
                        g.DrawString(diplayText, this.Font, new SolidBrush(ShadowColor),
                            new RectangleF((float)(rect.Left + 1f), (float)(rect.Top + 1f), (float)rect.Width, (float)rect.Height), sf);

                    g.DrawString(diplayText, this.Font, new SolidBrush(ForeColor), rect, sf);
                }
            }

            if (showDebugInfo)
            {
                g.DrawString(Value.ToString(), this.fontPin, Brushes.SkyBlue, rect.Left, rect.Top);
            }

        }

        /// <summary>
        /// arFrame 전용 속성값을 복사 합니다
        /// </summary>
        /// <param name="ctl"></param>
        public void copyTo(GridView ctl)
        {
            ctl.backcolor = this.backcolor;
            ctl.menugap = this.menugap;
            ctl.Items = this.Items;
            ctl.menubordersize = this.menubordersize;
            ctl.padding = this.padding;
            ctl.ForeColor = this.ForeColor;
            ctl.Font = this.Font;
            ctl.TextAttachToImage = this.TextAttachToImage;
            ctl.bordercolor = this.bordercolor;
            ctl.bordersize = this.bordersize;
        }

        public void RemakeChartRect()
        {
            if (DisplayRectangle == Rectangle.Empty) return;

            double x = 0;
            double y = 0;
            double w = DisplayRectangle.Width / (ColumnCount * 1.0);
            double h = DisplayRectangle.Height / (RowCount * 1.0);

            if (this.Items == null || itemCount != this.Items.Length)
            {
                //아이템갯수가 달라졌으므로 다시 갱신해야함
                GridViewItem[] item = new GridViewItem[RowCount * ColumnCount];
                for (int r = 0; r < RowCount; r++)
                {
                    for (int c = 0; c < ColumnCount; c++)
                    {
                        int idx = r * ColumnCount + c;
                        item[idx] = new GridViewItem(idx, r, c);
                        item[idx].Enable = false;
                        item[idx].Padding = new Padding(0, 0, 0, 0);
                        item[idx].TextAlign = ContentAlignment.MiddleCenter;
                        x = (c * w);
                        y = (r * h);
                        item[idx].rect = new RectangleF((float)x, (float)y, (float)w, (float)h);
                    }
                }

                this.Items = item;
            }
            else
            {
                //아이템의 갯수는 같으므로 좌표값만 변경해준다.
                for (int r = 0; r < RowCount; r++)
                {
                    for (int c = 0; c < ColumnCount; c++)
                    {
                        int idx = r * ColumnCount + c;
                        var item = Items[idx];
                        x = (c * w);
                        y = (r * h);
                        item.Dirty = true;
                        item.rect = new RectangleF((float)x, (float)y, (float)w, (float)h);
                    }
                }
            }
            this.Invalidate();

            //int i = 0;
            //var menuList = this.Items.OrderBy(t => t.No).ToArray();
            //foreach (var menu in menuList) 
            //{
            //    int x, y, w, h;
            //   // var menu = menus[i];

            //    var mWidth = menuwidth;
            //    if (menu.MenuWidth > 0) mWidth = menu.MenuWidth;

            //    w = mWidth;
            //    h = DisplayRectangle.Height - Padding.Top - Padding.Bottom;

            //    if (menu.isRightMenu)
            //    {
            //        x = DisplayRectangle.Right - Padding.Right - (rightAcc) - (MenuGap * rightIdx);
            //        y = DisplayRectangle.Top + Padding.Top;
            //        rightAcc += 0;// = 0;// x;
            //        rightIdx += 1;
            //    }
            //    else
            //    {
            //        x = DisplayRectangle.Left + Padding.Left + leftAcc + (MenuGap * leftIdx);
            //        y = DisplayRectangle.Top + Padding.Top;
            //        leftAcc += mWidth;
            //        leftIdx += 1;
            //    }
            //    rects[i] = new Rectangle(x, y, w, h);
            //    i += 1;
            //}
        }
    }
}
