using System;
using System.Globalization;
using System.IO;
using System.IO.Pipes;
using System.Text;
using Newtonsoft.Json;

#if !LINUX
using CefSharp;
#endif


namespace VRCX
{
    public class IPCClient
    {
        private static readonly UTF8Encoding noBomEncoding = new UTF8Encoding(false, false);
        private readonly NamedPipeServerStream _ipcServer;
        private readonly byte[] _recvBuffer = new byte[1024 * 8];
        private readonly MemoryStream memoryStream;
        private readonly byte[] packetBuffer = new byte[1024 * 1024];
        private readonly Newtonsoft.Json.JsonSerializer serializer = new Newtonsoft.Json.JsonSerializer();
        private string _currentPacket;

        public IPCClient(NamedPipeServerStream ipcServer)
        {
            memoryStream = new MemoryStream(packetBuffer);
            serializer.Culture = CultureInfo.InvariantCulture;
            serializer.Formatting = Formatting.None;

            _ipcServer = ipcServer;
        }

        public void BeginRead()
        {
            _ipcServer.BeginRead(_recvBuffer, 0, _recvBuffer.Length, OnRead, _ipcServer);
        }

        public void Send(IPCPacket ipcPacket)
        {
            try
            {
                memoryStream.Seek(0, SeekOrigin.Begin);
                using (var streamWriter = new StreamWriter(memoryStream, noBomEncoding, 65535, true))
                using (var writer = new JsonTextWriter(streamWriter))
                {
                    serializer.Serialize(writer, ipcPacket);
                    streamWriter.Write((char)0x00);
                    streamWriter.Flush();
                }

                var length = (int)memoryStream.Position;
                _ipcServer?.BeginWrite(packetBuffer, 0, length, OnSend, null);
            }
            catch
            {
                IPCServer.Clients.TryRemove(this, out _);
            }
        }

        private void OnRead(IAsyncResult asyncResult)
        {
            try
            {
                var bytesRead = _ipcServer.EndRead(asyncResult);

                if (bytesRead <= 0)
                {
                    IPCServer.Clients.TryRemove(this, out _);
                    _ipcServer.Close();
                    return;
                }

                _currentPacket += Encoding.UTF8.GetString(_recvBuffer, 0, bytesRead);

                if (_currentPacket[_currentPacket.Length - 1] == (char)0x00)
                {
                    var packets = _currentPacket.Split((char)0x00);

                    foreach (var packet in packets)
                    {
                        if (string.IsNullOrEmpty(packet))
                            continue;

#if !LINUX
                        if (MainForm.Instance?.Browser != null && !MainForm.Instance.Browser.IsLoading && MainForm.Instance.Browser.CanExecuteJavascriptInMainFrame)
                            MainForm.Instance.Browser.ExecuteScriptAsync("window?.$pinia?.vrcx.ipcEvent", packet);
#endif
                    }

                    _currentPacket = string.Empty;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }

            BeginRead();
        }

        public static void OnSend(IAsyncResult asyncResult)
        {
            var ipcClient = (NamedPipeClientStream)asyncResult.AsyncState;
            ipcClient?.EndWrite(asyncResult);
        }

        public static void Close(IAsyncResult asyncResult)
        {
            var ipcClient = (NamedPipeClientStream)asyncResult.AsyncState;
            ipcClient?.EndWrite(asyncResult);
            ipcClient?.Close();
        }
    }
}