//  
//   Rebex Sample Code License
// 
//   Copyright 2025, REBEX CR s.r.o.
//   All rights reserved.
//   https://www.rebex.net/
// 
//   Permission to use, copy, modify, and/or distribute this software for any
//   purpose with or without fee is hereby granted.
// 
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//   OTHER DEALINGS IN THE SOFTWARE.
// 

using System;
using System.Xml;
using System.IO;
using Rebex.Net;
using System.Text;

namespace Rebex.Samples
{
    /// <summary>
    /// ConnectionEditor data extended for specific fields and operations used in file-transfer samples.
    /// </summary>
    public class ConnectionFormData : ConnectionData
    {
        private readonly FileTransferComponent _supportedComponents;

        public FileTransferComponent SupportedComponents { get { return _supportedComponents; } }

        public LogLevel LogLevel { get { return _logLevel; } set { _logLevel = value; } }
        private LogLevel _logLevel;

        /// <summary>
        /// Create a new instance and fill it with defaults.
        /// </summary>
        public ConnectionFormData(FileTransferComponent supportedComponents)
        {
            _supportedComponents = supportedComponents;

            // set defaults
            LogLevel = LogLevel.Info;
            ServerHost = "test.rebex.net";

            if ((SupportedComponents & FileTransferComponent.Ftp) != 0)
            {
                FileTransferMode = FileTransferMode.Ftp;
                ServerPort = ConnectionEditorUtils.FtpDefaultPort;
            }
            else
            {
                FileTransferMode = FileTransferMode.Sftp;
                ServerPort = ConnectionEditorUtils.SftpDefaultPort;
            }

            ServerUser = "demo";
            ServerPassword = "password";
            PassiveMode = true;
            ProxyType = FileTransferProxyType.None;

            ServerCertificateVerifyingMode = CertificateVerifyingMode.UseWindowsInfrastructure;
            TlsProtocol = TlsVersion.TLS13 | TlsVersion.TLS12;

            ServerKeyVerifyingMode = KeyVerifyingMode.LocalyStoredFingerprint;

        }

        /// <summary>
        /// Load data from a component-specific config file 
        /// </summary>
        public void LoadConfig()
        {
            bool proxyTypeSet = false;

            XmlDocument xml = new XmlDocument();
            if (!File.Exists(ConfigFile))
                return;

            xml.Load(ConfigFile);

            XmlElement config = xml["configuration"];
            foreach (XmlNode key in config.ChildNodes)
            {
                string item = null;
                string name = null;
                if (key.Attributes["value"] != null)
                    item = key.Attributes["value"].Value;
                if (key.Attributes["name"] != null)
                    name = key.Attributes["name"].Value;
                if (key["value"] != null)
                    item = key["value"].InnerText;
                if (key["name"] != null)
                    name = key["name"].InnerText;

                if (name == null)
                    continue;
                if (item == null)
                    item = "";

                switch (name)
                {
                    case "loglevel": LogLevel = ToEnum(item, LogLevel); break;
                    case "fileTransferMode": FileTransferMode = ToEnum(item, FileTransferMode); break;
                    case "host": ServerHost = item; break;
                    case "login": ServerUser = item; break;
                    case "port": ServerPort = ToInt(item, ServerPort); break;
                    case "gssApi": UseGssApi = (item.ToLower() == "false" ? false : true); break;
                    case "domain": ServerDomain = item; break;
                    case "password": ServerPassword = item; StorePassword = !string.IsNullOrEmpty(ServerPassword); break;
                    case "passive": PassiveMode = (item.ToLower() == "false" ? false : true); break;
                    case "proxytype": if (!proxyTypeSet) ProxyType = ToEnum(item, ProxyType); break;
                    case "proxy": ProxyHost = item; break;
                    case "proxyport": ProxyPort = ToInt(item, ProxyPort); break;
                    case "proxylogin": ProxyUser = item; break;
                    case "proxypassword": ProxyPassword = item; break;
                    case "clearCommandChannel": ClearCommandChannel = (item.ToLower() == "false" ? false : true); break;
                    case "certificatepath": ClientCertificateFilename = item; break;
                    case "privateKeyFilename": PrivateKeyFilename = item; break;
                    case "serverCertificateThumbprint": ServerCertificateThumbprint = item; break;
                    case "serverCertificateVerifyingMode": ServerCertificateVerifyingMode = ToEnum(item, ServerCertificateVerifyingMode); break;
                    case "serverKeyFingerprint": ServerKeyFingerprint = item; break;
                    case "serverKeyAlgorithm": ServerKeyAlgorithm = ToEnum(item, ServerKeyAlgorithm); break;
                    case "serverKeyHashAlgorithm": ServerKeyHashAlgorithm = ToEnum(item, ServerKeyHashAlgorithm); break;
                    case "serverKeyVerifyingMode": ServerKeyVerifyingMode = ToEnum(item, ServerKeyVerifyingMode); break;
                    case "tlsSslVersion": TlsProtocol = ToEnum(item, TlsProtocol); break;
                    case "allowedSuite": AllowedSuite = ToEnum(item, AllowedSuite); break;
                    case "allowWeakSuites":	AllowWeakSuites = (item.ToLower() == "true"); break;
                    case "proxyenabled":
                        if (item.ToLower() == "false")
                        {
                            ProxyType = FileTransferProxyType.None;
                            proxyTypeSet = true;
                        }
                        break;
                }
            }
        }

        /// <summary>
        /// Save values into config file
        /// </summary>
        public void SaveConfig()
        {
            XmlDocument xml = new XmlDocument();
            XmlElement config = xml.CreateElement("configuration");
            xml.AppendChild(config);

            AddKey(xml, config, "loglevel", LogLevel.ToString());
            AddKey(xml, config, "fileTransferMode", FileTransferMode.ToString());
            AddKey(xml, config, "host", ServerHost);
            AddKey(xml, config, "login", ServerUser);
            AddKey(xml, config, "gssApi", UseGssApi.ToString());
            AddKey(xml, config, "domain", ServerDomain);
            AddKey(xml, config, "port", ServerPort.ToString());
            string pwd = (StorePassword || ServerPassword == "password") ? ServerPassword : "";
            AddKey(xml, config, "password", pwd);
            AddKey(xml, config, "passive", PassiveMode.ToString());
            AddKey(xml, config, "proxy", ProxyHost);
            AddKey(xml, config, "proxyport", ProxyPort.ToString());
            AddKey(xml, config, "proxytype", ProxyType.ToString());
            AddKey(xml, config, "proxylogin", ProxyUser);
            AddKey(xml, config, "proxypassword", ProxyPassword);
            AddKey(xml, config, "clearCommandChannel", ClearCommandChannel.ToString());
            AddKey(xml, config, "certificatepath", ClientCertificateFilename);
            AddKey(xml, config, "privateKeyFilename", PrivateKeyFilename);
            AddKey(xml, config, "serverCertificateThumbprint", ServerCertificateThumbprint);
            AddKey(xml, config, "serverCertificateVerifyingMode", ServerCertificateVerifyingMode.ToString());
            AddKey(xml, config, "serverKeyFingerprint", ServerKeyFingerprint);
            AddKey(xml, config, "serverKeyAlgorithm", ServerKeyAlgorithm.ToString());
            AddKey(xml, config, "serverKeyHashAlgorithm", ServerKeyHashAlgorithm.ToString());
            AddKey(xml, config, "serverKeyVerifyingMode", ServerKeyVerifyingMode.ToString());
            AddKey(xml, config, "tlsSslVersion", ToString(TlsProtocol));
            AddKey(xml, config, "allowedSuite", AllowedSuite.ToString());
            AddKey(xml, config, "allowWeakSuites", AllowWeakSuites.ToString());

            string configPath = Path.GetDirectoryName(ConfigFile);
            if (!Directory.Exists(configPath))
                Directory.CreateDirectory(configPath);
            xml.Save(ConfigFile);
        }

        #region Utility methods

        private string ConfigFile
        {
            get
            {
                if ((int)_supportedComponents <= 0)
                    throw new InvalidOperationException("SupportedComponents value has not been initialized - call the Initialize() method.");
                string filename = string.Format(@"Rebex{0}{1}{0}WinFormClient.xml", Path.DirectorySeparatorChar, ConnectionEditorUtils.GetEnumValueDescription(_supportedComponents));
                return Path.Combine(Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), filename);
            }
        }

        private static int ToInt(string value, int defaultValue)
        {
            try
            {
                return Int32.Parse(value);
            }
            catch
            {
                return defaultValue;
            }
        }

        private static T ToEnum<T>(string value, T defaultValue)
        {
            try
            {
                return (T)Enum.Parse(typeof(T), value);
            }
            catch
            {
                return defaultValue;
            }
        }

        private static string ToString(TlsVersion tlsVersion)
        {
            StringBuilder sb = new StringBuilder();
            bool addSeparator = false;

            Action<TlsVersion> convert = (tv =>
            {
                if ((tlsVersion & tv) != 0)
                {
                    if (addSeparator)
                    {
                        sb.Append(", ");
                    }
                    sb.Append(tv.ToString());
                    addSeparator = true;
                }
            });

            convert(TlsVersion.TLS13);
            convert(TlsVersion.TLS12);
            convert(TlsVersion.TLS11);
            convert(TlsVersion.TLS10);

            return sb.ToString();
        }

        private static void AddKey(XmlDocument xml, XmlElement config, string name, string val)
        {
            XmlElement key = xml.CreateElement("key");
            config.AppendChild(key);
            XmlAttribute atrName = xml.CreateAttribute("name");
            atrName.Value = name;
            XmlAttribute atrVal = xml.CreateAttribute("value");
            atrVal.Value = val;
            key.Attributes.Append(atrName);
            key.Attributes.Append(atrVal);
        }

        #endregion 
    }
}
