/////////////////////////////////////
/////// ClientTool.cs
/////////////////////////////////////
/// 
/// Project „SWTGear“ 
/// 
/// 
/// $Author: franke, keller $: 
///
/// Copyright 2021, Physikalisch-Technische Bundesanstalt, Braunschweig & Berlin


using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.Linq;
using tracimPTB_SWTGear;

using System.Text;
using System.Net;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml.Schema;

namespace SWTGearClientTool
{
    /// <summary>
    /// This is an example class to mock a customers software under test. It should help to understand the usage of
    /// the TraCIM API.
    /// </summary>
    class ClientTool
    {

        private string dataDir = ".\\tracimTestClientData\\"; // "..\\..\\data/";
        private string BaseURL;
        public string TraCIMServer { get; set; }
        public string OrderKey { get; set; }
        public string ProcessKey { get; set; }
        public bool success = false;

        public static string proxyServer { get; set; }
        public static string proxyUser { get; set; }
        public static string proxyPassword { get; set; }

        public bool loadFreeData { get; set; }

        // version, vendor and customer information on tested software
        public string softwareName { get; set; }
        public string softwareVendor { get; set; }
        public string softwareVersion { get; set; }
        public string softwareRevision { get; set; }

        public decimal mpeLength { get; set; }
        public decimal mpeAngle { get; set; }
        public decimal mpeTaperOfPitchCone { get; set; }

        public string tracimTestMessageAsString;
        public tracimMessage tracimTestMessage;

        
        private tracimPTB_SWTGear.swtGearResultPackage TestResult;
     

        public string tracimResultAsXMLString;

        public string tracimValidationMessageAsString;
        public tracimMessage tracimValidationMessage;

        private DefaultResults DefaultResultsExampleData;


        public ClientTool()
        {
            // set values related to the used tracim-server and the API 
            BaseURL = "/tracim/api/";            
            TraCIMServer = "https://tracim.ptb.de";
            //BaseURL = "/tracim-server/api/";
            //TraCIMServer = "http://n191001:7780";



            OrderKey = "[Enter your order ID here]";
            proxyServer = "";
            proxyUser = "";
            proxyPassword = "";

            // set values related to the software under test
            softwareName = ""; 
            softwareVendor = ""; 
            softwareVersion = ""; 
            softwareRevision = ""; 
            softwareRevision = ""; 

            mpeLength = 0.001m;
          
      
            tracimTestMessageAsString = " ";

            // Test results for the free public test
            // This is needed to try the communication with the server, ect. without need of a gear evaluation software

            DefaultResultsExampleData = new DefaultResults();

            System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture;
        }



        /// <summary>
        /// Load test data from xml file
        /// </summary>
        /// <returns>none</returns>
        //  public void loadTest()
        public void loadTest()
        {
            
            success = false;

            string testDataXMLString = " ";

            FileStream fs = null;
            Stream myStream = null;
            OpenFileDialog openFileDialog = new OpenFileDialog();

            openFileDialog.InitialDirectory = System.Windows.Forms.Application.StartupPath + dataDir;
            openFileDialog.Filter = "xml files (*.xml)|*.xml|All files (*.*)|*.*";
            openFileDialog.FilterIndex = 1;
            //openFileDialog1.RestoreDirectory = true;

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    using (myStream)
                    {
                        fs = File.OpenRead(openFileDialog.FileName);
                        testDataXMLString = File.ReadAllText(openFileDialog.FileName);
                    }
                }
                catch (Exception ex)
                {
                    tracimTestMessageAsString = "Error: Could not read file from disk. Original error: " + ex.Message;
                    return;
                }
            }
            else
            {
                success = false;
                return;
            }

            try
            {

                XmlSerializer serializer = new XmlSerializer(typeof(tracimMessage));
                this.tracimTestMessage = (tracimMessage)serializer.Deserialize(fs);
                success = true;
                
                swtGearTestPackage testPackage = (swtGearTestPackage)tracimTestMessage.test;

                serializer = new XmlSerializer(typeof(gear_data_exchange_format));
                XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
                ns.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");


                string subDir = tracimTestMessage.process.key + "\\";
                Directory.CreateDirectory(dataDir + subDir);

               
                 //File.WriteAllText(dataDir + subDir + "testData.xml", FormatXml(tracimTestMessageAsString));
              
                foreach (swtGearTestPackageSwtGearTestData testData in testPackage.swtGearTestData)
                {
                    MemoryStream _ms = new MemoryStream();
                    XmlTextWriter _xmlTextWriter = new XmlTextWriter(_ms, Encoding.UTF8);
                    _xmlTextWriter.Formatting = Formatting.Indented;
                    serializer.Serialize(_xmlTextWriter, testData.gear_data_exchange_format, ns);
                    String gdeString = Encoding.UTF8.GetString(_ms.ToArray());

                    File.WriteAllText(dataDir + subDir + "gdeTest_" + testData.basicID + ".xml", gdeString);
                }


                ns = new XmlSerializerNamespaces();
                ns.Add("swtg-test", "http://tracim.ptb.de/swtgear/test");
                ns.Add("gde", "https://www.vdi.de/fileadmin/xml/2610");
                ns.Add("tracim", "http://tracim.ptb.de/tracim");
                ns.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");

                MemoryStream ms = new MemoryStream();
                XmlTextWriter xmlTextWriter = new XmlTextWriter(ms, Encoding.UTF8);
                xmlTextWriter.Formatting = Formatting.Indented;
                serializer = new XmlSerializer(typeof(tracimMessage));
                serializer.Serialize(xmlTextWriter, this.tracimTestMessage, ns);

                ms.Position = 0;

                // copy tracim message to string (e.g. for display on GUI)
                tracimTestMessageAsString = Encoding.UTF8.GetString(ms.ToArray());
                
            }

            catch (Exception e)
            {
                tracimTestMessageAsString = "<br /><br /> " + "Failed to read test data from file <br /><br /> " + e.ToString();
                System.Diagnostics.Debug.WriteLine(tracimTestMessageAsString);
                success = false;
            }
        }



        /// <summary>
        /// Perform the http request POST to get test data from the server
        /// </summary>
        /// <returns>none</returns>
        public async Task getTest()
        {

            success = false;
            try
            {

                if (OrderKey == null || OrderKey == "")
                    return;                

                Directory.CreateDirectory(dataDir);
                TraCIMServer = TraCIMServer.TrimEnd('/');
                HttpResponseMessage res = postRequest((TraCIMServer + BaseURL + "order/" + OrderKey + "/test"), "");
                MemoryStream ms = new MemoryStream();
                if (res.IsSuccessStatusCode)
                {
                    using (var httpStream = await res.Content.ReadAsStreamAsync().ConfigureAwait(false))
                    {
                        httpStream.CopyTo(ms);
                        ms.Position = 0;
                    }

                    XmlSerializer serializer = new XmlSerializer(typeof(tracimMessage));
                    this.tracimTestMessage = (tracimMessage)serializer.Deserialize(ms);
                    success = true;

                    // copy tracim message to string (e.g. for display on GUI)
                    tracimTestMessageAsString = Encoding.UTF8.GetString(ms.ToArray());

                    string subDir = tracimTestMessage.process.key + "\\";
                    Directory.CreateDirectory(dataDir + subDir);

                    File.WriteAllText(dataDir + subDir + "testData.xml", FormatXml(tracimTestMessageAsString));

                    
                    swtGearTestPackage testPackage = (swtGearTestPackage) tracimTestMessage.test;

                    serializer = new XmlSerializer(typeof(gear_data_exchange_format));
                    XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
                    ns.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
                    

                    foreach (swtGearTestPackageSwtGearTestData testData in testPackage.swtGearTestData)
                    {
                        ms = new MemoryStream();
                        XmlTextWriter xmlTextWriter = new XmlTextWriter(ms, Encoding.UTF8);
                        xmlTextWriter.Formatting = Formatting.Indented;
                        serializer.Serialize(xmlTextWriter, testData.gear_data_exchange_format, ns);
                        String gdeString = Encoding.UTF8.GetString(ms.ToArray());

                        File.WriteAllText(dataDir + subDir + "gdeTest_" + testData.basicID + ".xml", gdeString);
                    }
                    

                    /*
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(tracimTestMessageAsString);

                    XmlNamespaceManager nsm = new XmlNamespaceManager(xmlDoc.NameTable);
                    nsm.AddNamespace("gde", "https://www.vdi.de/fileadmin/xml/2610");

                    XmlNodeList gdeNodes = xmlDoc.SelectNodes("//gde:gear_data_exchange_format", nsm);



                    foreach (XmlNode gde in gdeNodes)
                    {
                        XmlDeclaration xml_declaration;


                        XmlDocument xml = new XmlDocument();
                        xml.LoadXml(gde.OuterXml);


                        xml_declaration = xml.CreateXmlDeclaration("1.0", "UTF-8", "yes");
                        XmlElement document_element = xml.DocumentElement;
                        xml.InsertBefore(xml_declaration, document_element);


                        xml.DocumentElement.SetAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
                        //xml.DocumentElement.SetAttribute("xsi", "http://www.w3.org/2001/XMLSchema-instance");
                        xml.DocumentElement.SetAttribute("xsi:schemaLocation", "https://www.vdi.de/fileadmin/xml/2610 https://www.vdi.de/fileadmin/xml/2610/GDE_3_2.xsd");
                        //xml.DocumentElement.SetAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema");
                        
                        //XmlAttribute attr = xml.CreateAttribute("xsi", "schemaLocation", "https://www.vdi.de/fileadmin/xml/2610");
                        //attr.Value = xml.DocumentElement.NamespaceURI + " " + "https://www.vdi.de/fileadmin/xml/2610/GDE_3_2.xsd";
                        //xml.DocumentElement.SetAttributeNode(attr);
                        

                        //String gdeString = FormatXml(xml.OuterXml);
                        
                        String gdeString = FormatXml(xml.InnerXml);
                        gdeString = gdeString.Replace("<gde:", "<");
                        gdeString = gdeString.Replace("</gde:", "</");
                        gdeString = gdeString.Replace(":gde", "");
                        gdeString = gdeString.Replace(" schemaLocation", " xsi:schemaLocation");

                        XmlNode basicIdNode = xml.SelectSingleNode("//gde:number_of_gear", nsm);
                        File.WriteAllText(dataDir + subDir + "gdeTest_" + basicIdNode.InnerText + "_var2.xml", gdeString);
                    }
                    */


                }

                else
                {
                    using (var httpStream = await res.Content.ReadAsStreamAsync().ConfigureAwait(false))
                    {
                        httpStream.CopyTo(ms);
                        ms.Position = 0;
                        // copy tracim message to string (e.g. for display on GUI)
                        tracimTestMessageAsString = Encoding.UTF8.GetString(ms.ToArray());
                        if (this.tracimResultAsXMLString == null)
                            this.tracimResultAsXMLString = "Failed to download test data";
                        File.WriteAllText(dataDir + "error_getTest.xml", FormatXml(tracimTestMessageAsString));
                    }
                }

            }
            catch (Exception e)
            {
                tracimTestMessageAsString = "<br /><br /> " + "Failed to collect test data from server <br /><br /> " + e.ToString();
                success = false;
            }
        }



        /// <summary>
        /// Evaluate the test package
        /// </summary>
        /// <remarks>This method iterates through the requested test package and evaluates the references parameters for each contained geometric element.</remarks>
        /// <returns>true if all evaluations have be successful, otherwise false</returns>
        public bool EvaluateResults()
        {
            try
            {                
                // allocate the storage for the test results

                TestResult = new swtGearResultPackage();                

                if (loadFreeData)
                {
                    TestResult.swtGearResultData = getFreeSWTGearResults();
                }
                else
                {
                    TestResult.swtGearResultData = loadSWTGearResults();
                }
     

                XmlSerializer serializer = new XmlSerializer(typeof(tracimPTB_SWTGear.swtGearResultPackage));

                TestResult.processKey = ProcessKey; // tracimTestMessage.process.key;
                TestResult.softwareName = softwareName;
                TestResult.softwareVendor = softwareVendor;
                TestResult.softwareVersion = softwareVersion;
                TestResult.softwareRev = softwareRevision;

                // mpe in mm:
                TestResult.mpe= Convert.ToDouble(mpeLength / 1.0e3m);
               
                //  Serialize the object to Xml with UTF8 encoding
                
                XmlSerializerNamespaces ns = new XmlSerializerNamespaces();                
                ns.Add("swtg-result", "http://tracim.ptb.de/swtgear/result");
                ns.Add("gde", "https://www.vdi.de/fileadmin/xml/2610");
                ns.Add("tracim", "http://tracim.ptb.de/tracim");
                ns.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
                

                MemoryStream ms = new MemoryStream();
                XmlTextWriter xmlTextWriter = new XmlTextWriter(ms, Encoding.UTF8);
                xmlTextWriter.Formatting = Formatting.Indented;
                serializer.Serialize(xmlTextWriter, TestResult, ns);

                // copy calculated results to xml file (only needed for debugging)
                string subDir = ProcessKey + "\\";
                Directory.CreateDirectory(dataDir + subDir);

                ms.Position = 0;
                FileStream dbgFile = File.Create(dataDir + subDir + "calculated_result.xml");
                ms.CopyTo(dbgFile);
                dbgFile.Close();
                // copy result message to string (e.g. for display on GUI)
                tracimResultAsXMLString = Encoding.UTF8.GetString(ms.ToArray());
            }
            catch (Exception e)
            {
                tracimResultAsXMLString = "Failed to import test results <br /><br />" + e.ToString();
                return false;
            }

            return true;
        }

        /// <summary>
        ///  Perform the http request POST to send the evaluated reference parameters to the server
        /// </summary>
        /// <returns>none</returns>
        public async Task postResults()
        {

            success = false;
            this.tracimValidationMessage = new tracimMessage();
            tracimValidationMessageAsString = "";

            string subDir = ProcessKey + "\\";
            Directory.CreateDirectory(dataDir + subDir);

            TraCIMServer = TraCIMServer.TrimEnd('/');

            try
            {

                TestResult.processKey = ProcessKey; // tracimTestMessage.process.key;
                TestResult.softwareName = softwareName;
                TestResult.softwareVendor = softwareVendor;
                TestResult.softwareVersion = softwareVersion;
                TestResult.softwareRev = softwareRevision;

                // mpe in mm:
                TestResult.mpe = Convert.ToDouble(mpeLength / 1.0e3m);
                
                XmlSerializer resultserializer = new XmlSerializer(typeof(tracimPTB_SWTGear.swtGearResultPackage));

                MemoryStream ms1 = new MemoryStream();
                
                XmlTextWriter xmlTextWriter = new XmlTextWriter(ms1, Encoding.UTF8);
                xmlTextWriter.Formatting = Formatting.Indented;
                resultserializer.Serialize(xmlTextWriter, TestResult);
                
                ms1.Position = 0;
                tracimResultAsXMLString = Encoding.UTF8.GetString(ms1.ToArray());

                MemoryStream ms = new MemoryStream();
                XmlSerializer serializer = new XmlSerializer(typeof(tracimMessage));

                HttpResponseMessage res = postRequest((TraCIMServer + BaseURL + "test/" + ProcessKey), tracimResultAsXMLString);

                if (res.IsSuccessStatusCode)
                {
                    using (var httpStream = await res.Content.ReadAsStreamAsync().ConfigureAwait(false))
                    {
                        httpStream.CopyTo(ms);
                        ms.Position = 0;
                        tracimValidationMessageAsString = Encoding.UTF8.GetString(ms.ToArray());
                        File.WriteAllText(dataDir + subDir + "validation_result.xml", FormatXml(Encoding.UTF8.GetString(ms.ToArray())));
                        this.tracimValidationMessage = serializer.Deserialize(ms) as tracimMessage;

                        success = true;
                    }
                }
                else
                {
                    using (var httpStream = await res.Content.ReadAsStreamAsync().ConfigureAwait(false))
                    {
                        httpStream.CopyTo(ms);
                        ms.Position = 0;
                        tracimValidationMessageAsString = Encoding.UTF8.GetString(ms.ToArray());
                        //tracimValidationMessageAsString = "<br />" + tracimValidationMessageAsString + "<br />";                       
                        File.WriteAllText(dataDir + subDir + "validation_error.xml", FormatXml(Encoding.UTF8.GetString(ms.ToArray())));
                        this.tracimValidationMessage = serializer.Deserialize(ms) as tracimMessage;
                        
                        try
                        {
                            if (tracimValidationMessage.error != null)
                                tracimValidationMessageAsString = tracimValidationMessage.error.description.Replace("\n","<br />");
                        }
                        catch (Exception ex)
                        {
                            tracimValidationMessageAsString = tracimValidationMessageAsString + "<br /><br />" + ex.ToString();
                        }
                    }
                }
            }
            catch (Exception e)
            {
                tracimValidationMessageAsString = "<br />" + tracimValidationMessageAsString + "<br /><br />" + e.ToString();

                System.Diagnostics.Debug.WriteLine(tracimValidationMessageAsString);
                success = false;
            }

            if (success)
                try
                {
                    string PDFFileName = "Report_" + this.ProcessKey + ".pdf";
                    FileStream pdfStream = new FileStream(dataDir + subDir + PDFFileName, FileMode.Create, FileAccess.Write);
                    byte[] myReportPdf = Convert.FromBase64String(this.tracimValidationMessage.validation.reportPDF);
                    pdfStream.Write(myReportPdf, 0, myReportPdf.Length);
                    pdfStream.Close();
                    System.Diagnostics.Process.Start(dataDir + subDir + PDFFileName);

                    File.WriteAllText(dataDir + subDir + "report.txt", this.tracimValidationMessage.validation.report);

                }
                catch (Exception e)
                {
                    tracimValidationMessageAsString = tracimValidationMessageAsString + "<br /><br />" + e.ToString();
                }
            

        }

        /// <summary>
        /// helper function for posting http requests
        /// </summary>
        /// <param name="url">Full URL of the requested resource one the server</param>
        /// <param name="data">Data to post (can be empty string)</param>
        /// <returns>Response of the server </returns>
        static HttpResponseMessage postRequest(string url, string data)
        {

            ICredentials credentials = new NetworkCredential(proxyUser.Trim(), proxyPassword.Trim());

            HttpClient cli;

            if (proxyServer.Trim().Equals(""))
                cli = new HttpClient();
            else
            {
                var httpClientHandler = new HttpClientHandler
                {
                    Proxy = new WebProxy(proxyServer.Trim(), true, null, credentials),
                    UseProxy = true
                };

                cli = new HttpClient(httpClientHandler);
            }

           
            cli.BaseAddress = new Uri(url);

                var req = new HttpRequestMessage(HttpMethod.Post, "");

                req.Content = new StringContent(data);
                req.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/xml");
                var res = cli.SendAsync(req, HttpCompletionOption.ResponseHeadersRead);
                res.Wait();
                return res.Result;

                throw new Exception("Failed loading test date");
        }



      

        /// <summary>
        /// get build-in results for the free test date
        public tracimPTB_SWTGear.swtGearResultPackageSwtGearResultData[] getFreeSWTGearResults()
        {

            return DefaultResultsExampleData.DefaultExampleResults.swtGearResultData;

            throw new Exception("Build-in results for free test not found");

        }



        /// <summary>
        /// get build-in results for the free test date
        public tracimPTB_SWTGear.swtGearResultPackageSwtGearResultData[] loadSWTGearResults()
        {

            List<swtGearResultPackageSwtGearResultData> resultList = new List<swtGearResultPackageSwtGearResultData>();

            string subDir = ProcessKey + "\\";

            string[] dirs = Directory.GetFiles(dataDir + subDir, "gdeResult_S*.xml");

            Console.WriteLine("The number of found files: ", dirs.Length);
            foreach (string dir in dirs)
            {
                Console.WriteLine(dir);

                //string basicID = String.Format("S{0:00}", elementCount);


                Match m = Regex.Match(dir, @"\bgdeResult_(S[0-9][0-9]).xml\b", RegexOptions.IgnoreCase);
                if (m.Success)
                {

                    String filename;
                    String basicID;
                    if (m.Groups.Count == 2)
                    {

                        Group g = m.Groups[0];
                        CaptureCollection cc = g.Captures;
                        Capture c = cc[0];
                        filename = c.ToString();

                        g = m.Groups[1];
                        cc = g.Captures;
                        c = cc[0];
                        basicID = c.ToString();

                        tracimPTB_SWTGear.swtGearResultPackageSwtGearResultData result = new tracimPTB_SWTGear.swtGearResultPackageSwtGearResultData();
                        result.basicID = basicID;

                        using (StreamReader file = new StreamReader(dataDir + subDir + "gdeResult_" + basicID + ".xml"))
                        {
                            XmlSerializer reader = new XmlSerializer(typeof(tracimPTB_SWTGear.gear_data_exchange_format));
                            gear_data_exchange_format gde = (gear_data_exchange_format)reader.Deserialize(file);
                            result.gear_data_exchange_format = gde;
                            resultList.Add(result);
                        }
                    }
                }
            }


            return resultList.ToArray();


            throw new Exception("Error reading results");

        }



        /// <summary>
        /// Function to format xml strings (used to add linebreaks)
        static string FormatXml(string xml)
        {
            try
            {
                XDocument doc = XDocument.Parse(xml);
                return doc.Declaration.ToString() + "\n" + doc.ToString();
            }
            catch (Exception)
            {
                return xml;
              }
        }

        /*         
        /// <summary>
        /// Another function to format xml strings (used to add linebreaks)
        static string PrettyXml(string xml)
        {
            var stringBuilder = new StringBuilder();

            var element = XElement.Parse(xml);

            var settings = new XmlWriterSettings();
            settings.OmitXmlDeclaration = true;
            settings.Indent = true;
            settings.NewLineOnAttributes = true;

            using (var xmlWriter = XmlWriter.Create(stringBuilder, settings))
            {
                element.Save(xmlWriter);
            }

            return stringBuilder.ToString();
        }
        */

    }
}






