using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Collections;
using static JtkManagerClient.PcListViewItem;

namespace JtkManagerClient {
    
    public interface IPcListViewItemOwner {
        void Selected(string pcId);
        void Deselected(string pcId);
        void DeselectAll(string withoutPcId);
    }

    public partial class PcListView : UserControl, IPcListViewItemOwner {

        public readonly float dpiScale;
        public readonly float itemWidth;

        private readonly PcListViewItemCollection items;
        public PcListViewItemCollection Items { get => items; }

        public event EventHandler SelectedItemChanged;
        public event ItemDragEventHandler ItemDrag;
        
        //private readonly SelectedPcMapEditPanelItemCollection selectedItems = new SelectedPcMapEditPanelItemCollection();
        public HashSet<PcListViewItem> SelectedItems {
            get {
                var hashSet = new HashSet<PcListViewItem>();
                foreach (var item in Items) {
                    if (item.Selected == true) {
                        hashSet.Add(item);
                    }
                }
                return hashSet;
            }
        }

        public PcListView() {
            InitializeComponent();
            //DoubleBuffered = true;
            items = new PcListViewItemCollection(this);
            using (var g = CreateGraphics()) {
                dpiScale = g.DpiX / 96f;
                itemWidth = dpiScale * 100.0f;
            }
        }

        public void Redraw() {
            // 필요없는 것 삭제
            var removeList = new List<PcListViewItemPanel>();
            foreach (var control in Controls) {
                if (control is PcListViewItemPanel panel && Items.Contains(panel.Item) == false) {
                    removeList.Add(panel);
                }
            }
            foreach (var panel in removeList) {
                Controls.Remove(panel);
            }

            // 없는것 추가하며 수정
            foreach (var item in Items) {
                PcListViewItemPanel panel = null;
                foreach (var control in Controls) {
                    if (control is PcListViewItemPanel p && p.Item == item) {
                        panel = p;
                        break;
                    }
                }
                if (panel == null) {
                    panel = new PcListViewItemPanel(this, item);
                    panel.DragStart += OnPanelDragStart;
                    panel.Name = item.PcId;
                    panel.Width = panel.Height = (int)itemWidth;
                    
                    Controls.Add(panel);
                }
                panel.Item = item;
                
                panel.Left = panel.Width * panel.Item.Column - HorizontalScroll.Value;
                panel.Top = panel.Height * panel.Item.Row - VerticalScroll.Value;

                panel.Invalidate();
            }
        }

        public (int row, int column) GetRowColumnAt(int x, int y) {
            x += HorizontalScroll.Value;
            y += VerticalScroll.Value;
            return ((int)(y / itemWidth), (int)(x / itemWidth));
        }

        public void Selected(string pcId) {
            //foreach (var control in Controls) {
            //    if (control is PcListViewItemPanel panel && panel.Item.PcId == pcId) {
            //        panel.Invalidate();
            //        break;
            //    }
            //}
            SelectedItemChanged?.Invoke(this, EventArgs.Empty);
            Controls[pcId].Invalidate();
        }

        public void Deselected(string pcId) {
            //foreach (var control in Controls) {
            //    if (control is PcListViewItemPanel panel && panel.Item.PcId == pcId) {
            //        panel.Invalidate();
            //        break;
            //    }
            //}
            SelectedItemChanged?.Invoke(this, EventArgs.Empty);
            Controls[pcId].Invalidate();
        }

        public void DeselectAll(string withoutPcId) {
            //throw new NotImplementedException();
            if (string.IsNullOrWhiteSpace(withoutPcId) == false) {
                //var invalidatePcIdHashSet = new HashSet<string>();
                foreach (var item in Items) {
                    if (item.PcId != withoutPcId && item.Selected == true) {
                        item.Selected = false;
                        //Deselected(item.PcId);
                        //invalidatePcIdHashSet.Add(item.PcId);
                    }
                }
                //foreach (var control in Controls) {
                    //if (control is PcListViewItemPanel panel && invalidatePcIdHashSet.Contains(panel.Item.PcId) == true) {
                        //panel.Invalidate();
                    //}
                //}
            } else {
                foreach (var control in Controls) {
                    if (control is PcListViewItemPanel panel && panel.Item.Selected == true) {
                        panel.Item.Selected = false;
                        //Deselected(panel.Item.PcId);
                        //panel.Invalidate();
                    }
                }
            }
        }

        private void OnPanelDragStart(object sender, EventArgs e) {
            ItemDrag?.Invoke(sender, new ItemDragEventArgs(MouseButtons.Left, (sender as PcListViewItemPanel).Item));
        }

        protected override void OnMouseDown(MouseEventArgs e) {
            base.OnMouseDown(e);

            DeselectAll(null);
        }

        public class PcListViewItemCollection : IList<PcListViewItem>, ICollection<PcListViewItem>, IEnumerable<PcListViewItem> {
            private readonly List<PcListViewItem> list = new List<PcListViewItem>();

            public PcListViewItem this[int index] { get => list[index]; set => list[index] = value; }
            public PcListViewItem this[string pcId] { get => list[IndexOf(pcId)]; set => list[IndexOf(pcId)] = value; }

            public int Count => list.Count;

            public bool IsReadOnly => false;

            private PcListView owner;
            internal PcListViewItemCollection(PcListView owner) {
                this.owner = owner;
            }

            public void Add(PcListViewItem item) {
                item.Owner = owner;
                list.Add(item);
            }

            public void Clear() => list.Clear();

            public bool Contains(PcListViewItem item) => list.Contains(item);

            public void CopyTo(PcListViewItem[] array, int arrayIndex) => list.CopyTo(array, arrayIndex);

            public IEnumerator<PcListViewItem> GetEnumerator() => list.GetEnumerator();

            public int IndexOf(PcListViewItem item) => list.IndexOf(item);

            public int IndexOf(string pcId) => list.IndexOf(new PcListViewItem(pcId));

            public void Insert(int index, PcListViewItem item) => list.Insert(index, item);

            public bool Remove(PcListViewItem item) => list.Remove(item);

            public void RemoveAt(int index) => list.RemoveAt(index);

            IEnumerator IEnumerable.GetEnumerator() => list.GetEnumerator();
        }

        protected class PcListViewItemPanel : UserControl {
            public PcListViewItem Item;
            private IPcListViewItemOwner owner;

            public event EventHandler DragStart;

            public PcListViewItemPanel(IPcListViewItemOwner owner, PcListViewItem item) {
                InitializeComponent();
                Item = item;
                this.owner = owner;
            }

            private void InitializeComponent() {
                this.SuspendLayout();
                //this.AllowDrop = true;
                //this.AutoScaleDimensions = new SizeF(10F, 18F);
                //this.AutoScaleMode = AutoScaleMode.Font;
                //this.AutoScroll = true;
                //this.BorderStyle = BorderStyle.None;
                this.BackColor = SystemColors.ActiveBorder;
                this.DoubleBuffered = true;
                this.Name = "PcListViewItemPanel";
                this.Size = new System.Drawing.Size(100, 100);
                this.ResumeLayout(false);
            }

            private bool leftMouseButtonDown = false;

            protected override void OnMouseDown(MouseEventArgs e) {
                base.OnMouseDown(e);

                if (e.Button == MouseButtons.Left) {
                    if ((ModifierKeys & Keys.Control) == Keys.Control) {
                        //MessageBox.Show("컨트롤");
                        if (Item.Selected == true) {
                            Item.Selected = false;
                            //owner.Deselected(Item.PcId);
                        } else {
                            Item.Selected = true;
                            //owner.Selected(Item.PcId);
                        }
                    } else {
                        //MessageBox.Show("노컨트롤");
                        if (Item.Selected == false) {
                            Item.Selected = true;
                            //owner.Selected(Item.PcId);
                        }
                        
                        owner.DeselectAll(Item.PcId);
                    }
                    Invalidate();

                    leftMouseButtonDown = true;
                }
            }

            protected override void OnMouseMove(MouseEventArgs e) {
                base.OnMouseMove(e);

                if (leftMouseButtonDown == true) {
                    DragStart?.Invoke(this, EventArgs.Empty);
                    leftMouseButtonDown = false;
                }
            }

            protected override void OnMouseUp(MouseEventArgs e) {
                base.OnMouseUp(e);

                if (e.Button == MouseButtons.Left) {
                    leftMouseButtonDown = false;
                }
            }

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

                var g = e.Graphics;

                // 큰 사각형
                var paddingH = Width * 0.05f;
                var paddingV = Height * 0.05f;
                var bigRect = new RectangleF {
                    X = paddingH,
                    Y = paddingV,
                    Width = Width - paddingH * 2,
                    Height = Height - paddingV * 2
                };

                // 작은 사각형
                var borderWidth = Width * 0.05f;
                var borderHeight = Height * 0.05f;
                var smallRect = new RectangleF {
                    X = bigRect.X + borderWidth,
                    Y = bigRect.Y + borderHeight,
                    Width = bigRect.Width - borderWidth * 2,
                    Height = bigRect.Height - borderHeight * 2
                };

                // 번호 사각형 + 텍스트
                var numberString = Item.Number.ToString("000");
                var numberStringSize = g.MeasureString(numberString, Font);
                var numberRect = new RectangleF {
                    X = bigRect.X,
                    Y = bigRect.Y,
                    Width = numberStringSize.Width,
                    Height = numberStringSize.Height
                };

                // 브러쉬
                var backBrush = Brushes.White;
                var foreBrush = default(Brush);

                // 선택된 경우
                if (Item.Selected) {
                    g.FillRectangle(SystemBrushes.MenuHighlight, paddingH * 0.333f, paddingV * 0.333f, Width - paddingH*0.666f, Height- paddingH*0.666f);
                }

                // 큰 사각형 그리기
                g.FillRectangle(backBrush, bigRect);

                // 작은 사각형 + 텍스트 그리기
                switch (Item.Mode) {
                    case PcListViewItemPanelMode.Off:
                        g.FillRectangle(foreBrush = Brushes.Black, smallRect);
                        var offString = "전원 꺼짐";
                        var offStringSize = g.MeasureString(offString, Font);
                        g.DrawString(offString, Font, backBrush, new PointF(smallRect.X + smallRect.Width / 2 - offStringSize.Width / 2, smallRect.Y + smallRect.Height / 2 - offStringSize.Height / 2));
                        break;
                    case PcListViewItemPanelMode.ShowId:
                        g.FillRectangle(foreBrush = Brushes.Black, smallRect);
                        var pcIdSize = g.MeasureString(Item.PcId, Font);
                        g.DrawString(Item.PcId, Font, backBrush, new PointF(smallRect.X + smallRect.Width / 2 - pcIdSize.Width / 2, smallRect.Y + smallRect.Height / 2 - pcIdSize.Height / 2));
                        break;
                    case PcListViewItemPanelMode.Using:
                        g.FillRectangle(foreBrush = Brushes.ForestGreen, smallRect);
                        var timeElapsed = DateTime.Now - Item.StartTime;
                        var startTime = timeElapsed.TotalDays < 1.0 ? timeElapsed.ToString(@"hh\:mm") : timeElapsed.ToString(@"d\.hh\:mm");
                        var startTimeSize = g.MeasureString(startTime, Font);
                        g.DrawString(startTime, Font, Brushes.White, new PointF(smallRect.X + numberStringSize.Width + (smallRect.Width - numberStringSize.Width) / 2 - startTimeSize.Width / 2, smallRect.Y));
                        var userId = Item.UserId;
                        var userIdSize = g.MeasureString(userId, Font);
                        g.DrawString(userId, Font, Brushes.White, new PointF(smallRect.X + smallRect.Width / 2 - userIdSize.Width / 2, smallRect.Y + numberStringSize.Height));
                        break;
                    case PcListViewItemPanelMode.Waiting:
                        g.FillRectangle(foreBrush = Brushes.Gray, smallRect);
                        var waitingString = "대기 중";
                        var waitingStringSize = g.MeasureString(waitingString, Font);
                        g.DrawString(waitingString, Font, backBrush, new PointF(smallRect.X + smallRect.Width / 2 - waitingStringSize.Width / 2, smallRect.Y + smallRect.Height / 2 - waitingStringSize.Height / 2));
                        break;
                }

                

                // 번호 사각형 + 텍스트 그리기
                g.FillRectangle(backBrush, numberRect);
                g.DrawString(numberString, Font, foreBrush, numberRect.Location);
            }
        }
    }

    public class PcListViewItem : IEquatable<PcListViewItem> {
        public enum PcListViewItemPanelMode {
            Off, Waiting, Using, ShowId
        }

        public IPcListViewItemOwner Owner;
        public PcListViewItem(string pcId, IPcListViewItemOwner owner = null) {
            Owner = owner;
            PcId = pcId;
        }

        public PcListViewItemPanelMode Mode;
        public readonly string PcId;
        public int Number;
        public int Row;
        public int Column;
        public string UserId;
        public DateTime StartTime;

        public bool IsGuest;
        public double TotalPaidCash;
        public double cashLeft;

        private bool selected = false;
        

        public bool Selected {
            get => selected;
            set {
                if (selected != value) {
                    selected = value;
                    if (selected == true) {
                        Owner?.Selected(PcId);
                    } else {
                        Owner?.Deselected(PcId);
                    }
                }
            }
        }

        public bool Equals(PcListViewItem other) => PcId == other.PcId;
        public override bool Equals(object other) => other is PcListViewItem item && PcId == item.PcId;

        public override int GetHashCode() => PcId.GetHashCode();

        public static bool operator == (PcListViewItem a, PcListViewItem b) => a.Equals(b);
        public static bool operator != (PcListViewItem a, PcListViewItem b) => !a.Equals(b);
    }
}
