using EDAViewer.Models;
using EDAViewer.Singleton.Helper;
using Infineon.Monitoring.MonA;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Shared;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
using System.Xml.Serialization;

namespace EDAViewer.Singleton
{

    public partial class Background : IBackground
    {

        public List<Timer> Timers => _Timers;
        public string Message { get; private set; }
        public string WorkingDirectory { get; private set; }
        public List<Exception> Exceptions { get; private set; }

        private bool _ShuttingDown;
        private readonly object _Lock;
        private readonly Calendar _Calendar;
        private readonly List<Timer> _Timers;
        private readonly AppSettings _AppSettings;
        private readonly string[] _SiEquipmentTypes;
        private readonly string[] _GaNEquipmentTypes;
        private readonly IsEnvironment _IsEnvironment;

        private const string _Site = "sjc";

        private Log _Log;
        private DateTime _PrimaryInstanceSetAt;
        private string _EdaDataCollectionPlansLastRun;

        public Background(IsEnvironment isEnvironment, AppSettings appSettings, string workingDirectory)
        {
            _Log = null;
            Exceptions = new();
            _IsEnvironment = isEnvironment;
            _Lock = new object();
            _ShuttingDown = false;
            if (_Lock is null)
            { }
            Message = string.Empty;
            _AppSettings = appSettings;
            _Timers = new List<Timer>();
            _IsEnvironment = isEnvironment;
            WorkingDirectory = workingDirectory;
            _PrimaryInstanceSetAt = DateTime.MinValue;
            _EdaDataCollectionPlansLastRun = string.Empty;
            _SiEquipmentTypes = GetEquipmentTypes(isGaN: false, isSi: true);
            _GaNEquipmentTypes = GetEquipmentTypes(isGaN: true, isSi: false);
            CultureInfo cultureInfo = new CultureInfo("en-US");
            _Calendar = cultureInfo.Calendar;
        }

        public void Dispose()
        {
            foreach (Timer timer in _Timers)
                timer.Dispose();
        }

        void IBackground.Update(ILogger<object> logger)
        {
            Update(logger);
        }

        internal void Update(ILogger<object> logger)
        {
            _Log = new Log(logger);
            //https://blog.magnusmontin.net/2018/11/05/platform-conditional-compilation-in-net-core/
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                _Log.Debug("Running on Linux!");
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
                _Log.Debug("Running on macOS!");
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                _Log.Debug("Running on Windows!");
#if Linux
            _Log.Debug("Built on Linux!");
#elif OSX
            _Log.Debug("Built on macOS!");
#elif Windows
            _Log.Debug("Built in Windows!");
#else
            ()("Built in unkown!");
#endif
            if (string.IsNullOrEmpty(_AppSettings.URLs) && !_IsEnvironment.Development)
                throw new Exception("Invalid Application Settings URLs!");
        }

        internal void Catch(Exception exception)
        {
            Exceptions.Add(exception);
            _Log.Error(exception);
            if (!string.IsNullOrEmpty(_AppSettings.MonARessource))
            {
                MonIn monIn = MonIn.GetInstance();
                monIn.SendStatus(_Site, _AppSettings.MonARessource, "Heartbeat", State.Warning);
            }
        }

        public void SendStatusOk()
        {
            if (!string.IsNullOrEmpty(_AppSettings.MonARessource))
            {
                MonIn monIn = MonIn.GetInstance();
                monIn.SendStatus(_Site, _AppSettings.MonARessource, "Heartbeat", State.Ok);
            }
        }

        public string GetCountDirectory(string verb)
        {
            DateTime dateTime = DateTime.Now;
            CultureInfo cultureInfo = new("en-US");
            Calendar calendar = cultureInfo.Calendar;
            string weekOfYear = calendar.GetWeekOfYear(dateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString("00");
            //string nameSpace = System.Reflection.Assembly.GetExecutingAssembly().EntryPoint.DeclaringType.Namespace;
            string nameSpace = GetType().Namespace.Split('.')[0];
            if (!string.IsNullOrEmpty(_AppSettings.MonARessource))
            {
                MonIn monIn = MonIn.GetInstance();
                monIn.SendStatus(_Site, _AppSettings.MonARessource, "Heartbeat", State.Up);
            }
            return string.Concat(@"\\", _AppSettings.Server, @"\EC_EDA\Counts\", dateTime.ToString("yyyy"), @"\", "Week_", weekOfYear, @"\", dateTime.ToString("yyyy - MM - dd"), @"\", "Application", @"\", nameSpace, @"\", verb, @"\", dateTime.Ticks);
        }

        public void Stop(bool immediate)
        {
            _ShuttingDown = true;
            foreach (Timer timer in _Timers)
                timer.Change(Timeout.Infinite, 0);
            if (!_IsEnvironment.Development)
            {
                if (!string.IsNullOrEmpty(_AppSettings.MonARessource))
                {
                    MonIn monIn = MonIn.GetInstance();
                    monIn.SendStatus(_Site, _AppSettings.MonARessource, "Heartbeat", State.Down);
                }
                string countDirectory = GetCountDirectory("Stop");
                Directory.CreateDirectory(countDirectory);
            }
        }

        public void ClearMessage()
        {
            Message = string.Empty;
        }

        public void SetIsPrimaryInstance()
        {
            _PrimaryInstanceSetAt = DateTime.Now;
        }

        public void ClearIsPrimaryInstance()
        {
            _PrimaryInstanceSetAt = DateTime.MinValue;
        }

        public bool IsPrimaryInstance()
        {
            bool result;
            DateTime dateTime = DateTime.Now.AddDays(-1);
            result = _PrimaryInstanceSetAt > dateTime;
            return result;
        }

        public void EdaDataCollectionPlansCallback()
        {
            string cSharpFormat = "yyyy-MM-dd_hh:mm:ss tt";
            string oracleFormat = "yyyy-MM-dd_hh:mi:ss AM";
            _Log.Debug(string.Concat("A) _EdaDataCollectionPlansLastRun = ", _EdaDataCollectionPlansLastRun));
            DataCollectionPlans(_AppSettings, _EdaDataCollectionPlansLastRun, cSharpFormat, oracleFormat);
            _EdaDataCollectionPlansLastRun = DateTime.Now.ToString(cSharpFormat);
            _Log.Debug(string.Concat("B) _EdaDataCollectionPlansLastRun = ", _EdaDataCollectionPlansLastRun));
        }
        private void DeleteEmptyDirectories(string directory)
        {
            string[] files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);
            string[] directories = Directory.GetDirectories(directory, "*", SearchOption.AllDirectories);
            if (files.Length == 0 && directories.Length == 0)
                Directory.Delete(directory);
            else
            {
                foreach (string subDirectory in Directory.GetDirectories(directory, "*", SearchOption.TopDirectoryOnly))
                {
                    if (_ShuttingDown)
                        break;
                    DeleteEmptyDirectories(subDirectory);
                }
            }
        }

        private void ZipFilesByDate(string sourceDirectory, SearchOption searchOption = SearchOption.TopDirectoryOnly, string dayFormat = null)
        {
            string key;
            bool addFile;
            string fileName;
            string[] segments;
            string[] subFiles;
            string weekOfYear;
            FileInfo fileInfo;
            DateTime creationTime;
            DateTime lastWriteTime;
            Regex regex = new Regex("[a-zA-Z0-9]{1,}");
            DateTime dateTime = DateTime.Now.AddDays(-6);
            DateTime firstEmail = new DateTime(2019, 3, 8);
            CultureInfo cultureInfo = new CultureInfo("en-US");
            Calendar calendar = cultureInfo.Calendar;
            int ticksLength = dateTime.Ticks.ToString().Length;
            Dictionary<string, DateTime> weeks = new Dictionary<string, DateTime>();
            for (int i = 0; i < 1000; i++)
            {
                if (_ShuttingDown)
                    break;
                dateTime = firstEmail.AddDays(i);
                weekOfYear = calendar.GetWeekOfYear(dateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString("00");
                key = string.Concat(dateTime.ToString("yyyy"), "_Week_", weekOfYear);
                if (!weeks.ContainsKey(key))
                    weeks.Add(key, dateTime);
            }
            weekOfYear = calendar.GetWeekOfYear(DateTime.Now, CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString("00");
            string skipKey = string.Concat(DateTime.Now.ToString("yyyy"), "_Week_", weekOfYear);
            Dictionary<string, List<string>> keyValuePairs = new Dictionary<string, List<string>>();
            string[] topDirectories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly);
            if (topDirectories.Length == 0)
                topDirectories = new string[] { sourceDirectory };
            foreach (string topDirectory in topDirectories)
            {
                if (_ShuttingDown)
                    break;
                keyValuePairs.Clear();
                subFiles = Directory.GetFiles(topDirectory, "*", searchOption);
                foreach (string subFile in subFiles)
                {
                    if (_ShuttingDown)
                        break;
                    addFile = false;
                    if (subFile.EndsWith(".zip"))
                        continue;
                    fileName = Path.GetFileName(subFile);
                    fileInfo = new FileInfo(subFile);
                    creationTime = fileInfo.CreationTime;
                    if (creationTime > dateTime)
                        continue;
                    lastWriteTime = fileInfo.LastWriteTime;
                    if (fileName.Contains(lastWriteTime.ToString("yyyyMMdd")) || fileName.Contains(lastWriteTime.ToString("yyyy-MM-dd")) ||
                        fileName.Contains(creationTime.ToString("yyyyMMdd")) || fileName.Contains(creationTime.ToString("yyyy-MM-dd")) ||
                        fileName.Contains(lastWriteTime.ToString("yyMMdd")) || fileName.Contains(lastWriteTime.ToString("yy-MM-dd")) ||
                        fileName.Contains(creationTime.ToString("yyMMdd")) || fileName.Contains(creationTime.ToString("yy-MM-dd")) ||
                        fileName.Contains(lastWriteTime.AddDays(-1).ToString("yyyyMMdd")) || fileName.Contains(lastWriteTime.AddDays(-1).ToString("yyyy-MM-dd")) ||
                        fileName.Contains(creationTime.AddDays(-1).ToString("yyyyMMdd")) || fileName.Contains(creationTime.AddDays(-1).ToString("yyyy-MM-dd")) ||
                        fileName.Contains(lastWriteTime.AddDays(-1).ToString("yyMMdd")) || fileName.Contains(lastWriteTime.AddDays(-1).ToString("yy-MM-dd")) ||
                        fileName.Contains(creationTime.AddDays(-1).ToString("yyMMdd")) || fileName.Contains(creationTime.AddDays(-1).ToString("yy-MM-dd")))
                        addFile = true;
                    if (!addFile && fileName.Length > ticksLength)
                    {
                        MatchCollection matches = regex.Matches(fileName);
                        foreach (Match match in matches)
                        {
                            if (_ShuttingDown)
                                break;
                            if (match.Value.Length != ticksLength)
                                continue;
                            if (!long.TryParse(match.Value, out long ticks))
                                continue;
                            addFile = true;
                            break;
                        }
                        if (addFile)
                            break;
                    }
                    if (addFile)
                    {
                        weekOfYear = calendar.GetWeekOfYear(lastWriteTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString("00");
                        if (string.IsNullOrEmpty(dayFormat))
                            key = string.Concat(lastWriteTime.ToString("yyyy"), "_Week_", weekOfYear);
                        else
                            key = string.Concat(lastWriteTime.ToString("yyyy"), "_Week_", weekOfYear, "_", lastWriteTime.ToString(dayFormat));
                        if (key == skipKey)
                            continue;
                        if (!keyValuePairs.ContainsKey(key))
                            keyValuePairs.Add(key, new List<string>());
                        keyValuePairs[key].Add(subFile);
                    }
                }
                foreach (KeyValuePair<string, List<string>> element in keyValuePairs)
                {
                    if (_ShuttingDown)
                        break;
                    key = string.Concat(topDirectory, @"\", element.Key, ".zip");
                    if (File.Exists(key))
                    {
                        for (short i = 101; i < short.MaxValue; i++)
                        {
                            if (_ShuttingDown)
                                break;
                            key = string.Concat(topDirectory, @"\", element.Key, "_", i, ".zip");
                            if (!File.Exists(key))
                                break;
                        }
                    }
                    if (_ShuttingDown)
                        break;
                    lock (_Lock)
                    {
                        using (ZipArchive zip = ZipFile.Open(key, ZipArchiveMode.Create))
                        {
                            foreach (string file in element.Value)
                            {
                                zip.CreateEntryFromFile(file, Path.GetFileName(file));
                                File.Delete(file);
                            }
                        }
                    }
                }
                subFiles = Directory.GetFiles(topDirectory, "*.zip", SearchOption.TopDirectoryOnly);
                foreach (string subFile in subFiles)
                {
                    if (_ShuttingDown)
                        break;
                    fileName = Path.GetFileNameWithoutExtension(subFile);
                    segments = fileName.Split('_');
                    if (segments.Length > 2)
                        fileName = string.Concat(segments[0], '_', segments[1], '_', segments[2]);
                    if (weeks.ContainsKey(fileName))
                    {
                        try
                        { File.SetLastWriteTime(subFile, weeks[fileName]); }
                        catch (Exception) { }
                    }
                }
                if (topDirectory != sourceDirectory)
                    try
                    { DeleteEmptyDirectories(topDirectory); }
                    catch (Exception) { }
                //Print(topDirectory);
            }
        }

        public void LogPathCleanUpByWeek(string server, bool isEDA = false, bool isNA = false)
        {
            if (isEDA && isNA)
                throw new Exception();
            else if (!isEDA && !isNA)
                throw new Exception();
            string share;
            Tuple<string, string>[] tuples;
            if (isEDA)
            {
                share = string.Concat(@"\\", server, @"\ec_eda");
                tuples = new Tuple<string, string>[]
                {
                    new Tuple<string, string>(@"\Staging\Traces\MET08ANLYSDIFAAST230\LogFile", string.Empty),
                    //new Tuple<string, string>(@"\Staging\Traces\DEP08EGANAIXG5HT\R69-HSMS\LogFile", "yyyy_MM_dd"),
                    //new Tuple<string, string>(@"\Staging\Traces\DEP08EGANAIXG5\LogFile\R69-HSMS", "yyyy_MM_dd"),
                    //new Tuple<string, string>(@"\Staging\Traces\DEP08EGANAIXG5HT\R71-HSMS\LogFile", "yyyy_MM_dd"),
                    //new Tuple<string, string>(@"\Staging\Traces\DEP08EGANAIXG5\LogFile\R71-HSMS", "yyyy_MM_dd"),
                    new Tuple<string, string>(@"\Staging\Traces\MET08XRDXPERTPROMRDXL_Monthly\LogFile", string.Empty),
                    new Tuple<string, string>(@"\Staging\Traces\MET08XRDXPERTPROMRDXL_Weekly\LogFile", string.Empty),
                    new Tuple<string, string>(@"\Staging\Traces\MET08DDINCAN8620_Daily\LogFile", string.Empty),
                    new Tuple<string, string>(@"\Staging\Traces\MET08PRFUSB4000\LogFile", string.Empty),
                };
            }
            else if (isNA)
                throw new Exception();
            else
                throw new Exception();
            string[] files;
            string weekOfYear;
            FileInfo fileInfo;
            string newDirectroy;
            string sourceDirectory;
            DateTime lastWriteTime;
            string[] topDirectories;
            long twoDays = DateTime.Now.AddDays(-2).Ticks;
            foreach (Tuple<string, string> tuple in tuples)
            {
                if (_ShuttingDown)
                    break;
                sourceDirectory = string.Concat(share, tuple.Item1);
                if (!Directory.Exists(sourceDirectory))
                    continue;
                if (isEDA)
                    topDirectories = new string[] { sourceDirectory };
                else if (isNA)
                    throw new Exception();
                else
                    throw new Exception();
                if (topDirectories.Length == 0)
                    topDirectories = new string[] { sourceDirectory };
                foreach (string topDirectory in topDirectories)
                {
                    if (_ShuttingDown)
                        break;
                    files = Directory.GetFiles(topDirectory, "*", SearchOption.TopDirectoryOnly);
                    foreach (string file in files)
                    {
                        if (_ShuttingDown)
                            break;
                        if (file.EndsWith(".zip"))
                            continue;
                        fileInfo = new FileInfo(file);
                        lastWriteTime = fileInfo.LastAccessTime;
                        if (lastWriteTime.Ticks > twoDays)
                            continue;
                        weekOfYear = _Calendar.GetWeekOfYear(lastWriteTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString("00");
                        newDirectroy = string.Concat(topDirectory, @"\", lastWriteTime.ToString("yyyy"), "___Week_", weekOfYear, @"\", lastWriteTime.ToString("yyyy_MM_dd"));
                        if (!Directory.Exists(newDirectroy))
                            Directory.CreateDirectory(newDirectroy);
                        if (!fileInfo.Exists)
                            continue;
                        if (!_ShuttingDown)
                        {
                            lock (_Lock)
                                File.Move(file, string.Concat(newDirectroy, @"\", Path.GetFileName(file)));
                        }
                    }
                }
            }
            foreach (Tuple<string, string> tuple in tuples)
            {
                if (_ShuttingDown)
                    break;
                sourceDirectory = string.Concat(share, tuple.Item1);
                if (!Directory.Exists(sourceDirectory))
                    continue;
                ZipFilesByDate(sourceDirectory, SearchOption.AllDirectories, tuple.Item2);
            }
        }

        public void LogPathCleanUpByWeekCallback()
        {
            DateTime dateTime = DateTime.Now;
            if (dateTime.Hour > 8 && dateTime.Hour < 17)
            {
                _Log.Debug(string.Concat("A) ", nameof(LogPathCleanUpByWeek)));
                LogPathCleanUpByWeek(_AppSettings.Server, isEDA: true);
                _Log.Debug(string.Concat("B) ", nameof(LogPathCleanUpByWeek)));
            }
        }

        public void EDAOutputArchive(string sourceDirectory)
        {
            string key;
            string[] files;
            string[] cellIntaces;
            string fileWeekOfYear;
            DateTime fileDateTime;
            string checkDirectory;
            string cellInstancesName;
            string emptyFilesDirectory;
            int today = DateTime.Now.Day;
            string recipeArchiveDirectory;
            Dictionary<string, List<string>> keyValuePairs = new Dictionary<string, List<string>>();
            string[] equipmentTypes = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly);
            foreach (string equipmentType in equipmentTypes)
            {
                if (_ShuttingDown)
                    break;
                if (equipmentType.EndsWith(@"\_ Copy") || equipmentType.EndsWith(@"\_ Empty") || equipmentType.EndsWith(@"\CellInstance") || equipmentType.EndsWith(@"\IQS") || equipmentType.EndsWith(@"\Villach"))
                    continue;
                checkDirectory = string.Concat(equipmentType, @"\Ignore\Recipe");
                if (!Directory.Exists(checkDirectory))
                    Directory.CreateDirectory(checkDirectory);
                cellIntaces = Directory.GetDirectories(checkDirectory, "*", SearchOption.TopDirectoryOnly);
                foreach (string cellIntace in cellIntaces)
                {
                    if (_ShuttingDown)
                        break;
                    keyValuePairs.Clear();
                    cellInstancesName = Path.GetFileName(cellIntace);
                    recipeArchiveDirectory = string.Concat(equipmentType, @"\Ignore\!Archive\Recipe\", cellInstancesName);
                    if (!Directory.Exists(recipeArchiveDirectory))
                        Directory.CreateDirectory(recipeArchiveDirectory);
                    emptyFilesDirectory = string.Concat(equipmentType, @"\Ignore\!Archive\EmptyFiles\", cellInstancesName);
                    if (!Directory.Exists(emptyFilesDirectory))
                        Directory.CreateDirectory(emptyFilesDirectory);
                    files = Directory.GetFiles(cellIntace, "*", SearchOption.TopDirectoryOnly);
                    foreach (string file in files)
                    {
                        if (_ShuttingDown)
                            break;
                        fileDateTime = new FileInfo(file).LastWriteTime;
                        if (fileDateTime.Day != today)
                        {
                            fileWeekOfYear = _Calendar.GetWeekOfYear(fileDateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString("00");
                            key = string.Concat(fileDateTime.ToString("yyyy-MM-dd"), "_Week_", fileWeekOfYear);
                            if (!keyValuePairs.ContainsKey(key))
                                keyValuePairs.Add(key, new List<string>());
                            keyValuePairs[key].Add(file);
                        }
                    }
                    foreach (KeyValuePair<string, List<string>> element in keyValuePairs)
                    {
                        if (_ShuttingDown)
                            break;
                        lock (_Lock)
                        {
                            key = string.Concat(recipeArchiveDirectory, @"\", element.Key.Split('-')[0]);
                            if (!Directory.Exists(key))
                                Directory.CreateDirectory(key);
                            key = string.Concat(recipeArchiveDirectory, @"\", element.Key.Split('-')[0], @"\", element.Key, ".zip");
                            if (File.Exists(key))
                            {
                                for (short i = 101; i < short.MaxValue; i++)
                                {
                                    key = string.Concat(recipeArchiveDirectory, @"\", element.Key.Split('-')[0], @"\", element.Key, "_", i, ".zip");
                                    if (!File.Exists(key))
                                        break;
                                }
                            }
                        }
                        if (!string.IsNullOrEmpty(key))
                        {
                            if (_ShuttingDown)
                                break;
                            lock (_Lock)
                            {
                                using ZipArchive zip = ZipFile.Open(key, ZipArchiveMode.Create);
                                foreach (string file in element.Value)
                                {
                                    zip.CreateEntryFromFile(file, Path.GetFileName(file));
                                    File.Delete(file);
                                }
                            }
                        }
                    }
                }
            }
        }

        public void EDAOutputArchiveCallback()
        {
            DateTime dateTime = DateTime.Now;
            if (dateTime.Hour > 8 && dateTime.Hour < 17)
            {
                string sourceDirectory = string.Concat(@"\\", _AppSettings.Server, @"\ec_eda\Staging\Traces");
                if (!Directory.Exists(sourceDirectory))
                    _Log.Debug(string.Concat("// Source directory <", sourceDirectory, "> doesn't exist."));
                else
                    EDAOutputArchive(sourceDirectory);
            }
        }

        public static string[] GetEquipmentTypes(bool isGaN = false, bool isSi = false)
        {
            string[] results;
            if (isGaN && isSi)
                throw new Exception();
            else if (!isGaN && !isSi)
                isGaN = true;
            if (isSi)
            {
                results = new string[]
                {
                    Shared.EquipmentType.MET08ANLYSDIFAAST230_Semi.ToString(),
                    Shared.EquipmentType.MET08DDUPSFS6420.ToString(),
                    Shared.EquipmentType.MET08DDUPSP1TBI.ToString(),
                    Shared.EquipmentType.MET08RESIHGCV.ToString(),
                    Shared.EquipmentType.MET08RESIMAPCDE.ToString(),
                    Shared.EquipmentType.MET08THFTIRQS408M.ToString(),
                    Shared.EquipmentType.MET08THFTIRSTRATUS.ToString()
                };
            }
            else if (isGaN)
            {
                results = new string[]
                {
                    Shared.EquipmentType.DEP08EGANAIXG5.ToString(),
                    Shared.EquipmentType.MET08AFMD3100.ToString(),
                    Shared.EquipmentType.MET08BVHGPROBE.ToString(),
                    Shared.EquipmentType.MET08CVHGPROBE802B150.ToString(),
                    Shared.EquipmentType.MET08CVHGPROBE802B150_Monthly.ToString(),
                    Shared.EquipmentType.MET08CVHGPROBE802B150_Weekly.ToString(),
                    Shared.EquipmentType.MET08DDINCAN8620.ToString(),
                    Shared.EquipmentType.MET08DDINCAN8620_Daily.ToString(),
                    Shared.EquipmentType.MET08EBEAMINTEGRITY26.ToString(),
                    Shared.EquipmentType.MET08HALLHL5580.ToString(),
                    Shared.EquipmentType.MET08HALLHL5580_Monthly.ToString(),
                    Shared.EquipmentType.MET08HALLHL5580_Weekly.ToString(),
                    Shared.EquipmentType.MET08MESMICROSCOPE.ToString(),
                    Shared.EquipmentType.MET08NDFRESIMAP151C.ToString(),
                    Shared.EquipmentType.MET08NDFRESIMAP151C_Verification.ToString(),
                    Shared.EquipmentType.MET08PLMAPRPM.ToString(),
                    Shared.EquipmentType.MET08PLMAPRPM_Daily.ToString(),
                    Shared.EquipmentType.MET08PLMAPRPM_Verification.ToString(),
                    Shared.EquipmentType.MET08PLMPPLATO.ToString(),
                    Shared.EquipmentType.MET08PRFUSB4000.ToString(),
                    Shared.EquipmentType.MET08UVH44GS100M.ToString(),
                    Shared.EquipmentType.MET08VPDSUBCON.ToString(),
                    Shared.EquipmentType.MET08WGEOMX203641Q.ToString(),
                    Shared.EquipmentType.MET08WGEOMX203641Q_Verification.ToString(),
                    Shared.EquipmentType.METBRXRAYJV7300L.ToString(),
                    Shared.EquipmentType.MET08XRDXPERTPROMRDXL.ToString(),
                    Shared.EquipmentType.MET08XRDXPERTPROMRDXL_Monthly.ToString(),
                    Shared.EquipmentType.MET08XRDXPERTPROMRDXL_Weekly.ToString()
                };
            }
            else
                throw new Exception();
            return results;
        }

        private Stream ToStream(string @this)
        {
            var stream = new MemoryStream();
            var writer = new StreamWriter(stream);
            writer.Write(@this);
            writer.Flush();
            stream.Position = 0;
            return stream;
        }

        private T ParseXML<T>(string @this, bool throwExceptions) where T : class
        {
            object result = null;
            try
            {
                Stream stream = ToStream(@this.Trim());
                var reader = XmlReader.Create(stream, new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Document });
                //XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
                XmlSerializer xmlSerializer = new XmlSerializer(typeof(T), typeof(T).GetNestedTypes());
                result = xmlSerializer.Deserialize(reader);
                stream.Dispose();
            }
            catch (Exception)
            {
                if (throwExceptions)
                    throw;
            }
            return result as T;
        }

        public void DataCollectionPlans(AppSettings appSettings, string edaDataCollectionPlansLastRun, string cSharpFormat, string oracleFormat)
        {
            bool _ShuttingDown = false;
            object _Lock = new object();
            bool dev = appSettings.Server.Contains("_ec_");
            Dictionary<string, Dictionary<ModuleInstanceTypeName, List<List<object>>>> rows = new Dictionary<string, Dictionary<ModuleInstanceTypeName, List<List<object>>>>();
            //
            //int moduleinstancetypename = 0;
            int unitname = 1;
            //int modulename = 2;
            int containername = 3;
            int configurationstate = 4;
            int configurationproductivestate = 5;
            //int containermodifiername = 6;
            int containermodifieddate = 7;
            int containerconfiguration = 8;
            int configurationmodifieddate = 9;
            string objectTypeDirectory;
            string workingDirectory = string.Concat(@"\\", appSettings.Server, @"\EC_EDA");
            // ()
            {
                object @object;
                string @string;
                int fieldCount;
                List<object> row;
                string decrypted;
                string connectionName;
                string connectionString;
                StringBuilder sql = new StringBuilder();
                Array objectTypes = Enum.GetValues(typeof(ModuleInstanceTypeName));
                List<Tuple<string, string>> connections = new List<Tuple<string, string>>();
                if (dev)
                {
                    connectionName = @"DEV";
                    connectionString = EDAViewer.Singleton.Helper.Background.EDADatabase.GetEdaDevelopment();
                    decrypted = RijndaelEncryption.Decrypt(appSettings.IFXEDADatabasePassword, appSettings.Company);
                    connectionString = string.Concat(connectionString, decrypted, ";");
                    connections.Add(new Tuple<string, string>(connectionName, connectionString));
                }
                else
                {
                    connectionName = "Staging";
                    connectionString = EDAViewer.Singleton.Helper.Background.EDADatabase.GetEdaStaging();
                    decrypted = RijndaelEncryption.Decrypt(appSettings.ECEDADatabasePassword, appSettings.Company);
                    connectionString = string.Concat(connectionString, decrypted, ";");
                    connections.Add(new Tuple<string, string>(connectionName, connectionString));
                    connectionName = "Production";
                    connectionString = EDAViewer.Singleton.Helper.Background.EDADatabase.GetEdaProduction();
                    connectionString = string.Concat(connectionString, decrypted, ";");
                    connections.Add(new Tuple<string, string>(connectionName, connectionString));
                }
                foreach (Tuple<string, string> connection in connections)
                {
                    if (_ShuttingDown)
                        break;
                    rows.Add(connection.Item1, new Dictionary<ModuleInstanceTypeName, List<List<object>>>());
                    foreach (ModuleInstanceTypeName objectType in objectTypes)
                    {
                        if (_ShuttingDown)
                            break;
                        if (string.IsNullOrEmpty(edaDataCollectionPlansLastRun))
                        {
                            objectTypeDirectory = string.Concat(workingDirectory, @"\", connection.Item1, @"\", objectType);
                            if (!Directory.Exists(objectTypeDirectory))
                                Directory.CreateDirectory(objectTypeDirectory);
                            else
                                edaDataCollectionPlansLastRun = new DirectoryInfo(objectTypeDirectory).LastWriteTime.ToString(cSharpFormat);
                        }
                        if (!rows[connection.Item1].ContainsKey(objectType))
                            rows[connection.Item1].Add(objectType, new List<List<object>>());
                        using (Oracle.ManagedDataAccess.Client.OracleConnection oracleConnection = new Oracle.ManagedDataAccess.Client.OracleConnection(connection.Item2))
                        {
                            sql.Clear();
                            oracleConnection.Open();
                            sql.Append(" select v.moduleinstancetypename, v.unitname, ").
                                Append("        v.modulename, v.containername, v.configurationstate, ").
                                Append("        v.configurationproductivestate, v.containermodifiername, ").
                                Append("        v.containermodifieddate, v.containerconfiguration, v.configurationmodifieddate ").
                                Append(" from veditconfiguration v ").
                                Append(" where v.moduleinstancetypename = '").Append(objectType.ToString()).Append("' ");
                            //Append("   and v.containerversion in ('4.80', '4.111') ");
                            if (!string.IsNullOrEmpty(edaDataCollectionPlansLastRun))
                                sql.Append("   and greatest(v.containermodifieddate, v.configurationmodifieddate) >= to_date('").Append(edaDataCollectionPlansLastRun).Append("', '").Append(oracleFormat).Append("') ");
                            //Print(sql.ToString());
                            using (Oracle.ManagedDataAccess.Client.OracleCommand oracleCommand = new Oracle.ManagedDataAccess.Client.OracleCommand(sql.ToString(), oracleConnection))
                            {
                                using (Oracle.ManagedDataAccess.Client.OracleDataReader oracleDataReader = oracleCommand.ExecuteReader())
                                {
                                    fieldCount = oracleDataReader.FieldCount;
                                    while (oracleDataReader.Read())
                                    {
                                        if (_ShuttingDown)
                                            break;
                                        row = new List<object>();
                                        for (int c = 0; c < fieldCount; c++)
                                        {
                                            @object = oracleDataReader.GetValue(c);
                                            row.Add(@object);
                                            @string = @object.ToString();
                                            //if (@string.Length < 128)
                                            //    _Log.Debug(@string);
                                        }
                                        rows[connection.Item1][objectType].Add(row);
                                    }
                                }
                            }
                        };
                    }
                }
            }
            if (rows.Any())
            {
                string csv;
                string xml;
                string json;
                string text;
                string html;
                Common common;
                string emptyAPC;
                string fileName;
                string edaObjectFile;
                string goldDirectory;
                string unitDirectory;
                string replace = "$$$";
                string edaObjectDirectory;
                DateTime lastModifiedDate;
                DateTime containerModifiedDate;
                PDSFConfiguration configuration;
                DateTime configurationModifiedDate;
                JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true };
                foreach (KeyValuePair<string, Dictionary<ModuleInstanceTypeName, List<List<object>>>> databaseElement in rows)
                {
                    if (_ShuttingDown)
                        break;
                    emptyAPC = string.Concat(workingDirectory, @"\", databaseElement.Key, @"\EmptyAPC.xlsx");
                    foreach (KeyValuePair<ModuleInstanceTypeName, List<List<object>>> tableNameElement in databaseElement.Value)
                    {
                        if (_ShuttingDown)
                            break;
                        objectTypeDirectory = string.Concat(workingDirectory, @"\", databaseElement.Key, @"\", tableNameElement.Key);
                        foreach (List<object> rowItem in tableNameElement.Value)
                        {
                            if (_ShuttingDown)
                                break;
                            //foreach (var item in rowItem)
                            //    Print(item.ToString());
                            //if (!rowItem[containername].ToString().Contains("Plan_Pdsf_Health_Cp2Mg"))
                            //if (!rowItem[unitname].ToString().Contains("XRD04") || !rowItem[containername].ToString().Contains("Plan_Pdsf"))
                            //    continue;
                            containerModifiedDate = DateTime.Parse(rowItem[containermodifieddate].ToString());
                            configurationModifiedDate = DateTime.Parse(rowItem[configurationmodifieddate].ToString());
                            if (containerModifiedDate < configurationModifiedDate)
                                lastModifiedDate = configurationModifiedDate;
                            else
                                lastModifiedDate = containerModifiedDate;
                            goldDirectory = string.Concat(objectTypeDirectory, @"\", rowItem[unitname], @"\", rowItem[containername], @"\Gold");
                            if (!Directory.Exists(goldDirectory))
                                Directory.CreateDirectory(goldDirectory);
                            edaObjectDirectory = string.Concat(objectTypeDirectory, @"\", rowItem[unitname], @"\", rowItem[containername], @"\", rowItem[configurationstate], @"\", rowItem[configurationproductivestate], @"\", lastModifiedDate.ToString("yyyy-MM-dd_"), new TimeSpan(lastModifiedDate.Ticks - new DateTime(lastModifiedDate.Year, lastModifiedDate.Month, lastModifiedDate.Day).Ticks).TotalSeconds);
                            if (!Directory.Exists(edaObjectDirectory))
                                Directory.CreateDirectory(edaObjectDirectory);
                            edaObjectFile = string.Concat(edaObjectDirectory, @"\", rowItem[unitname], " ", rowItem[containername], " - ", replace, " - ", rowItem[configurationstate].ToString(), " ", rowItem[configurationproductivestate].ToString());
                            xml = rowItem[containerconfiguration].ToString();
                            //continue;
                            lock (_Lock)
                            {
                                fileName = string.Concat(edaObjectFile.Replace(replace, "Raw"), ".xml");
                                File.WriteAllText(fileName, xml);
                                try
                                { File.SetCreationTime(fileName, lastModifiedDate); File.SetLastWriteTime(fileName, lastModifiedDate); }
                                catch (Exception) { }
                                common = new Common(ModuleInstanceTypeName.Pdsf, rowItem[unitname], rowItem[containername], rowItem[configurationstate], rowItem[configurationproductivestate]);
                                configuration = ParseXML<PDSFConfiguration>(xml, throwExceptions: true);
                                if (!Directory.Exists(Path.GetPathRoot(configuration.Settings.StoragePath)))
                                    continue;
                                if (configuration is null)
                                    continue;
                                else
                                {
                                    common.Update(configuration);
                                    json = JsonSerializer.Serialize(configuration, configuration.GetType(), jsonSerializerOptions);
                                    if (!Directory.Exists(common.StoragePath))
                                        Directory.CreateDirectory(common.StoragePath);
                                    if (!common.StoragePath.Contains(common.UnitName) && (common.StoragePath.Contains(@"01EquipmentIntegration") || common.StoragePath.Contains(@"02BusinessIntegration")))
                                    {
                                        common.StoragePath = common.StoragePath.Replace("Traces", "Empty");
                                        if (!Directory.Exists(common.StoragePath))
                                            Directory.CreateDirectory(common.StoragePath);
                                        if (common.UnitName != "PRF01")
                                        {
                                            unitDirectory = string.Concat(Path.GetDirectoryName(common.StoragePath), @"\", common.UnitName);
                                            common.StoragePath = string.Concat(unitDirectory, @"\BadPath");
                                            if (!Directory.Exists(common.StoragePath))
                                                Directory.CreateDirectory(common.StoragePath);
                                            common.StoragePath = string.Concat(unitDirectory, @"\LogFile");
                                            if (!Directory.Exists(common.StoragePath))
                                                Directory.CreateDirectory(common.StoragePath);
                                            common.StoragePath = string.Concat(unitDirectory, @"\PollPath");
                                            if (!Directory.Exists(common.StoragePath))
                                                Directory.CreateDirectory(common.StoragePath);
                                        }
                                    }
                                    fileName = string.Concat(edaObjectFile.Replace(replace, "Partial"), ".csv");
                                    File.WriteAllText(fileName, common.ParametersAsCsv);
                                    try
                                    { File.SetCreationTime(fileName, lastModifiedDate); File.SetLastWriteTime(fileName, lastModifiedDate); }
                                    catch (Exception) { }
                                    fileName = string.Concat(edaObjectFile.Replace(replace, "Partial"), ".json");
                                    File.WriteAllText(fileName, json);
                                    text = EDAViewer.Singleton.Helper.Background.EdaDCP.GetText(edaObjectFile, common, json);
                                    fileName = string.Concat(edaObjectFile.Replace(replace, "Partial"), ".txt");
                                    File.WriteAllText(fileName, text);
                                    try
                                    { File.SetCreationTime(fileName, lastModifiedDate); File.SetLastWriteTime(fileName, lastModifiedDate); }
                                    catch (Exception) { }
                                    html = EDAViewer.Singleton.Helper.Background.EdaDCP.GetEdaObjectToHtml(edaObjectFile, common);
                                    fileName = string.Concat(edaObjectFile.Replace(replace, "Partial"), ".html");
                                    File.WriteAllText(fileName, html);
                                    try
                                    { File.SetCreationTime(fileName, lastModifiedDate); File.SetLastWriteTime(fileName, lastModifiedDate); }
                                    catch (Exception) { }
                                    xml = EDAViewer.Singleton.Helper.Background.EdaDCP.GetEdaObjectToDMSGridFormat(edaObjectFile, common, useAlias: false);
                                    fileName = string.Concat(edaObjectFile.Replace(replace, "DMSGridFormat"), ".xml");
                                    File.WriteAllText(fileName, xml);
                                    try
                                    { File.SetCreationTime(fileName, lastModifiedDate); File.SetLastWriteTime(fileName, lastModifiedDate); }
                                    catch (Exception) { }
                                    xml = EDAViewer.Singleton.Helper.Background.EdaDCP.GetEdaObjectToDMSGridFormat(edaObjectFile, common, useAlias: true);
                                    fileName = string.Concat(edaObjectFile.Replace(replace, "DMSGridFormat - Alias"), ".xml");
                                    File.WriteAllText(fileName, xml);
                                    try
                                    { File.SetCreationTime(fileName, lastModifiedDate); File.SetLastWriteTime(fileName, lastModifiedDate); }
                                    catch (Exception) { }
                                    csv = EDAViewer.Singleton.Helper.Background.EdaDCP.GetEdaObjectToAPCParameter(edaObjectFile, common);
                                    fileName = string.Concat(edaObjectFile.Replace(replace, "APCParameter"), ".csv");
                                    File.WriteAllText(fileName, csv);
                                    try
                                    { File.SetCreationTime(fileName, lastModifiedDate); File.SetLastWriteTime(fileName, lastModifiedDate); }
                                    catch (Exception) { }
                                    csv = EDAViewer.Singleton.Helper.Background.EdaDCP.GetEdaObjectToAPCRunKeyNumber(edaObjectFile, common);
                                    fileName = string.Concat(edaObjectFile.Replace(replace, "APCRunKeyNumber"), ".csv");
                                    File.WriteAllText(fileName, csv);
                                    try
                                    { File.SetCreationTime(fileName, lastModifiedDate); File.SetLastWriteTime(fileName, lastModifiedDate); }
                                    catch (Exception) { }
                                    fileName = string.Concat(edaObjectFile.Replace(replace, "APC"), ".xlsx");
                                    if (File.Exists(emptyAPC) && !File.Exists(fileName))
                                    {
                                        File.Copy(emptyAPC, fileName);
                                        try
                                        { File.SetCreationTime(fileName, lastModifiedDate); File.SetLastWriteTime(fileName, lastModifiedDate); }
                                        catch (Exception) { }
                                    }
                                }
                            }
                        }
                        try
                        { Directory.SetLastWriteTime(objectTypeDirectory, DateTime.Now); }
                        catch (Exception) { }
                    }
                }
            }
        }

    }

}