/// <summary>
 /// Instantiates the controller taking a logger.
 /// </summary>
 /// <param name="logger">The logger to use for logging.</param>
 /// <param name="configuration">The configuration settings.</param>
 /// <param name="dbContext">The databse context to use for retrieving the values to return.</param>
 /// <param name="hamnetDbAccess">The accessor to HamnetDB (needed to get coordinates for callsigns).</param>
 public ToolController(ILogger <RestController> logger, IConfiguration configuration, QueryResultDatabaseContext dbContext, IHamnetDbAccess hamnetDbAccess)
 {
     this.logger         = logger ?? throw new System.ArgumentNullException(nameof(logger), "The logger to use is null");
     this.configuration  = configuration ?? throw new System.ArgumentNullException(nameof(configuration), "The configuration to use is null");
     this.dbContext      = dbContext ?? throw new System.ArgumentNullException(nameof(dbContext), "The database context to take the data from is null");
     this.hamnetDbAccess = hamnetDbAccess ?? throw new System.ArgumentNullException(nameof(dbContext), "The HamnetDB access singleton is null");
 }
        /// <summary>
        /// Records a failing query.
        /// </summary>
        /// <param name="databaseContext">The database context to work with.</param>
        /// <param name="ex">The exception that caused the failure.</param>
        /// <param name="pair">The pair of hosts inside the subnet to query.</param>
        private void DoRecordFailingRssiQueryEntry(QueryResultDatabaseContext databaseContext, Exception ex, KeyValuePair <IHamnetDbSubnet, IHamnetDbHosts> pair)
        {
            var failingSubnetString = pair.Key.Subnet.ToString();
            var failEntry           = databaseContext.RssiFailingQueries.Find(failingSubnetString);
            var hamnetSnmpEx        = ex as HamnetSnmpException;

            if (failEntry == null)
            {
                failEntry = new RssiFailingQuery
                {
                    Subnet        = failingSubnetString,
                    AffectedHosts = (hamnetSnmpEx != null) ? hamnetSnmpEx.AffectedHosts : pair.Value.Select(h => h.Address?.ToString()).ToArray()
                };

                databaseContext.RssiFailingQueries.Add(failEntry);
            }

            failEntry.TimeStamp   = DateTime.UtcNow;
            failEntry.PenaltyInfo = this.failureRetryFilteringDataHandler?.QueryPenaltyDetails(QueryType.RssiQuery, pair.Key.Subnet);

#if DEBUG
            failEntry.ErrorInfo = ex?.ToString() ?? string.Empty;
#else
            failEntry.ErrorInfo = ex?.Message ?? string.Empty;
#endif
        }
 /// <summary>
 /// Instantiates the controller taking a logger.
 /// </summary>
 /// <param name="logger">The logger to use for logging.</param>
 /// <param name="configuration">The configuration settings.</param>
 /// <param name="dbContext">The databse context to use for retrieving the values to return.</param>
 /// <param name="retryFeasibleHandler">The handler to check whether retry is feasible.</param>
 public BgpController(ILogger <RestController> logger, IConfiguration configuration, QueryResultDatabaseContext dbContext, IFailureRetryFilteringDataHandler retryFeasibleHandler)
 {
     this.logger               = logger ?? throw new ArgumentNullException(nameof(logger), "The logger to use is null");
     this.configuration        = configuration ?? throw new ArgumentNullException(nameof(configuration), "The configuration to use is null");
     this.dbContext            = dbContext ?? throw new ArgumentNullException(nameof(dbContext), "The database context to take the data from is null");
     this.retryFeasibleHandler = retryFeasibleHandler ?? throw new ArgumentNullException(nameof(retryFeasibleHandler), "The retry feasible handler singleton has not been provided by DI engine");
 }
        /// <summary>
        /// Adds or modifies an RSSI entry for the given link detail.
        /// </summary>
        /// <param name="databaseContext">The database context to work with.</param>
        /// <param name="subnet">The subnet that is being recorded.</param>
        /// <param name="queryTime">The time of the data aquisition (recorded with the data).</param>
        /// <param name="linkDetail">The link details to record.</param>
        /// <param name="adressToSearch">The host address to search for (and modify if found).</param>
        /// <param name="rssiToSet">The RSSI value to record.</param>
        /// <param name="hostCall">The call of the foreign host.</param>
        /// <param name="description">The description for this value.</param>
        private void SetNewRssiForLink(QueryResultDatabaseContext databaseContext, IHamnetDbSubnet subnet, DateTime queryTime, ILinkDetail linkDetail, string adressToSearch, double rssiToSet, string hostCall, string description)
        {
            var adressEntry = databaseContext.RssiValues.Find(adressToSearch);

            if (adressEntry == null)
            {
                adressEntry = new Rssi
                {
                    ForeignId    = adressToSearch,
                    Metric       = RssiMetricName,
                    MetricId     = RssiMetricId,
                    ParentSubnet = subnet.Subnet?.ToString(),
                    Description  = description,
                    ForeignCall  = hostCall
                };

                databaseContext.RssiValues.Add(adressEntry);
            }

            adressEntry.RssiValue = rssiToSet.ToString("0.0");

            // we're setting a couple of values here again so that migrated database will get the value added
            adressEntry.ParentSubnet = subnet.Subnet?.ToString();
            adressEntry.Description  = description;
            adressEntry.ForeignCall  = hostCall;

            adressEntry.TimeStampString = queryTime.ToUniversalTime().ToString("yyyy-MM-ddTHH\\:mm\\:sszzz");
            adressEntry.UnixTimeStamp   = (ulong)queryTime.ToUniversalTime().Subtract(Program.UnixTimeStampBase).TotalSeconds;
        }
        /// <summary>
        /// Records a failing query.
        /// </summary>
        /// <param name="databaseContext">The database context to work with.</param>
        /// <param name="ex">The exception that caused the failure.</param>
        /// <param name="host">The host inside that failed the query.</param>
        private void DoRecordFailingBgpQueryEntry(QueryResultDatabaseContext databaseContext, Exception ex, IHamnetDbHost host)
        {
            var failingHost  = host.Address.ToString();
            var failEntry    = databaseContext.BgpFailingQueries.Find(failingHost);
            var hamnetSnmpEx = ex as HamnetSnmpException;

            if (failEntry == null)
            {
                failEntry = new BgpFailingQuery
                {
                    Host = failingHost
                };

                databaseContext.BgpFailingQueries.Add(failEntry);
            }

            failEntry.TimeStamp   = DateTime.UtcNow;
            failEntry.PenaltyInfo = this.failureRetryFilteringDataHandler?.QueryPenaltyDetails(QueryType.BgpQuery, host.Address);

#if DEBUG
            failEntry.ErrorInfo = ex?.ToString() ?? string.Empty;
#else
            failEntry.ErrorInfo = ex?.Message ?? string.Empty;
#endif
        }
        /// <summary>
        /// Records the BGP results in the database.
        /// </summary>
        /// <param name="databaseContext">The database context to work with.</param>
        /// <param name="host">The host data of the query.</param>
        /// <param name="bgpPeers">The BGP peers to record.</param>
        /// <param name="queryTime">The time of the data aquisition (recorded with the data).</param>
        private void DoRecordBgpDetailsInDatabase(QueryResultDatabaseContext databaseContext, IHamnetDbHost host, IBgpPeers bgpPeers, DateTime queryTime)
        {
            var localAdressToSearch = host.Address.ToString();

            foreach (var item in bgpPeers.Details)
            {
                var remoteAdressToSearch = item.RemoteAddress.ToString();
                var peerEntry            = databaseContext.BgpPeers.SingleOrDefault(p => (p.LocalAddress == localAdressToSearch) && (p.RemoteAddress == remoteAdressToSearch));
                if (peerEntry == null)
                {
                    peerEntry = new BgpPeerData
                    {
                        LocalAddress  = localAdressToSearch,
                        LocalCallsign = host.Callsign.ToUpperInvariant(),
                        RemoteAddress = remoteAdressToSearch
                    };

                    databaseContext.BgpPeers.Add(peerEntry);
                }

                peerEntry.LocalCallsign = host.Callsign.ToUpperInvariant();
                peerEntry.PeeringName   = item.Name;
                peerEntry.PeeringState  = item.State;
                peerEntry.PrefixCount   = item.PrefixCount;
                peerEntry.Uptime        = item.Uptime.ToString();

                peerEntry.TimeStampString = queryTime.ToUniversalTime().ToString("yyyy-MM-ddTHH\\:mm\\:sszzz");
                peerEntry.UnixTimeStamp   = (ulong)queryTime.ToUniversalTime().Subtract(Program.UnixTimeStampBase).TotalSeconds;
            }
        }
        /// <summary>
        /// Constructs taking the logger.
        /// </summary>
        /// <param name="logger">The logger to use.</param>
        /// <param name="configuration">The service configuration.</param>
        public MaintenanceService(ILogger <MaintenanceService> logger, IConfiguration configuration)
        {
            this.logger        = logger;
            this.configuration = configuration;

            this.resultDatabaseContext = QueryResultDatabaseProvider.Instance.CreateContext();
        }
 /// <summary>
 /// Disposes off the result database context.
 /// </summary>
 private void DisposeDatabaseContext()
 {
     if (this.resultDatabaseContext != null)
     {
         this.resultDatabaseContext.Dispose();
         this.resultDatabaseContext = null;
     }
 }
        /// <summary>
        /// Deletes an entry in the failing query table.
        /// </summary>
        /// <param name="databaseContext">The database context to work with.</param>
        /// <param name="host">The host which serves as a key to the entry to delete.</param>
        private void DoDeleteFailingBgpQuery(QueryResultDatabaseContext databaseContext, IHamnetDbHost host)
        {
            var failingHostString = host.Address.ToString();
            var entryToRemove     = databaseContext.BgpFailingQueries.SingleOrDefault(e => e.Host == failingHostString);

            if (entryToRemove != null)
            {
                log.Debug($"Removing fail entry for subnet '{failingHostString}'");
                databaseContext.Remove(entryToRemove);
            }
        }
        /// <summary>
        /// Deletes an entry in the failing query table.
        /// </summary>
        /// <param name="databaseContext">The database context to work with.</param>
        /// <param name="subnet">The subnet which serves as key to the entry to delete.</param>
        private void DoDeleteFailingRssiQuery(QueryResultDatabaseContext databaseContext, IHamnetDbSubnet subnet)
        {
            var failingSubnetString = subnet.Subnet.ToString();
            var entryToRemove       = databaseContext.RssiFailingQueries.SingleOrDefault(e => e.Subnet == failingSubnetString);

            if (entryToRemove != null)
            {
                log.Debug($"Removing fail entry for subnet '{failingSubnetString}'");
                databaseContext.Remove(entryToRemove);
            }
        }
        /// <summary>
        /// Records the link details in the database.
        /// </summary>
        /// <param name="databaseContext">The database context to work with.</param>
        /// <param name="inputData">The input data of the query.</param>
        /// <param name="linkDetails">The link details to record.</param>
        /// <param name="queryTime">The time of the data aquisition (recorded with the data).</param>
        private void DoRecordRssiDetailsInDatabase(QueryResultDatabaseContext databaseContext, KeyValuePair <IHamnetDbSubnet, IHamnetDbHosts> inputData, ILinkDetails linkDetails, DateTime queryTime)
        {
            string host1call = inputData.Value.First().Callsign?.ToUpperInvariant();
            string host2call = inputData.Value.Last().Callsign?.ToUpperInvariant();

            foreach (var item in linkDetails.Details)
            {
                this.SetNewRssiForLink(databaseContext, inputData.Key, queryTime, item, item.Address1.ToString(), item.RxLevel1at2, host1call, $"{host1call} at {host2call}");
                this.SetNewRssiForLink(databaseContext, inputData.Key, queryTime, item, item.Address2.ToString(), item.RxLevel2at1, host2call, $"{host2call} at {host1call}");
            }
        }
        /// <summary>
        /// Creates a new database context for the result database.
        /// </summary>
        private void NewDatabaseContext()
        {
            this.DisposeDatabaseContext();

            this.resultDatabaseContext = QueryResultDatabaseProvider.Instance.CreateContext();
        }
 /// <summary>
 /// Instantiates the controller taking a logger.
 /// </summary>
 /// <param name="logger">The logger to use for logging.</param>
 /// <param name="dbContext">The database context to get the data from.</param>
 public VwRestRssiController(ILogger <VwRestRssiController> logger, QueryResultDatabaseContext dbContext)
 {
     this.logger    = logger ?? throw new System.ArgumentNullException(nameof(logger), "The logger to use is null");
     this.dbContext = dbContext ?? throw new System.ArgumentNullException(nameof(dbContext), "The database context to take the data from is null");
 }
 /// <summary>
 /// Instantiates the controller taking a logger.
 /// </summary>
 /// <param name="logger">The logger to use for logging.</param>
 /// <param name="configuration">The configuration settings.</param>
 /// <param name="dbContext">The query result database context to use.</param>
 public StatusController(ILogger <RestController> logger, IConfiguration configuration, QueryResultDatabaseContext dbContext)
 {
     this.dbContext     = dbContext ?? throw new ArgumentNullException(nameof(dbContext), "The database context is null");
     this.logger        = logger ?? throw new System.ArgumentNullException(nameof(logger), "The logger to use is null");
     this.configuration = configuration ?? throw new System.ArgumentNullException(nameof(configuration), "The configuration to use is null");
 }