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

namespace JtkLocalServerSimulator {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        private async void button1_Click(object sender, EventArgs e) {
            addressTextBox.ReadOnly = true;
            button1.Enabled = false;

            var list = new List<(string id, string password, int fph, CheckBox checkBox)>();
            for (var row = 1; row < tableLayoutPanel1.RowCount; row++) {
                var idTextBox = tableLayoutPanel1.GetControlFromPosition(0, row) as TextBox;
                var passwordTextBox = tableLayoutPanel1.GetControlFromPosition(1, row) as TextBox;
                var fphTextBox = tableLayoutPanel1.GetControlFromPosition(2, row) as TextBox;
                var checkBox = tableLayoutPanel1.GetControlFromPosition(3, row) as CheckBox;
                idTextBox.ReadOnly = passwordTextBox.ReadOnly = fphTextBox.ReadOnly = true;

                var id = idTextBox.Text.Trim();
                var password = passwordTextBox.Text.Trim();
                int.TryParse(fphTextBox.Text, out var fph);

                if (!string.IsNullOrWhiteSpace(id) && !string.IsNullOrWhiteSpace(password) && fph > 0) {
                    list.Add((id, password, fph, checkBox));
                }
            }

            var webSocketUri = new Uri($"wss://{addressTextBox.Text}/ws");
            var webApiUri = new Uri($"https://{addressTextBox.Text}/api");
            ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };  // 보안인증서 무조건신뢰
            foreach (var item in list) {
                var webSocket = new ClientWebSocket();
                try {
                    // 로컬서버와 웹소켓 연결
                    await webSocket.ConnectAsync(webSocketUri, new CancellationTokenSource(5000).Token);
                    if (webSocket.State != WebSocketState.Open) {
                        throw new Exception("Failed to connect to the server.");
                    }
                    // 인증 정보 송신
                    var sendBuffer = Encoding.UTF8.GetBytes(JObject.FromObject(new { place_id = item.id, place_password = item.password, fare_per_hour = item.fph}).ToString());
                    webSocket.SendAsync(new ArraySegment<byte>(sendBuffer), WebSocketMessageType.Text, true, CancellationToken.None).Wait();
                    // 인증 결과 수신
                    var receiveBuffer = new byte[1024];
                    var receiveTask = webSocket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
                    receiveTask.Wait();
                    var receiveResult = receiveTask.Result;
                    // 거부되었나 확인
                    if (receiveResult.CloseStatus != null) {
                        throw new Exception(receiveResult.CloseStatusDescription);
                    }
                    // 유효하지 않은 결과인지 확인
                    if (receiveResult.MessageType != WebSocketMessageType.Text) {
                        throw new Exception("Received invalid data.");
                    }
                    var receiveTextBuilder = new StringBuilder(Encoding.UTF8.GetString(receiveBuffer));
                    while (receiveResult.EndOfMessage == false) {
                        receiveBuffer = new byte[1024];
                        receiveTask = webSocket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
                        receiveTask.Wait();
                        receiveResult = receiveTask.Result;
                        receiveTextBuilder.Append(Encoding.UTF8.GetString(receiveBuffer));
                    }
                    // 결과 JSON 파싱
                    var receiveText = receiveTextBuilder.ToString();
                    var receiveJObject = JObject.Parse(string.IsNullOrWhiteSpace(receiveText) ? "{}" : receiveText);
                    // ok 아닐 시
                    if (receiveJObject.GetValue("result").ToString() != "ok") {
                        throw new Exception("The response is not ok.");
                    }

                    var pcMapJson = receiveJObject["pc_map"].ToString();
                    var pcNumbers = new List<int>();
                    var jobj = JObject.Parse(string.IsNullOrWhiteSpace(pcMapJson) ? "{}" : pcMapJson);
                    var pcCount = jobj.Count;
                    foreach (var tkn in jobj) {
                        pcNumbers.Add(tkn.Value["num"].ToObject<int>());
                    }
                    var authKey = receiveJObject.GetValue("auth_key").ToObject<int>();
                    var rand = new Random();
                    var usingPcCount = rand.Next((int)(pcNumbers.Count * 0.333), (int)(pcNumbers.Count * 0.95));
                    var usingPcNumbers = new List<int>();
                    for (var i = 0; i < usingPcCount; i++) {
                        var randIdx = rand.Next(pcNumbers.Count - 1);
                        usingPcNumbers.Add(pcNumbers[randIdx]);
                        pcNumbers.RemoveAt(randIdx);
                    }
                    var requestBody = JObject.FromObject(new { work = "update_using_pc_nums", place_id = item.id, auth_key = authKey, list = usingPcNumbers, total_pc_count = pcCount }).ToString();

                    // 요청하기
                    HttpWebRequest request = WebRequest.Create(webApiUri) as HttpWebRequest;
                    request.CookieContainer = new CookieContainer();
                    request.Method = "POST";
                    var requestBuffer = Encoding.UTF8.GetBytes(requestBody);
                    request.ContentLength = requestBuffer.Length;
                    request.ContentType = "application/x-www-form-urlencoded";
                    using (var stream = await request.GetRequestStreamAsync()) {
                        stream.Write(requestBuffer, 0, requestBuffer.Length);
                    }

                    // 응답받기
                    var response = await request.GetResponseAsync();
                    if (response.ContentLength <= 0) {
                        throw new Exception();
                    }
                    response.Close();

                    // 성공
                    item.checkBox.CheckState = CheckState.Checked;
                } catch {
                    item.checkBox.CheckState = CheckState.Indeterminate;
                    if (webSocket.State == WebSocketState.Open) {
                        //await webSocket.CloseAsync(WebSocketCloseStatus.Empty, "", CancellationToken.None);
                    }
                }
            }
            MessageBox.Show("Click OK to exit.", "Finished", MessageBoxButtons.OK, MessageBoxIcon.Information);
            Close();
        }
    }
}
