//******************************************************************************************************
//  Settings.cs - Gbtc
//
//  Copyright © 2026, Grid Protection Alliance.  All Rights Reserved.
//
//  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
//  the NOTICE file distributed with this work for additional information regarding copyright ownership.
//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
//  file except in compliance with the License. You may obtain a copy of the License at:
//
//      http://opensource.org/licenses/MIT
//
//  Unless agreed to in writing, the subject software distributed under the License is distributed on an
//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
//  License for the specific language governing permissions and limitations.
//
//  Code Modification History:
//  ----------------------------------------------------------------------------------------------------
//  01/15/2026 - J. Ritchie Carroll
//       Generated original version of source code.
//
//******************************************************************************************************

using System.Text.RegularExpressions;
using Gemstone.Configuration;
using ConfigSettings = Gemstone.Configuration.Settings;

namespace DNP3ConfigLoader;

/// <summary>
/// Represents application configuration settings.
/// </summary>
public class Settings : IDefineSettings
{
    /// <summary>
    /// Name of the host service to load configuration from.
    /// </summary>
    public string HostService { get; set; } = s_section.HostService;

    /// <summary>
    /// Default installation path for the host service.
    /// </summary>
    public string DefaultInstallPath { get; set; } = s_section.DefaultInstallPath;

    /// <summary>
    /// Define CIM export file path, optional -- this file is used to automate creation
    /// of Devices file (DeviceCSVFilePath) and related tag map files.
    /// </summary>
    public string CIMExportFilePath { get; set; } = s_section.CIMExportFilePath;

    /// <summary>
    /// Source devices CSV file (include path if not in same directory as executable).
    /// </summary>
    public string DeviceCSVFilePath { get; set; } = s_section.DeviceCSVFilePath;

    /// <summary>
    /// Default DNP3 port for devices.
    /// </summary>
    public int DefaultDNP3Port { get; set; } = s_section.DefaultDNP3Port;

    /// <summary>
    /// openPDC database connection string.
    /// </summary>
    public string ConnectionString { get; set; } = s_section.ConnectionString;

    /// <summary>
    /// Target historian for measurements.
    /// </summary>
    public string TargetSource { get; set; } = s_section.TargetSource;

    /// <summary>
    /// Default company acronym.
    /// </summary>
    public string CompanyAcronym { get; set; } = s_section.CompanyAcronym;

    /// <summary>
    /// Version 2011 of IEEE C37.118 allows analog and digital fields to be 255 characters long,
    /// older versions of the standard only allow 16 characters.
    /// </summary>
    public int IEEEC37_118Version { get; set; } = s_section.IEEEC37_118Version;

    /// <summary>
    /// Determines if DNP3 quality flags should be mapped to measurement state flags.
    /// Note: option works to include flags for IEEE 2664 (STTP) style outputs.
    /// </summary>
    public bool MapQualityToStateFlags { get; set; } = s_section.MapQualityToStateFlags;

    /// <summary>
    /// Determines if DNP3 quality flags should be published as separate measurement outputs.
    /// Note: option works to include flags for IEEE C37.118 or IEEE 2664 (STTP) style outputs.
    /// </summary>
    public bool PublishFlagsAsSeparateMeasurements { get; set; } = s_section.PublishFlagsAsSeparateMeasurements;

    /// <summary>
    /// Regular expression pattern used to match tag names for quality flag outputs.
    /// Note: expression expects capture groups "TagName" and "SignalType".
    /// In the case of Dominion, tag names do not include the ":ALOG" or ":DIGI" suffix
    /// so the pattern is set up to make that portion optional.
    /// </summary>
    public string TagMatchPattern { get; set; } = s_section.TagMatchPattern;

    /// <summary>
    /// Suffix to append to the tag name for DNP3 quality flag measurement outputs.
    /// </summary>
    public string QualityTagSuffix { get; set; } = s_section.QualityTagSuffix;

    /// <summary>
    /// Interval, in seconds, at which the adapter will poll the DNP3 device.
    /// Note: set to zero for on-change only.
    /// </summary>
    public double PollingInterval { get; set; } = s_section.PollingInterval;

    /// <summary>
    /// Valid +/- time range, in seconds, for calculation input measurement values before
    /// they are considered stale -- keep value small, e.g., 2-4 seconds, only consequence
    /// is assigning bad time quality to the calculated output measurement.
    /// </summary>
    public double ValidTimeRange { get; set; } = s_section.ValidTimeRange;

    /// <summary>
    /// Output directory for DNP3 configuration files.
    /// </summary>
    public string OutputDirectory { get; set; } = s_section.OutputDirectory;

    /// <summary>
    /// Template directory for XML templates.
    /// </summary>
    public string TemplateDirectory { get; set; } = s_section.TemplateDirectory;

    /// <summary>
    /// Tag map directory.
    /// </summary>
    public string TagMapDirectory { get; set; } = s_section.TagMapDirectory;

    /// <summary>
    /// Path to openPDCConsole.exe application.
    /// </summary>
    public string OpenPDCConsolePath { get; set; } = s_section.OpenPDCConsolePath;

    /// <summary>
    /// Determines if adapters should be initialized after configuration.
    /// Note: Updated DNP3 config files must be loaded into openPDC installation folder before
    /// adapter can be initialized. Suggest setting OutputDirectory to openPDC installation
    /// folder, e.g., "C:\\Program Files\\openPDC\\DNP3Config\\", before enabling this option.
    /// Alternately, this application can be run from openPDC install folder to use relative paths.
    /// </summary>
    public bool InitializeAdapters { get; set; } = s_section.InitializeAdapters;

    /// <summary>
    /// Comment character for CSV file rows.
    /// </summary>
    public char CommentChar { get; set; } = s_section.CommentChar;

    /// <summary>
    /// Default access ID for devices.
    /// </summary>
    // Referenced by `Device` model default value expression
    public int DefaultAccessID { get; set; } = s_section.DefaultAccessID;

    /// <summary>
    /// Default frames per second for devices.
    /// </summary>
    // Referenced by `Device` model default value expression
    public int DefaultFramesPerSecond { get; set; } = s_section.DefaultFramesPerSecond;

    /// <summary>
    /// Default measurement reporting interval for devices.
    /// </summary>
    // Referenced by `Device` model default value expression
    public int DefaultMeasurementReportingInterval { get; set; } = s_section.DefaultMeasurementReportingInterval;

    /// <summary>
    /// Default value for determining if new devices should be enabled.
    /// </summary>
    public bool DefaultEnableDevices { get; set; } = s_section.DefaultEnableDevices;

    /// <summary>
    /// Delay in milliseconds between commands sent to openPDCConsole.
    /// </summary>
    public int CommandDelayMilliseconds { get; set; } = s_section.CommandDelayMilliseconds;

    /// <summary>
    /// Timeout in milliseconds for openPDCConsole process.
    /// </summary>
    public int ProcessTimeoutMilliseconds { get; set; } = s_section.ProcessTimeoutMilliseconds;

    private static dynamic s_section = null!;

    /// <inheritdoc />
    public static void DefineSettings(ConfigSettings settings, string settingsCategory)
    {
        s_section = settings[settingsCategory];

        s_section.HostService = ("openPDC", "Name of the host service to load configuration from");
        s_section.DefaultInstallPath = (@"C:\Program Files\openPDC\", "Default installation path for the host service");
        s_section.CIMExportFilePath = (string.Empty, "CIM export file path");
        s_section.DeviceCSVFilePath = ("Devices.csv", "Source devices CSV file (include path if not in same directory as executable)");
        s_section.DefaultDNP3Port = (20006, "Default DNP3 port for devices");
        s_section.ConnectionString = (@"Data Source=.\SQLEXPRESS; Initial Catalog=openPDC; Integrated Security=SSPI", "openPDC database connection string");
        s_section.TargetSource = ("PPA", "Target historian for measurements");
        s_section.CompanyAcronym = ("DOM", "Default company acronym");
        s_section.IEEEC37_118Version = (2011, "Version 2011 of IEEE C37.118 allows analog and digital fields to be 255 characters long, older versions of the standard only allow 16 characters");
        s_section.MapQualityToStateFlags = (true, "Determines if DNP3 quality flags should be mapped to measurement state flags");
        s_section.PublishFlagsAsSeparateMeasurements = (false, "Determines if DNP3 quality flags should be published as separate measurement outputs");
        s_section.TagMatchPattern = (@"(?<TagName>.+?)((?<SignalType>:(ALOG|DIGI))\d+)?", "Regular expression pattern used to match tag names for quality flag outputs -- expression expects capture groups \"TagName\" and \"SignalType\"");
        s_section.QualityTagSuffix = ("!FLAGS", "Suffix to append to the tag name for DNP3 quality flag measurement outputs");
        s_section.PollingInterval = (2.0D, "Interval, in seconds, at which the adapter will poll the DNP3 device -- set to zero for on-change only");
        s_section.ValidTimeRange = (4.0D, "Valid +/- time range, in seconds, for calculation input measurement values before they are considered stale");
        s_section.OutputDirectory = ("dnp3config/", """Output directory for DNP3 configuration files -- use "C:\Program Files\openPDC\DNP3Config\" for live updates""");
        s_section.TemplateDirectory = ("template/", "Template directory for XML templates");
        s_section.TagMapDirectory = ("tagmaps/", "Tag map directory");
        s_section.OpenPDCConsolePath = (@"C:\Program Files\openPDC\openPDCConsole.exe", "Path to openPDCConsole.exe application");
        s_section.InitializeAdapters = (false, "Determines if adapters should be initialized after configuration -- Updated DNP3 config files must be loaded into openPDC installation folder before adapter can be initialized");
        s_section.CommentChar = ('#', "Comment character for CSV file rows");
        s_section.DefaultAccessID = (1, "Default access ID for devices");
        s_section.DefaultFramesPerSecond = (30, "Default frames per second for devices");
        s_section.DefaultMeasurementReportingInterval = (100000, "Default measurement reporting interval for devices");
        s_section.DefaultEnableDevices = (false, "Default value for determining if new devices should be enabled");
        s_section.CommandDelayMilliseconds = (1000, "Delay in milliseconds between commands sent to openPDCConsole");
        s_section.ProcessTimeoutMilliseconds = (30000, "Timeout in milliseconds for openPDCConsole process");
    }
}
