//-----------------------------------------------------------------------
// <copyright file="ServerExtension.cs" company="Beckhoff Automation GmbH & Co. KG">
//     Copyright (c) Beckhoff Automation GmbH & Co. KG. All Rights Reserved.
// </copyright>
//-----------------------------------------------------------------------


using Newtonsoft.Json;
using System;
using System.IO;
using System.Reflection;
using System.Threading;
using TcHmiSrv.Core;
using TcHmiSrv.Core.General;
using TcHmiSrv.Core.Listeners;
using TcHmiSrv.Core.Tools.Management;
using ADSTestDriver;
using ADSTestDriver;

namespace ServerExtension
{
    // Represents the default type of the TwinCAT HMI server extension.
    public class ServerExtension : IServerExtension
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private readonly RequestListener requestListener = new RequestListener();
        private string requestFileName = "";
        private ADSTestDriverClass clientDriver = null;

        /*
        private TcAdsClient clientDriver;
        private Thread watcherThread;
        private object watcherLock = new object();
        private AutoResetEvent adsIComplete = new AutoResetEvent(false);
        private bool initDone = false;
        private volatile bool runWatcherThread = true;
        */

        /*
        GETTING STARTED

        The recommended way to get started is to look at a few of the sample extensions that are available on GitHub:
        https://github.com/Beckhoff/TF2000_Server_Samples

        The full documentation for the extension API can be found in the Beckhoff Information System:
        https://infosys.beckhoff.com/english.php?content=../content/1033/te2000_tc3_hmi_engineering/10591698827.html

        An offline version of this documentation is available at this path:
        %TWINCAT3DIR%..\Functions\TE2000-HMI-Engineering\Infrastructure\TcHmiServer\docs\TcHmiSrvExtNet.Core.Documentation.chm
        */

        // Called after the TwinCAT HMI server loaded the server extension.
        public ErrorValue Init()
        {

            this.requestListener.OnRequest += this.OnRequest;
            return ErrorValue.HMI_SUCCESS;
        }

        /*
        private void WatcherTimerCallback()//object arg)
        {
            while (runWatcherThread)
            {
                adsIComplete.Set();
                Thread.Sleep(1000);
                if (initDone)
                {
                    while (clientDriver == null)
                    {
                        CreateClient();
                        if (clientDriver == null)
                            Thread.Sleep(1000);
                    }

                }
            }
        }

        */
        // Called when a clientDriver requests a symbol from the domain of the TwinCAT HMI server extension.
        private void OnRequest(object sender, TcHmiSrv.Core.Listeners.RequestListenerEventArgs.OnRequestEventArgs e)
        {
            try
            {
                e.Commands.Result = ServerExtensionErrorValue.ServerExtensionSuccess;
                foreach (Command command in e.Commands)
                {
                    try
                    {
                        // Use the mapping to check which command is requested
                        switch (command.Mapping)
                        {

                            case "GetCurrentTestCases": // Get the all test case designed in a json file for example, valve_test.json, motor_test.json etc, List of json from a file
                                var ss = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                                //log.InfoFormat("Calling CallMethodforLog i =  {0}", ss);

                                if (command.IsSet == true && command.WriteValue != null && clientDriver != null)
                                {
                                    requestFileName = command.WriteValue;
                                    string json = JsonConvert.SerializeObject(clientDriver.GetCurrentTestCases(requestFileName));
                                    command.ReadValue = json;
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    command.ResultString = "ReadValue1 '" + command.Mapping + "' not handled.";
                                }
                                break;

                            //save single root object to memory. The object constructed based on the HMI
                            case "UpdateSingleRootObject": 
                                if (command.IsSet == true && command.WriteValue != null && clientDriver != null)
                                {
                                    TestFileContent myroot1 = JsonConvert.DeserializeObject<TestFileContent>(command.WriteValue);
                                    int ret = clientDriver.UpdateSingleRootObject(myroot1);
                                    command.ReadValue = ret;
                                    if (ret == 0)
                                    {
                                        command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                        command.ResultString = "UpdateSingleRootObject '" + command.Mapping + "' has been handled.";
                                    }
                                    else
                                    {
                                        command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionFail;
                                        command.ResultString = "UpdateSingleRootObject '" + command.Mapping + "' has been handled by error.";
                                    }
                                }
                                break;

                            //save single root object to memory untill it is saved to file. All tags are removed from the found tag list, which is read back
                            case "SaveSingleFile":
                                if (command.IsSet == true && command.WriteValue != null && clientDriver != null)
                                {
                                    TestFileContent myroot2 = JsonConvert.DeserializeObject<TestFileContent>(command.WriteValue);
                                    int ret = clientDriver.SaveSingleFile(myroot2);
                                    command.ReadValue = ret;
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    command.ResultString = "SaveSingleFile '" + command.Mapping + "' has been handled.";
                                }
                                break;

                                
                            case "DeleteSingleTestFile": //Delete single test case file and restore the list
                                if (command.IsSet == true && command.WriteValue != null && clientDriver != null)
                                {                                    
                                    var resOut = clientDriver.DeleteSingleTestFile(command.WriteValue);
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    command.ResultString = "DeleteSingleTestFile '" + command.Mapping + "' has been handled.";
                                    command.ReadValue = resOut;
                                }
                                break;

                            case "UpdateSingleOutput": //save single root object to memory untill it is saved to file. All tags are removed from the found tag list, which is read back
                                if (command.IsSet == true && command.WriteValue != null && clientDriver != null)
                                {
                                    Matchingoutput outputItem = JsonConvert.DeserializeObject<Matchingoutput>(command.WriteValue);
                                    var resOut = clientDriver.UpdateSingleOutput(outputItem);
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    command.ResultString = "UpdateSingleOutput '" + command.Mapping + "' has been handled.";
                                    command.ReadValue = resOut;
                                }
                                break;
                            case "UpdateSingleInput": //save single root object to memory untill it is saved to file. All tags are removed from the found tag list, which is read back
                                if (command.IsSet == true && command.WriteValue != null && clientDriver != null)
                                {
                                    Affectedinput inputItem = JsonConvert.DeserializeObject<Affectedinput>(command.WriteValue);
                                    var ret = clientDriver.UpdateSingleInput(inputItem);
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    command.ResultString = "UpdateSingleInput '" + command.Mapping + "' has been handled.";
                                    command.ReadValue = ret;
                                }
                                break;
                            case "UpdateSingleAnalog": //save single root object to memory untill it is saved to file. All tags are removed from the found tag list, which is read back
                                if (command.IsSet == true && command.WriteValue != null && clientDriver != null)
                                {
                                    Analogdevice analogItem = JsonConvert.DeserializeObject<Analogdevice>(command.WriteValue);
                                    var retAn = clientDriver.UpdateSingleAnalog(analogItem);
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    command.ResultString = "UpdateSingleAnalog '" + command.Mapping + "' has been handled.";
                                    command.ReadValue = retAn;
                                }
                                break;

                            case "GetTestCaseFileNames": // get all the test case file name as list valve_test.json as valve test (list of string)
                                //var location = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                                //log.InfoFormat("Calling CallMethodforLog i =  {0}", location);

                                if (command.IsSet == true && clientDriver != null)
                                {
                                    string json = JsonConvert.SerializeObject(clientDriver.GetTestCaseFileNames());
                                    command.ReadValue = json;
                                    log.InfoFormat("Calling GetTestCaseFileNames allfile names  =  {0}", json);
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    command.ResultString = "ReadValue1 '" + command.Mapping + "' not handled.";
                                }
                                break;

                            case "GetInputCase": // Get all the input cases from a file (which was created in previous time by scanning it)
                                if (clientDriver != null)
                                {
                                    //requestFileName = command.WriteValue;
                                    string json = JsonConvert.SerializeObject(clientDriver.GetInputCase());
                                    command.ReadValue = json;
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    command.ResultString = "ReadValue1 '" + command.Mapping + "' not handled.";
                                }
                                break;

                            case "GetOutputCase": // Get all the output tags from a file (which was created in previous time by scanning it)
                                if (clientDriver != null)
                                {
                                    string json = JsonConvert.SerializeObject(clientDriver.GetOutputCase());
                                    command.ReadValue = json;
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    command.ResultString = "GetOutputCase '" + command.Mapping + "' not handled.";
                                }
                                break;

                            case "GetAnalogCase": // Get all the analog tags from a file (which was created in previous time by scanning it)
                                if (clientDriver != null)
                                {
                                    //requestFileName = command.WriteValue;
                                    string json = JsonConvert.SerializeObject(clientDriver.GetAnalogCase());
                                    command.ReadValue = json;
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    command.ResultString = "ReadValue1 '" + command.Mapping + "' not handled.";
                                }
                                break;

                            case "GetConnectionStatus": // get connection status
                                if (clientDriver != null)
                                {
                                    bool b = clientDriver.GetConnectionStatus();
                                    command.ReadValue = b;
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    command.ResultString = "Command '" + command.Mapping + "' handled.";
                                }
                                break;
                            case "GetCurrentTestFileName": // get the current test file name
                                if (clientDriver != null )
                                {
                                    string b = clientDriver.GetCurrentTestFileName();
                                    command.ReadValue = b;
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    command.ResultString = "Command '" + command.Mapping + "' handled.";
                                }
                                break;
                            case "GetNewTestCaseFlag": // get if the test has been started or not
                                if (clientDriver != null)
                                {
                                    bool b = clientDriver.GetNewTestCaseFlag();
                                    command.ReadValue = b;
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    command.ResultString = "Command '" + command.Mapping + "' handled.";
                                }
                                break;

                            case "SetNewTestCaseFlag": // 
                                if (clientDriver != null && command.WriteValue != null)
                                {
                                    bool myBool = bool.Parse(command.WriteValue);
                                    clientDriver.SetNewTestCaseFlag(myBool);
                                    command.ReadValue = 0;
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    command.ResultString = "Command '" + command.Mapping + "' handled.";
                                }
                                break;

                            case "SetCurrentTestFileName": // get the current test file name
                                if (clientDriver != null && command.WriteValue != null)
                                {
                                    clientDriver.SetCurrentTestFileName(command.WriteValue);
                                    command.ReadValue = 0;
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    command.ResultString = "Command '" + command.Mapping + "' handled.";
                                }
                                break;






                            case "ConnectPLC": // connect to PLC if change is done in AMS net ID
                                var result = JsonConvert.DeserializeObject<PLCInfo>(command.WriteValue);
                                if (clientDriver != null)
                                {
                                    ADSOperationCode status = clientDriver.ConnectPLC(result.plcName, result.amsNetID, result.port, result.inputFilter, result.outputFilter, result.analogFilter);
                                    if (status == ADSOperationCode.ADS_SUCCESS)
                                    {
                                        command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                        command.ReadValue = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    }
                                    else
                                    {
                                        command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionFail;
                                        command.ReadValue = ServerExtensionErrorValue.ServerExtensionFail;
                                    }
                                }
                                else
                                {
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionFail;
                                    command.ReadValue = ServerExtensionErrorValue.ServerExtensionFail;
                                }
                                break;

                            case "DisconnectFromPLC": // connect to PLC if change is done in AMS net ID

                                ADSOperationCode disStatus = clientDriver.DisconnectFromPLC();
                                if (disStatus == ADSOperationCode.ADS_SUCCESS)
                                {
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    command.ReadValue = ServerExtensionErrorValue.ServerExtensionSuccess;
                                }
                                else
                                {
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionFail;
                                    command.ReadValue = ServerExtensionErrorValue.ServerExtensionFail;
                                }
                                break;


                            case "InitExtension": // Init extension such as init the log4net

                                /*
                                var driverPath = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                                driverPath = driverPath + "\\";

                                if (!log4net.LogManager.GetRepository().Configured)
                                {
                                    string logfilepath = driverPath + "logfile.txt";
                                    string[] driverPath_ = driverPath.Split('\\');
                                    string formatted = "";
                                    for (int i = 0; i < driverPath_.Length-1; i++)
                                    {
                                        formatted = formatted + driverPath_[i] + "\\";
                                    }
                                    //formatted = formatted.Remove(formatted.Length - 1);
                                    string sss = formatted + "log4net.config";
                                    logfilepath = formatted + "logfile.txt";
                                    var configFile = new FileInfo(sss);
                                    if (!configFile.Exists)
                                    {
                                        using (StreamWriter sw = new StreamWriter(sss))
                                        {
                                            sw.WriteLine("<log4net>");
                                            sw.WriteLine("<root>");
                                            sw.WriteLine("<level value=\"ALL\" />");
                                            sw.WriteLine("<appender-ref ref=\"file\" />");
                                            sw.WriteLine("</root>");
                                            sw.WriteLine("<appender name=\"file\" type=\"log4net.Appender.RollingFileAppender\">");
                                            string filepath = "<file value=\"" + logfilepath + "\"/>";
                                            sw.WriteLine(filepath);
                                            sw.WriteLine("<appendToFile value=\"true\" />");
                                            sw.WriteLine("<rollingStyle value=\"Size\" />");
                                            sw.WriteLine("<maxSizeRollBackups value=\"5\" />");
                                            sw.WriteLine("<maximumFileSize value=\"10MB\" />");
                                            sw.WriteLine("<staticLogFileName value=\"true\" />");
                                            sw.WriteLine("<layout type=\"log4net.Layout.PatternLayout\">");
                                            sw.WriteLine("<conversionPattern value=\"%date [%thread] %level %logger - %message%newline\" />");
                                            sw.WriteLine("</layout>");
                                            sw.WriteLine("</appender>");
                                            sw.WriteLine("</log4net>");
                                        }
                                    }
                                    log4net.Config.XmlConfigurator.Configure(configFile);
                                    command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    command.ReadValue = 0;
                                }
                                */

                                if (clientDriver == null)
                                {
                                    clientDriver = new ADSTestDriverClass();
                                }
                                int statusPath = clientDriver.InitDriver();
                                if (statusPath == 0)
                                {
                                    command.ReadValue = ServerExtensionErrorValue.ServerExtensionSuccess;
                                    
                                }
                                else
                                {
                                    command.ReadValue = ServerExtensionErrorValue.ServerExtensionFail;
                                }
                                log.InfoFormat("InitExtension done, status = {0}", statusPath);

                                break;


                            default:
                                command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionFail;
                                command.ResultString = "Unknown command '" + command.Mapping + "' not handled.";
                                break;
                        }
                    }
                    catch (Exception ex)
                    {
                        command.ExtensionResult = ServerExtensionErrorValue.ServerExtensionFail;
                        command.ResultString = "Calling command '" + command.Mapping + "' failed! Additional information: " + ex.ToString();
                    }
                }
            }
            catch (Exception ex)
            {
                throw new TcHmiException(ex.ToString(), ErrorValue.HMI_E_EXTENSION);
            }
        }
    }
}
