/////////////////////////////////////
/////// VerRoVerClientTool.cs
/////////////////////////////////////
/// 
/// Project „VerRoVer“ 
/// 
/// $Rev: 265 $: Revision of last commit
/// $Author: franke, keller $: 
///
/// Copyright 2017, 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_VerRoVer;
using System.Text;
using System.Net;

namespace VerRoVerClientTool
{
    /// <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 MPE { get; set; }

        public string tracimTestMessageAsString;
        public tracimMessage tracimTestMessage;

        private tracimPTB_VerRoVer.verRoVerResultPackage 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";
            
            OrderKey = ""; //[Enter your order ID here]";
            proxyServer = "";
            proxyUser = "";
            proxyPassword = "";

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

            MPE = 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 VerRoVer caculation 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 = true;
                return;
            }

            try
            {

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

                /*
                XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
                ns.Add("verrover", "http://tracim.ptb.de/verrover/test");
                ns.Add("verrover", "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, this.tracimTestMessage);

                ms.Position = 0;

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


                string info = "Order key:       " + tracimTestMessage.order.key + "\n";
                info = info + "Process key:     " + tracimTestMessage.process.key + "\n";
                info = info + "Creation Date:   " + tracimTestMessage.order.creationDate.ToLongDateString() + " " + tracimTestMessage.order.creationDate.ToLongTimeString() + "\n";
                info = info + "Expiration Date: " + tracimTestMessage.order.expirationDate.ToLongDateString() + " " + tracimTestMessage.order.expirationDate.ToLongTimeString() + "\n";


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

                File.WriteAllText(dataDir + subDir + "processInfo.txt", info);
                tracimPTB_VerRoVer.verRoVerTestPackage myTest = (tracimPTB_VerRoVer.verRoVerTestPackage)tracimTestMessage.test;

                int nElement = myTest.testElement.Length;
                for (int k = 0; k < nElement; k++)
                {

                    double[][] data = myTest.testElement[k].measurementResults;

                    string matrix = "";
                    for (int i = 0; i < data[0].Length; i++)
                    {
                        for (int j = 0; j < data.Length; j++)
                            matrix = matrix + String.Format("{0,20:N16}", data[j][i]);
                        matrix = matrix + "\n";
                    }

                    File.WriteAllText(dataDir + subDir + myTest.testElement[k].basicID + "_measurementResults.txt", matrix);

                    string pos = "";
                    int[] relPos = myTest.testElement[k].relPos;
                    for (int r = 0; r < relPos.Length; r++)
                        pos = pos + relPos[r] + " ";
                    pos = pos + "\n";

                    File.WriteAllText(dataDir + subDir + myTest.testElement[k].basicID + "_relPos.txt", pos);

                }

            }
            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());


                    tracimPTB_VerRoVer.verRoVerTestPackage myTest = (tracimPTB_VerRoVer.verRoVerTestPackage)tracimTestMessage.test;
                    int nElement = myTest.testElement.Length;

                    string info = "Order key:       " + tracimTestMessage.order.key + "\n";
                    info = info + "Process key:     " + tracimTestMessage.process.key + "\n";
                    info = info + "Creation Date:   " + tracimTestMessage.order.creationDate.ToLongDateString() + " " + tracimTestMessage.order.creationDate.ToLongTimeString() + "\n";
                    info = info + "Expiration Date: " + tracimTestMessage.order.expirationDate.ToLongDateString() + " " + tracimTestMessage.order.expirationDate.ToLongTimeString() + "\n";

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

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


                    for (int k = 0; k < nElement; k++)
                    {
                        double[][] data = myTest.testElement[k].measurementResults;

                        string matrix = "";
                        for (int i = 0; i < data[0].Length; i++)
                        {
                            for (int j = 0; j < data.Length; j++)
                                matrix = matrix + String.Format("{0,20:N16}", data[j][i]);
                            matrix = matrix + "\n";
                        }

                        File.WriteAllText(dataDir + subDir + myTest.testElement[k].basicID + "_measurementResults.txt", matrix);

                        string pos = "";
                        int[] relPos = myTest.testElement[k].relPos;
                        for (int r = 0; r < relPos.Length; r++)
                            pos = pos + relPos[r] + " ";
                        pos = pos + "\n";

                        File.WriteAllText(dataDir + subDir + myTest.testElement[k].basicID + "_relPos.txt", pos);
                    }
                }

                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
            {
                int nElement = 10;

                // allocate the storage for the test results
                TestResult = new tracimPTB_VerRoVer.verRoVerResultPackage();
                TestResult.results = new tracimPTB_VerRoVer.verRoVerResult[nElement];

                // iterate through all elements
                int elementCount = 0;
                for (int k = 1; k <= nElement; k++)
                {

                    string basicID = String.Format("V{0:00}", k);

                    tracimPTB_VerRoVer.verRoVerResult ElementResult;

                    // Calculate or load result:
                    if (loadFreeData)
                        ElementResult = getFreeVerRoVerResult(basicID);
                    else
                        ElementResult = getVerRoVerResult(basicID);

                    TestResult.results[elementCount] = ElementResult;
                    elementCount++;
                }


                XmlSerializer serializer = new XmlSerializer(typeof(tracimPTB_VerRoVer.verRoVerResultPackage));

                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(MPE / 1.0e3m);
  
                //  Serialize the object to Xml with UTF8 encoding
                /*
                XmlSerializerNamespaces ns = new XmlSerializerNamespaces();                
                ns.Add("verrover", "http://tracim.ptb.de/verrover/test");
                ns.Add("verrover", "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);
                serializer.Serialize(xmlTextWriter, TestResult);

                // 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(MPE / 1.0e3m);

                XmlSerializer resultserializer = new XmlSerializer(typeof(tracimPTB_VerRoVer.verRoVerResultPackage));

                MemoryStream ms1 = new MemoryStream();
                
                XmlTextWriter xmlTextWriter = new XmlTextWriter(ms1, Encoding.UTF8);
                xmlTextWriter.Formatting = Formatting.Indented;
                //serializer.Serialize(xmlTextWriter, TestResult, ns);
                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);
                }
                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_VerRoVer.verRoVerResult getFreeVerRoVerResult(string basicID)
        {

            int resultCount = 0;
            foreach (tracimPTB_VerRoVer.verRoVerResult exampleResult in DefaultResultsExampleData.DefaultExampleResults.results)
            {
                if (exampleResult.basicID.Equals(basicID))
                {
                    return exampleResult;
                }
                resultCount++;
            }

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

        }


        /// <summary>
        /// read results from txt-files
        public tracimPTB_VerRoVer.verRoVerResult getVerRoVerResult(string basicID)
        {
            string subDir = ProcessKey + "\\";

            verRoVerResult result = new verRoVerResult();

            result.basicID = basicID;

            var allRows = File
              .ReadLines(dataDir + subDir + basicID + "_cumulativePitchDevA.txt")
              .Where(line => !string.IsNullOrWhiteSpace(line))
              .Select(double.Parse).ToList();

            var sumA = new double[allRows.Count];
            for (int row = 0; row != allRows.Count; row++)
                sumA[row] = allRows[row];

            result.cumulativePitchDevA = sumA;

            allRows = File
                .ReadLines(dataDir + subDir + basicID + "_cumulativePitchDevB.txt")
                .Where(line => !string.IsNullOrWhiteSpace(line))
                .Select(double.Parse).ToList();

            var sumB = new double[allRows.Count];
            for (int row = 0; row != allRows.Count; row++)
                sumB[row] = allRows[row];

            result.cumulativePitchDevB = sumB;

            allRows = File
              .ReadLines(dataDir + subDir + basicID + "_cumulativePitchDevC.txt")
              .Where(line => !string.IsNullOrWhiteSpace(line))
              .Select(double.Parse).ToList();

            var sumC = new double[allRows.Count];
            for (int row = 0; row != allRows.Count; row++)
                sumC[row] = allRows[row];

            result.cumulativePitchDevC = sumC;


            allRows = File
              .ReadLines(dataDir + subDir + basicID + "_singlePitchDevA.txt")
              .Where(line => !string.IsNullOrWhiteSpace(line))
              .Select(double.Parse).ToList();

            var singleA = new double[allRows.Count];
            for (int row = 0; row != allRows.Count; row++)
                singleA[row] = allRows[row];

            result.singlePitchDevA = singleA;

            allRows = File
                .ReadLines(dataDir + subDir + basicID + "_singlePitchDevB.txt")
                .Where(line => !string.IsNullOrWhiteSpace(line))
                .Select(double.Parse).ToList();

            var singleB = new double[allRows.Count];
            for (int row = 0; row != allRows.Count; row++)
                singleB[row] = allRows[row];

            result.singlePitchDevB = singleB;

            allRows = File
              .ReadLines(dataDir + subDir + basicID + "_singlePitchDevC.txt")
              .Where(line => !string.IsNullOrWhiteSpace(line))
              .Select(double.Parse).ToList();

            var singleC = new double[allRows.Count];
            for (int row = 0; row != allRows.Count; row++)
                singleC[row] = allRows[row];

            result.singlePitchDevC = singleC;


            allRows = File
                .ReadLines(dataDir + subDir + basicID + "_standardDeviations.txt")
                .Where(line => !string.IsNullOrWhiteSpace(line))
                .Select(double.Parse).ToList();

            var sigma = new double[allRows.Count];
            for (int row = 0; row != allRows.Count; row++)
                sigma[row] = allRows[row];

            result.sigma = sigma[0];
            result.sigmaCumulativePitchDevA = sigma[1];
            result.sigmaSinglePitchDevA = sigma[2];

            return result;


        }

        /// <summary>
        /// Function to format xml strings (used to add linebreaks)
        static string FormatXml(string xml)
        {
            try
            {
                XDocument doc = XDocument.Parse(xml);
                return 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();
        }
        */

    }
}






