// ******************************************************************************//
//  RAYTHEON PROPRIETARY: THIS DOCUMENT CONTAINS DATA OR INFORMATION
//  PROPRIETARY TO RAYTHEON COMPANY AND IS RESTRICTED TO USE ONLY BY PERSONS
//  AUTHORIZED BY RAYTHEON COMPANY IN WRITING TO USE IT. DISCLOSURE TO
//  UNAUTHORIZED PERSONS WOULD LIKELY CAUSE SUBSTANTIAL COMPETITIVE HARM TO
//  RAYTHEON COMPANY''S BUSINESS POSITION. NEITHER SAID DOCUMENT NOR ITS
//  CONTENTS SHALL BE FURNISHED OR DISCLOSED TO OR COPIED OR USED BY PERSONS
//  OUTSIDE RAYTHEON COMPANY WITHOUT THE EXPRESS WRITTEN APPROVAL OF RAYTHEON
//  COMPANY.
//
//  THIS PROPRIETARY NOTICE IS NOT APPLICABLE IF DELIVERED TO THE U.S. GOVERNMENT
//
//  UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
//
//  WARNING: THIS DOCUMENT CONTAINS TECHNICAL DATA AND / OR TECHNOLOGY WHOSE
//  EXPORT OR DISCLOSURE TO NON-U.S. PERSONS, WHEREVER LOCATED, IS RESTRICTED
//  BY THE INTERNATIONAL TRAFFIC IN ARMS REGULATIONS (ITAR) (22 C.F.R. SECTION
//  120-130) OR THE EXPORT ADMINISTRATION REGULATIONS (EAR) (15 C.F.R. SECTION
//  730-774). THIS DOCUMENT CANNOT BE EXPORTED (E.G., PROVIDED TO A SUPPLIER
//  OUTSIDE OF THE UNITED STATES) OR DISCLOSED TO A NON-U.S. PERSON, WHEREVER
//  LOCATED, UNTIL A FINAL JURISDICTION AND CLASSIFICATION DETERMINATION HAS
//  BEEN COMPLETED AND APPROVED BY RAYTHEON, AND ANY REQUIRED U.S. GOVERNMENT
//  APPROVALS HAVE BEEN OBTAINED. VIOLATIONS ARE SUBJECT TO SEVERE CRIMINAL
//  PENALTIES.
//
//  DOD 5220.22-M, INDUSTRIAL SECURITY MANUAL, CHAPTER 5, SECTION 1 THROUGH 9 :
//  FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN OR DOD 5200.1-R,
//  INFORMATION SECURITY PROGRAM, CHAPTER 6. FOR UNCLASSIFIED, LIMITED DOCUMENTS
//  DESTROY BY ANY  METHOD THAT WILL PREVENT DISCLOSURE OF CONTENTS OR
//  RECONSTRUCTION OF THE DOCUMENT.
// ****************************************************************************//

using System;
using System.IO;
using System.Linq;
using System.Xml.Linq;

namespace Raytheon.Common
{
   /// <summary>
   /// Implementation of the IConfiguration interface
   /// </summary>
   public class RaytheonConfiguration : IConfiguration
   {
      /// <summary>
      /// returns configuration file path
      /// </summary>
      private string configFilePath
      {
         get
         {
            return Path.Combine(OutputPath, string.Concat(Name, ".xml"));
         }
      }

      /// <summary>
      /// configuration file name
      /// </summary>
      public string Name
      {
         get;
         internal set;
      }

      /// <summary>
      /// configuration file location
      /// </summary>
      public string OutputPath
      {
         get;
         internal set;
      }

      /// <summary>
      /// default constructor
      /// </summary>
      public RaytheonConfiguration()
      {
         OutputPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Raytheon", "Configuration");
         IsValidPath(OutputPath);
      }

      /// <summary>
      /// constructor that takes configuration file name
      /// </summary>
      /// <param name="name"></param>
      public RaytheonConfiguration(string name) : this()
      {
         Name = name;
      }

      /// <summary>
      /// constructor that takes file name and location
      /// </summary>
      /// <param name="name"></param>
      /// <param name="outputPath"></param>
      public RaytheonConfiguration(string name, string outputPath) : this(name)
      {
         if (IsValidPath(outputPath))
         {
            OutputPath = outputPath;
         }
      }

      private static void CreateConfigurationFile(FileInfo file)
      {
         XDocument xDocument = new XDocument();
         xDocument.Add(new XElement("Configurations"));
         xDocument.Root.Add(new XElement("IniConfiguration"));
         xDocument.Root.Add(new XElement("XmlConfigurations"));
         xDocument.Save(file.FullName);
      }

      private FileInfo CreateFileIfMissing()
      {
         FileInfo fileInfo = new FileInfo(configFilePath);
         if (!fileInfo.Exists)
         {
            throw new Exception($"Can't find file: {configFilePath}");
         }
         return fileInfo;
      }

      private static XElement GetConfigSection(string section, XElement iniConfiguration, string configFilePath)
      {
         XElement xElement = (
             from s in iniConfiguration.Descendants("section")
             where (string)s.Attribute("name") == section
             select s).FirstOrDefault();
         if (xElement == null)
         {
            throw new Exception($"There is no <section> tag with 'name={section}' in {configFilePath}");
         }
         return xElement;
      }

      /// <summary>
      /// reads single string value from XML configuration file by section name and a key
      /// </summary>
      /// <param name="section"></param>
      /// <param name="key"></param>
      /// <param name="defaultValue"></param>
      /// <returns></returns>
      public string GetConfigurationValue(string section, string key, string defaultValue)
      {
         FileInfo fileInfo = CreateFileIfMissing();
         XDocument xDocument = XDocument.Load(fileInfo.FullName);
         XElement xElement = xDocument.Root.Element("IniConfiguration");
         XElement configSection = GetConfigSection(section, xElement, configFilePath);
         XElement xElement1 = (
             from k in configSection.Descendants("key")
             where (string)k.Attribute("name") == key
             select k).FirstOrDefault();
         if (xElement1 == null)
         {
            XName xName = "key";
            object[] xAttribute = new object[] { new XAttribute("name", key), new XAttribute("value", defaultValue) };
            xElement1 = new XElement(xName, xAttribute);
            configSection.Add(xElement1);
            xDocument.Save(fileInfo.FullName);
         }
         XAttribute xAttribute1 = xElement1.Attribute("value");
         if (xAttribute1 == null)
         {
            xAttribute1 = new XAttribute("value", defaultValue);
            xElement1.Add(xAttribute1);
            xDocument.Save(fileInfo.FullName);
         }
         if (xAttribute1 == null)
         {
            return null;
         }
         return xAttribute1.Value;
      }

      /// <summary>
      /// reads single string value from XML configuration file by section name and a key
      /// </summary>
      /// <param name="section"></param>
      /// <param name="key"></param>
      /// <param name="defaultValue"></param>
      /// <returns></returns>
      public string GetConfigurationValue(string section, string key)
      {
         FileInfo fileInfo = CreateFileIfMissing();
         XDocument xDocument = XDocument.Load(fileInfo.FullName);
         XElement xElement = xDocument.Root.Element("IniConfiguration");
         XElement configSection = GetConfigSection(section, xElement, configFilePath);
         XElement xElement1 = (
             from k in configSection.Descendants("key")
             where (string)k.Attribute("name") == key
             select k).FirstOrDefault();
         if (xElement1 == null)
         {
            throw new Exception($"There is no <key> tag with 'name={key}' under section {section} in {configFilePath}");
         }
         XAttribute xAttribute1 = xElement1.Attribute("value");
         if (xAttribute1 == null)
         {
            throw new Exception($"Attribute 'value' is missing for <key> tag with 'name={key}' under section {section} in {configFilePath}");
         }
         return xAttribute1.Value;
      }

      private static XElement GetConfigXml(string name, XElement xmlConfigurations)
      {
         XElement xElement = (
             from s in xmlConfigurations.Descendants("XmlConfiguration")
             where (string)s.Attribute("name") == name
             select s).FirstOrDefault();
         return xElement;
      }

      /// <summary>
      /// reads XML configuration from a file
      /// </summary>
      /// <param name="name"></param>
      /// <returns></returns>
      public string GetXmlConfiguration(string name)
      {
         XDocument xDocument = XDocument.Load(CreateFileIfMissing().FullName);
         XElement xElement = xDocument.Root.Element("XmlConfigurations");
         XElement configXml = GetConfigXml(name, xElement);
         if (configXml == null)
         {
            return string.Empty;
         }
         return configXml.ToString();
      }

      internal void Initialize()
      {
         Directory.CreateDirectory(OutputPath);
         CreateFileIfMissing();
      }

      private static bool IsValidPath(string outputPath)
      {
         bool flag;
         try
         {
            Directory.CreateDirectory(outputPath);
            flag = true;
         }
         catch (PathTooLongException)
         {
            flag = false;
         }
         catch (UnauthorizedAccessException)
         {
            flag = false;
         }
         catch (ArgumentException)
         {
            flag = false;
         }
         catch (DirectoryNotFoundException)
         {
            flag = false;
         }
         catch (Exception)
         {
            throw;
         }
         return flag;
      }

      private static void SetConfigKey(string key, string value, XElement configSection)
      {
         XElement xElement = (
             from k in configSection.Descendants("key")
             where (string)k.Attribute("name") == key
             select k).FirstOrDefault();
         if (xElement != null)
         {
            xElement.SetAttributeValue("value", value);
            return;
         }
         XName xName = "key";
         object[] xAttribute = new object[] { new XAttribute("name", key), null };
         xAttribute[1] = new XAttribute("value", value ?? string.Empty);
         xElement = new XElement(xName, xAttribute);
         configSection.Add(xElement);
      }

      /// <summary>
      /// writes configuration value by section name and by key
      /// </summary>
      /// <param name="section"></param>
      /// <param name="key"></param>
      /// <param name="value"></param>
      public void SetConfigurationValue(string section, string key, string value)
      {
         FileInfo fileInfo = CreateFileIfMissing();
         XDocument xDocument = XDocument.Load(fileInfo.FullName);
         XElement xElement = xDocument.Root.Element("IniConfiguration");
         SetConfigKey(key, value, GetConfigSection(section, xElement, configFilePath));
         xDocument.Save(fileInfo.FullName);
      }

      /// <summary>
      /// saves XML string into XML file
      /// </summary>
      /// <param name="name"></param>
      /// <param name="xml"></param>
      public void SetXmlConfiguration(string name, string xml)
      {
         FileInfo fileInfo = CreateFileIfMissing();
         XElement xElement = XElement.Parse(xml);
         int elementCount = xElement.Elements().Count();

         XDocument xDocument = XDocument.Load(fileInfo.FullName);
         XElement xmlConfigurations = xDocument.Root.Element("XmlConfigurations");
         XElement sectionXml = GetConfigXml(name, xmlConfigurations);

         XName xName = "XmlConfiguration";
         object[] xAttribute = new object[1 + elementCount];
         xAttribute[0] = new XAttribute("name", name);
         if (elementCount == 1)
         {
            xAttribute[1] = xElement.FirstNode;
         }
         else
         {
            int i = 1;
            foreach (var item in xElement.Elements())
            {
               xAttribute[i++] = item;
            }
         }

         if (sectionXml == null)
         {
            xmlConfigurations.Add(new XElement(xName, xAttribute));
         }
         else
         {
            sectionXml.ReplaceWith(new XElement(xName, xAttribute));
         }

         xDocument.Save(fileInfo.FullName);
      }

      /// <summary>
      /// tries reading enumeration value by section and key name
      /// </summary>
      /// <typeparam name="T"></typeparam>
      /// <param name="section"></param>
      /// <param name="key"></param>
      /// <param name="value"></param>
      /// <returns></returns>
      /// <exception cref="ArgumentException"></exception>
      public bool TryGetEnumValue<T>(string section, string key, out T value)
      where T : struct
      {
         if (string.IsNullOrWhiteSpace(section))
         {
            throw new ArgumentException("'section' cannot be Null or Whitespace", "section");
         }
         if (string.IsNullOrWhiteSpace(key))
         {
            throw new ArgumentException("'key' cannot be Null or Whitespace", "key");
         }
         if (!typeof(T).IsEnum)
         {
            throw new ArgumentException("T must be an enumerated type");
         }
         bool flag = Enum.TryParse<T>(GetConfigurationValue(section, key, string.Concat("NOT_A_VALID_", key.ToUpper())), out value);
         if (!flag)
         {
            int num = 0;
            string[] names = Enum.GetNames(typeof(T));
            for (int i = 0; i < names.Length; i++)
            {
               string str = names[i];
               int num1 = num + 1;
               num = num1;
               int num2 = num1;
               SetConfigurationValue(string.Concat("Valid_", key), num2.ToString(), str);
            }
         }
         return flag;
      }
   }
}