using JtkLibrary;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net.WebSockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace JtkManagerClient {
    public partial class MainForm : Form {
        //private SkinFramework.SkinningManager skinningManager;

        public MainForm() {
            InitializeComponent();
            WebSocketManager.OnReceive += WebSocketManager_OnReceive;
            //skinningManager = new SkinFramework.SkinningManager { DefaultSkin = SkinFramework.DefaultSkin.Office2007Obsidian, ParentForm = this };
        }

        /// <summary>로컬 서버로부터 메시지 수신</summary>
        private void WebSocketManager_OnReceive(object sender, WebSocketManagerEventArgs e) {
            if (e.MessageType != WebSocketMessageType.Text) {
                return;
            }
            try {
                var reqJobj = JObject.Parse(e.TextMessage);
                var type = reqJobj["type"].ToString();
                switch (type) {
                    case "notification": { 
                        var time = reqJobj["time"].ToObject<DateTime>();
                        var message = reqJobj["message"].ToString(); ;
                        notiListView.Invoke(new Action(() => {
                            notiListView.Items.Insert(0, new ListViewItem(new string[] { time.ToString("MM/dd HH:mm"), message }));
                        }));
                        break;
                    }
                    case "set_pc_map": {
                        var map = reqJobj["pc_map"].ToString();
                        UpdatePcListView(map);
                        break;
                    }
                    case "set_fare_per_hour": {
                        var fph = reqJobj["fare_per_hour"].ToObject<double>();
                        Program.FarePerHour = fph;
                        
                        break;
                    }
                    case "submit_order": {
                        var orderList = reqJobj["order_list"].ToString();
                        var pcNumber = reqJobj["pc_number"].ToObject<int>();
                        var paymentMethod = reqJobj["payment_method"].ToString();
                        var list = reqJobj["order_list"].ToList();
                        var dicts = (from item in list select new Dictionary<string, string> { { "name", item["Item1"].ToString() }, { "quantity", item["Item2"].ToString() } }).ToArray();

                        var lvItem = new ListViewItem(new string[] { DateTime.Now.ToString("MM/dd HH:mm"), $"{pcNumber}번 PC 주문 [처리중]" });
                        var method = paymentMethod == "cash" ? "현금" : (paymentMethod == "card" ? "카드" : "결제완료");
                        var form = new OrderInfoForm(dicts, pcNumber, method, lvItem);
                        lvItem.Tag = form;

                        
                        notiListView.Invoke(new Action(() => {
                            notiListView.Items.Insert(0, lvItem);
                        }));
                        break;
                    }
                    default:
                        break;
                }
            } catch (Exception ex) {
                MessageBox.Show(ex.ToString());
            }
        }

        private async void MainForm_Load(object sender, EventArgs e) {
            

            // 최근 알림 명단 가져오기
            var response = await Program.GetPostResponseAsync(new { work = "get_recent_notifications" });
            if (response.TryGetValue("list", out var notifications)) {
                foreach (var notification in notifications) {
                    var time = notification["time"].ToObject<DateTime>();
                    var message = notification["message"].ToString();
                    notiListView.Items.Add(new ListViewItem(new string[] { time.ToString("MM\\/dd HH:mm"), message }));
                }
            }

            // 맵 가져오기
            response = await Program.GetPostResponseAsync(new { work = "get_pc_map" });
            var map = response["pc_map"].ToString();
            UpdatePcListView(map);
            refreshTimer.Enabled = true;

            // 레이아웃 버그 수정
            foreach (var label in new Label[] { statusLabel, userIdLabel, loggedInTimeLabel, usedTimeLabel,usedCashLabel,remainingTimeLabel,remainingCashLabel }) {
                label.Width = detailGroupBox.Width - label.Left-10;
            }
            countLabel.Left = 10;
            countLabel.Width = pcListView.Left - 20;
            pcNumberLabel.Left = detailGroupBox.Left+10;
            pcNumberLabel.Width = detailGroupBox.Width-20;
            notiLabel.Left = 0;
            notiLabel.Width = pcListView.Left;
            var scale = CreateGraphics().DpiX / 96f;
            foreach (ColumnHeader col in notiListView.Columns) {
                col.Width = (int)(col.Width * scale);
            }
            notiListView.Height = ClientSize.Height - notiListView.Top - 10;
            pcListView.Height = ClientSize.Height - pcListView.Top - 10;
            //pcListView.Width = Width - 3;
        }

        /// <summary>
        /// 목록을 모두 비우고 다시 추가
        /// </summary>
        void UpdatePcListView(string json) {
            pcListView.Items.Clear();
            foreach (var pair in JObject.Parse(json)) {
                pcListView.Items.Add(new PcListViewItem(pcId:pair.Key) {
                    Number = pair.Value["num"].ToObject<int>(),
                    Row = pair.Value["row"].ToObject<int>(),
                    Column = pair.Value["col"].ToObject<int>()
                });
            }
            pcListView.Redraw();
        }

        // 일정 시간 주기로 새로고침
        private async void refreshTimer_Tick(object sender, EventArgs e) {
            // 로컬 서버에서 사용중인 PC 리스트 받아오기
            var response = await Program.GetPostResponseAsync(new { work = "get_using_pc_list" });
            // 우선 모두 Off
            foreach (var item in pcListView.Items) {
                item.Mode = PcListViewItem.PcListViewItemPanelMode.Off;
            }
            // 리스트 내용대로 채우기
            var usingCount = 0;
            if (response.TryGetValue("list", out var tknList) == true) {
                foreach (var tkn in tknList) {
                    var pcId = tkn["pc_id"].ToString();
                    var userId = tkn["user_id"]?.ToString();
                    var startTime = tkn["login_time"]?.ToObject<DateTime>() ?? default(DateTime);
                    var isGuest = tkn["user_is_guest"]?.ToObject<bool>() ?? false;
                    var totalPaidCash = tkn["total_paid_cash"]?.ToObject<double>() ?? 0.0;
                    var cashLeft = tkn["cash_left"]?.ToObject<double>() ?? 0.0;

                    var index = pcListView.Items.IndexOf(pcId);
                    if (index < 0) {
                        continue;
                    }
                    var pcMapItem = pcListView.Items[index];
                    if (string.IsNullOrWhiteSpace(userId) == true) {
                        pcMapItem.Mode = PcListViewItem.PcListViewItemPanelMode.Waiting;
                    } else {
                        usingCount++;
                        pcMapItem.Mode = PcListViewItem.PcListViewItemPanelMode.Using;
                    }
                    pcMapItem.StartTime = startTime;
                    pcMapItem.UserId = userId;
                    pcMapItem.IsGuest = isGuest;
                    pcMapItem.TotalPaidCash = totalPaidCash;
                    pcMapItem.cashLeft = cashLeft;
                }
            }
            pcListView.Redraw();
            // 레이블1
            countLabel.Text = $"{usingCount}/{pcListView.Items.Count}";
            // 레이블2
            UpdateDetailBox();
        }

        private void pcListView_SelectedItemChanged(object sender, EventArgs e) {
            UpdateDetailBox();
            //pcListView.SelectedItems.First().PcId
        }

        private void UpdateDetailBox() {
            var items = pcListView.SelectedItems;
            if (items.Count == 0) {
                pcNumberLabel.Text = "선택된 PC 없음";
                statusLabel.Text = userIdLabel.Text = loggedInTimeLabel.Text = usedTimeLabel.Text = usedCashLabel.Text = remainingTimeLabel.Text = remainingCashLabel.Text = "-";
            } else if (items.Count > 1) {
                pcNumberLabel.Text = $"{items.Count}개 PC 선택됨";
                statusLabel.Text = userIdLabel.Text = loggedInTimeLabel.Text = usedTimeLabel.Text = usedCashLabel.Text = remainingTimeLabel.Text = remainingCashLabel.Text = "-";
            } else {
                var item = items.First();
                pcNumberLabel.Text = $"PC {item.Number}";
                switch (item.Mode) {
                    case PcListViewItem.PcListViewItemPanelMode.Off: statusLabel.Text = "전원 꺼짐"; break;
                    case PcListViewItem.PcListViewItemPanelMode.Using: statusLabel.Text = item.IsGuest ? "비회원 사용 중" : "회원 사용 중"; break;
                    case PcListViewItem.PcListViewItemPanelMode.Waiting: statusLabel.Text = "대기 중"; break;
                }

                userIdLabel.Text = string.IsNullOrWhiteSpace(item.UserId) ? "-" : item.UserId;

                if (item.StartTime == default(DateTime)) {
                    loggedInTimeLabel.Text = usedTimeLabel.Text = usedCashLabel.Text = remainingTimeLabel.Text = remainingCashLabel.Text = "-";
                } else {
                    var usedTime = DateTime.Now - item.StartTime;
                    var remainingTime = TimeSpan.FromHours(Program.FarePerHour > 0 ? item.cashLeft / Program.FarePerHour : 0);
                    loggedInTimeLabel.Text = item.StartTime.ToString("HH시 mm분");
                    usedTimeLabel.Text = $"{(int)usedTime.TotalHours:N0}시간 {usedTime.Minutes}분";
                    usedCashLabel.Text = $"￦{item.TotalPaidCash:N0}";
                    remainingTimeLabel.Text = $"{(int)remainingTime.TotalHours:N0}시간 {remainingTime.Minutes}분";
                    remainingCashLabel.Text = $"￦{item.cashLeft:N0}";
                }
            }
        }

        #region Menu Items

        private async void pcMapMenuItem_Click(object sender, EventArgs e) {
            refreshTimer.Enabled = false;

            var form = new PcMapForm(pcListView.Items.ToArray());
            form.ShowDialog();
            if (form.DialogResult == DialogResult.OK) {
                try {
                    var response = await Program.GetPostResponseAsync(new { work = "set_pc_map", pc_map = form.ResultPcMapJson });
                    if (response["result"].ToString() == "ok") {
                        MessageBox.Show("변경되었습니다!", "", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    } else {
                        throw new Exception();
                    }
                } catch {
                    MessageBox.Show("실패했습니다!", "", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }

            refreshTimer.Enabled = true;
        }

        private void guestMenuItem_Click(object sender, EventArgs e) {
            var form = new GuestListForm();
            form.ShowDialog();
        }

        private void exitMenuItem_Click(object sender, EventArgs e) {
            Close();
        }

        private void aboutMenuItem_Click(object sender, EventArgs e) {
            new AboutBox().ShowDialog();
        }

        private void salesMenuItem_Click(object sender, EventArgs e) {
            var form = new SalesForm();
            form.ShowDialog();
        }

        private async void farePerHourMenuItem_Click(object sender, EventArgs e) {
            var form = new FphChangeForm(Program.FarePerHour);
            if (form.ShowDialog() == DialogResult.OK) {
                await Program.GetPostResponseAsync(new { work = "set_fare_per_hour", fare_per_hour = form.NewFarePerHour });
                MessageBox.Show("변경되었습니다.", "", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }


        #endregion

        private void descriptionMenuItem_Click(object sender, EventArgs e) {
            new IntoductionForm().ShowDialog();
        }

        private void notiListView_DoubleClick(object sender, EventArgs e) {
            if (notiListView.SelectedItems.Count > 0 && notiListView.SelectedItems[0].Tag is OrderInfoForm form) {
                form.ShowDialog();
            }
        }
    }

    public class PcMapKeyComparer : IComparer<string> {
        Dictionary<string, (int number, int row, int column)> PcMap;

        public PcMapKeyComparer(Dictionary<string, (int number, int row, int column)>  pcMap) {
            PcMap = pcMap;
        }
        public int Compare(string x, string y) {
            var a = PcMap[x].number;
            var b = PcMap[y].number;
            if (a < b) {
                return -1;
            }
            if (a > b) {
                return 1;
            }
            return 0;
        }
    }
}
