namespace RIoT
{
    using System;
    using System.Diagnostics;
    using System.Collections;
    using System.Net.Security;
    using System.Net.Sockets;
    using System.Security.Authentication;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;
    using System.Threading;
    
    public class SslTcpClient
    {
        static string aliasCert, aliasKey;

        public static void RunClient(string _aliasCert, string _aliasKey)
        {
            aliasCert = _aliasCert;
            aliasKey = _aliasKey;
            Thread tt = new Thread(new ThreadStart(RunClientX));
            tt.Start();
        }
        public static void RunClientX()
        {

        
            Thread.Sleep(3000);

            // The PFX file is created by this utility, but it's just a re-packaging 
            // of the Alias Key pair and the the certificate generated by RIoT

            string tempCertFile = "AliasCert.PFX";
            string password = "";
            Helpers.MakePFXFile(aliasCert, aliasKey, tempCertFile, password);
            var clientCert = new X509Certificate2(tempCertFile);

            var certs = new X509Certificate2Collection(new X509Certificate2[] {
                    clientCert                
                                });

            // connect to server
            TcpClient client = new TcpClient("127.0.0.1", 5556);
            //Helpers.NotifyClient("Client connected.");

            // Create an SSL stream and connect.
            SslStream sslStream = new SslStream(client.GetStream(), false,
                new RemoteCertificateValidationCallback(ValidateServerCertificate), null);

            try
            {

                sslStream.AuthenticateAsClient("RIoT Server CA", certs, SslProtocols.Tls, false);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine("Exception: {0}", e.Message);
                if (e.InnerException != null)
                {
                    Helpers.Notify($"Inner exception: {e.InnerException.Message}", true);
                }
                Helpers.Notify("Authentication failed - closing the connection.");
                client.Close();
                return;
            }

            byte[] messsage = Encoding.UTF8.GetBytes("GET /ABC/123");
            sslStream.Write(messsage);
            sslStream.Flush();

            // Read message from the server.
            string serverMessage = ReadMessage(sslStream);
            Helpers.Notify($"Client received: {serverMessage}");

            client.Close();
            Helpers.Notify("Client closed.");
        }

        internal bool FakeDRSServerHandshake(string devId)
        {
            string tempCertFile = "AliasCert.PFX";
            string password = "";
            Helpers.MakePFXFile(Program.ToPath(Program.AliasCert), Program.ToPath(Program.AliasKey), tempCertFile, password);
            var clientCert = new X509Certificate2(tempCertFile);

            var certs = new X509Certificate2Collection(new X509Certificate2[] { clientCert });
            // connect to server
            TcpClient client = new TcpClient("127.0.0.1", 5556);
            // Create an SSL stream and connect.
            SslStream sslStream = new SslStream(client.GetStream(), false,
                new RemoteCertificateValidationCallback(ValidateServerCertificate), null);

            try
            {

                sslStream.AuthenticateAsClient("RIoT Server CA", certs, SslProtocols.Tls, false);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine("Exception: {0}", e.Message);
                if (e.InnerException != null)
                {
                    Helpers.Notify($"Inner exception: {e.InnerException.Message}", true);
                }
                Helpers.Notify("Authentication failed - closing the connection.");
                client.Close();
                return false;
            }

            sslStream.ReadTimeout = 10000;
            sslStream.WriteTimeout= 10000;


            SslTcpServer.SendMessage(sslStream, devId);
            string messageFromServer = SslTcpServer.ReadMessage(sslStream);
            /*
            byte[] message = Encoding.UTF8.GetBytes(devId);
            byte[] len = new byte[] { (byte) message.Length };
            sslStream.Write(len, 0, 1);
            sslStream.Write(message,0, message.Length);
            sslStream.Flush();
            byte[] buf = new byte[1024];
            int numRead = sslStream.Read(buf, 0, 1);
            if(numRead!=1)
            {
                Helpers.Notify("TLSClient got a bad message from the server");
            }
            int pos = 0;
            int lenX = (int) buf[0];
            while (true)
            {
                numRead = sslStream.Read(buf, pos, lenX - pos);
                pos += numRead;
                if (pos == lenX) break;
            }
            string serverMessage = Encoding.UTF8.GetString(buf, 0, lenX);
            */
            Helpers.Notify($"Client received: {messageFromServer}");
            Thread.Sleep(30);

            client.Close();
            Helpers.Notify("Client closed.");
            return true;
        }



    


    private static string ReadMessage(SslStream sslStream)
        {
            byte[] buffer = new byte[2048];
            StringBuilder messageData = new StringBuilder();
            int bytes = -1;
            try
            {
                do
                {
                    bytes = sslStream.Read(buffer, 0, buffer.Length);
                    Decoder decoder = Encoding.UTF8.GetDecoder();
                    char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
                    decoder.GetChars(buffer, 0, bytes, chars, 0);
                    messageData.Append(chars);
                    // Check for EOF.
                    if (messageData.ToString().IndexOf("<EOF>") != -1)
                    {
                        break;
                    }
                } while (bytes != 0);

            }
            catch(Exception e)
            {
                Debug.WriteLine("Stream read error:" + e.ToString());
                throw;

            }
            return messageData.ToString();
        }


        public static bool ValidateServerCertificate(
            object sender,
            X509Certificate certificate,
            X509Chain chain,
            SslPolicyErrors sslPolicyErrors)
        {
            if (sslPolicyErrors == SslPolicyErrors.None)
                return true;
            // For this test code, any server-cert is allowed
            return true;
        }


    }

}
