using System; using System.Drawing; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using Pingerino.Infrastructure; using Pingerino.Services.Interfaces; using Pingerino.Utilities; namespace Pingerino { public partial class FormPinger : Form { #region Services private readonly IPingService _pingService; private readonly INetworkService _networkService; private readonly IConfigurationService _configService; private readonly ISystemService _systemService; private readonly ILoggingService _logger; #endregion #region Fields private CancellationTokenSource _cancellationTokenSource; private System.Threading.Timer _publicIpTimer; private string _currentPublicIp = "Loading..."; private bool _disposed; #endregion #region Constructor public FormPinger() { InitializeComponent(); // Get services from container _pingService = ServiceContainer.Instance.GetService(); _networkService = ServiceContainer.Instance.GetService(); _configService = ServiceContainer.Instance.GetService(); _systemService = ServiceContainer.Instance.GetService(); _logger = ServiceContainer.Instance.GetService(); InitializeFormAsync(); } #endregion #region Initialization private async void InitializeFormAsync() { try { _logger.LogInformation("Initializing Pingerino application"); // Setup UI SetupUI(); // Load configuration LoadConfiguration(); // Setup event handlers SetupEventHandlers(); // Initialize services await InitializeServicesAsync(); _logger.LogInformation("Application initialized successfully"); } catch (Exception ex) { _logger.LogError("Error during application initialization", ex); UIHelper.ShowError($"Failed to initialize application: {ex.Message}"); } } private void SetupUI() { // Apply rounded corners UIHelper.ApplyRoundedCorners(this); // Enable form dragging UIHelper.EnableFormDragging(this, panel1); // Enable double buffering this.DoubleBuffered = true; // Initialize context menu InitializeContextMenu(); // Set default values textBoxInterval.Text = "300"; ipAddress.Select(); } private void LoadConfiguration() { try { // Load form position var savedPosition = _configService.FormPosition; if (savedPosition != Point.Empty) { this.Location = savedPosition; } // Load IP address and interval ipAddress.Text = _configService.IpAddressValue; textBoxInterval.Text = _configService.TextBoxIntervalValue; _logger.LogInformation("Configuration loaded successfully"); } catch (Exception ex) { _logger.LogError("Error loading configuration", ex); } } private void SetupEventHandlers() { // Form events this.Load += FormPinger_Load; this.FormClosing += FormPinger_FormClosing; // Input events textBoxInterval.KeyDown += TextBoxInterval_KeyDown; ipAddress.KeyDown += TextBoxIpAddress_KeyDown; // Button events are handled by Designer file - no manual registration needed // Service events _pingService.PingCompleted += PingService_PingCompleted; _pingService.StatisticsUpdated += PingService_StatisticsUpdated; } private async System.Threading.Tasks.Task InitializeServicesAsync() { // Start public IP fetching await StartPublicIpUpdatesAsync(); // Start initial ping if auto-ping is enabled if (_configService.AutoPingEnabled) { await StartPingingAsync(); } } #endregion #region Event Handlers private async void FormPinger_Load(object sender, EventArgs e) { await UpdateNetworkStatisticsAsync(); } private async void FormPinger_FormClosing(object sender, FormClosingEventArgs e) { try { // Save configuration _configService.FormPosition = this.Location; _configService.IpAddressValue = ipAddress.Text; _configService.TextBoxIntervalValue = textBoxInterval.Text; _configService.Save(); // Stop services await _pingService.StopPingingAsync(); _publicIpTimer?.Dispose(); _cancellationTokenSource?.Cancel(); _cancellationTokenSource?.Dispose(); _logger.LogInformation("Application closing - configuration saved"); } catch (Exception ex) { _logger.LogError("Error during application shutdown", ex); } } private async void TextBoxInterval_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { await HandleIntervalChangeAsync(); } } private async void TextBoxIpAddress_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { await HandleIpAddressChangeAsync(); } } private void ButtonExit_Click(object sender, EventArgs e) { Application.Exit(); } private void ButtonMinimize_Click(object sender, EventArgs e) { WindowState = FormWindowState.Minimized; } private async void ButtonClearAll_Click(object sender, EventArgs e) { await ClearAllDataAsync(); } private async void ButtonCleanNetwork_Click(object sender, EventArgs e) { await ResetNetworkAsync(); } private async void ButtonCleanTemp_Click(object sender, EventArgs e) { await CleanTemporaryFilesAsync(); } private void ButtonNetworkAdapters_Click(object sender, EventArgs e) { _systemService.OpenNetworkAdaptersControl(); } private void ButtonMenu_Click(object sender, EventArgs e) { contextMenuStrip1.Show(ButtonMenu, 0, ButtonMenu.Height); } // Additional event handlers referenced by Designer private void COPYRIGHT_TextChanged(object sender, EventArgs e) { // Copyright text changed - no action needed } private void TextBoxInterval_TextChanged(object sender, EventArgs e) { // Interval text changed - we handle this with KeyDown events instead } private void TextBoxJitter_TextChanged(object sender, EventArgs e) { // Jitter display text changed - no action needed (read-only display) } private void Label1_Click(object sender, EventArgs e) { // Label click - no action needed } private void LabelJitter_Click(object sender, EventArgs e) { // Jitter label click - no action needed } private void IpAddress_Click(object sender, EventArgs e) { // IP address control click - no action needed } private void Label2_Click(object sender, EventArgs e) { // Label click - no action needed } private void Panel1_MouseDown(object sender, MouseEventArgs e) { // Panel mouse down - form dragging is handled by UIHelper.EnableFormDragging } private void Label3_MouseDown(object sender, MouseEventArgs e) { // Label mouse down - form dragging is handled by UIHelper.EnableFormDragging } private void TextBoxMinJitter_TextChanged(object sender, EventArgs e) { // Min jitter display text changed - no action needed (read-only display) } #endregion #region Service Event Handlers private void PingService_PingCompleted(object sender, PingResultEventArgs e) { UIHelper.SafeInvoke(this, () => { var timestamp = e.Timestamp.ToString("HH:mm:ss.ff"); var status = e.Result.IsSuccess ? $"Pinging {e.Result.IpAddress} - Success" : $"Ping to {e.Result.IpAddress} - Failed: {e.Result.Status}"; var rtt = e.Result.IsSuccess ? $"{e.Result.RoundTripTime} ms" : ""; AddLineToOutput(timestamp, status, rtt); }); } private void PingService_StatisticsUpdated(object sender, NetworkStatisticsEventArgs e) { UIHelper.SafeInvoke(this, () => { UpdateStatisticsDisplay(e.Statistics); }); } #endregion #region Business Logic Methods private async System.Threading.Tasks.Task HandleIntervalChangeAsync() { try { var intervalText = ValidationHelper.SanitizeInput(textBoxInterval.Text); if (!ValidationHelper.IsValidPingInterval(intervalText, out int interval)) { UIHelper.ShowError(ValidationHelper.GetValidationErrorMessage("Interval", intervalText)); return; } await ClearAllDataAsync(); if (_pingService.IsRunning) { await _pingService.StopPingingAsync(); await _pingService.StartPingingAsync(ipAddress.Text, interval); } _logger.LogInformation($"Ping interval changed to {interval}ms"); } catch (Exception ex) { _logger.LogError("Error changing ping interval", ex); UIHelper.ShowError("Failed to change ping interval"); } } private async System.Threading.Tasks.Task HandleIpAddressChangeAsync() { try { var address = ValidationHelper.SanitizeInput(ipAddress.Text); if (!ValidationHelper.IsValidIpAddressOrHostname(address)) { UIHelper.ShowError(ValidationHelper.GetValidationErrorMessage("IP Address", address)); return; } await ClearAllDataAsync(); // Save to configuration _configService.LastUsedIpAddress = address; _configService.Save(); // Restart pinging if it was running if (_pingService.IsRunning) { var interval = int.Parse(textBoxInterval.Text); await _pingService.StopPingingAsync(); await _pingService.StartPingingAsync(address, interval); } _logger.LogInformation($"IP address changed to {address}"); } catch (Exception ex) { _logger.LogError("Error changing IP address", ex); UIHelper.ShowError("Failed to change IP address"); } } private async System.Threading.Tasks.Task StartPingingAsync() { try { var address = ValidationHelper.SanitizeInput(ipAddress.Text); var intervalText = ValidationHelper.SanitizeInput(textBoxInterval.Text); if (!ValidationHelper.IsValidIpAddressOrHostname(address)) { UIHelper.ShowError("Please enter a valid IP address or hostname"); return; } if (!ValidationHelper.IsValidPingInterval(intervalText, out int interval)) { UIHelper.ShowError("Please enter a valid ping interval (100-60000 ms)"); return; } await _pingService.StartPingingAsync(address, interval); _logger.LogInformation($"Started pinging {address} with {interval}ms interval"); } catch (Exception ex) { _logger.LogError("Error starting ping service", ex); UIHelper.ShowError("Failed to start pinging"); } } private async System.Threading.Tasks.Task ClearAllDataAsync() { try { _pingService.ClearStatistics(); UIHelper.SafeInvoke(this, () => { dataGridView1.Rows.Clear(); dataGridView2.Rows.Clear(); // Reset statistics display ResetStatisticsDisplay(); }); // Refresh public IP await FetchPublicIpAsync(); _logger.LogInformation("All data cleared"); } catch (Exception ex) { _logger.LogError("Error clearing data", ex); } } private async System.Threading.Tasks.Task ResetNetworkAsync() { try { if (!UIHelper.ShowConfirmation( "This action will release and renew network settings. Are you sure you want to continue?", "Confirm Network Reset")) { return; } var adapter = await _networkService.GetActiveEthernetAdapterAsync(); if (adapter == null) { UIHelper.ShowError("No active Ethernet adapter found"); return; } ButtonCleanNetwork.Visible = false; var progress = new Progress(value => { UIHelper.SafeInvoke(this, () => { // Update progress if you have a progress bar }); }); var success = await _networkService.ResetNetworkAdapterAsync(adapter.Name, progress); UIHelper.SafeInvoke(this, () => { ButtonCleanNetwork.Visible = true; if (success) { AddLineToOutput(DateTime.Now.ToString("HH:mm:ss.ff"), "Network reset completed", ""); } else { UIHelper.ShowError("Network reset failed"); } }); } catch (Exception ex) { _logger.LogError("Error during network reset", ex); UIHelper.SafeInvoke(this, () => { ButtonCleanNetwork.Visible = true; UIHelper.ShowError("Network reset failed"); }); } } private async System.Threading.Tasks.Task CleanTemporaryFilesAsync() { try { buttonCleanTemp.Visible = false; var progress = new Progress(value => { UIHelper.SafeInvoke(this, () => { // Update progress if you have a progress bar }); }); var success = await _systemService.CleanTemporaryFilesAsync(progress); UIHelper.SafeInvoke(this, () => { buttonCleanTemp.Visible = true; if (success) { UIHelper.ShowInformation("Temporary files cleaned successfully"); } else { UIHelper.ShowError("Failed to clean temporary files"); } }); } catch (Exception ex) { _logger.LogError("Error cleaning temporary files", ex); UIHelper.SafeInvoke(this, () => { buttonCleanTemp.Visible = true; UIHelper.ShowError("Failed to clean temporary files"); }); } } private async System.Threading.Tasks.Task StartPublicIpUpdatesAsync() { await FetchPublicIpAsync(); // Update public IP every hour _publicIpTimer = new System.Threading.Timer(async _ => await FetchPublicIpAsync(), null, TimeSpan.FromHours(1), TimeSpan.FromHours(1)); } private async System.Threading.Tasks.Task FetchPublicIpAsync() { try { _cancellationTokenSource?.Cancel(); _cancellationTokenSource = new CancellationTokenSource(); var publicIp = await _networkService.GetPublicIpAddressAsync(_cancellationTokenSource.Token); _currentPublicIp = publicIp; UIHelper.SafeInvoke(this, () => { label4.Text = $"Public IP: {publicIp}"; }); } catch (Exception ex) { _logger.LogError("Error fetching public IP", ex); UIHelper.SafeInvoke(this, () => { label4.Text = "Error fetching IP"; }); // Retry after delay await System.Threading.Tasks.Task.Delay(30000); _ = System.Threading.Tasks.Task.Run(async () => await FetchPublicIpAsync()); } } private async System.Threading.Tasks.Task UpdateNetworkStatisticsAsync() { try { var address = ValidationHelper.SanitizeInput(ipAddress.Text); if (!ValidationHelper.IsValidIpAddressOrHostname(address)) return; // This will trigger the StatisticsUpdated event await System.Threading.Tasks.Task.Run(async () => { var result = await _pingService.PingOnceAsync(address); // Statistics are automatically calculated and updated via events }); } catch (Exception ex) { _logger.LogError("Error updating network statistics", ex); } } #endregion #region UI Helper Methods private void AddLineToOutput(string time, string status, string rtt = "") { if (dataGridView1.IsDisposed || !dataGridView1.IsHandleCreated) return; UIHelper.SafeInvoke(this, () => { dataGridView1.Rows.Add(new object[] { time, status, rtt }); // Auto-scroll to bottom if (dataGridView1.Rows.Count > 0) { dataGridView1.FirstDisplayedScrollingRowIndex = dataGridView1.Rows.Count - 1; } }); } private void UpdateStatisticsDisplay(NetworkStatistics stats) { try { // Update ping statistics if (dataGridView2.Rows.Count == 0) { dataGridView2.Rows.Add(); } var row = dataGridView2.Rows[0]; row.Cells["MaxPing"].Value = $"{stats.MaxPing} ms"; row.Cells["MinPing"].Value = $"{stats.MinPing} ms"; row.Cells["AvgPing"].Value = $"{stats.AveragePing:F0} ms"; // Update jitter statistics TextBoxMaxJitter.Text = $"{stats.MaxJitter:F0} ms"; TextBoxMinJitter.Text = $"{stats.MinJitter:F0} ms"; TextBoxAvgJitter.Text = $"{stats.AverageJitter:F0} ms"; // Update packet loss statistics TextBoxMaxLoss.Text = $"{stats.MaxPacketLoss:F0}%"; TextBoxMinLoss.Text = $"{stats.MinPacketLoss:F0}%"; TextBoxAvgLoss.Text = $"{stats.AveragePacketLoss:F0}%"; // Update current values textBoxPacketLoss.Text = $"{stats.PacketLoss:F2}%"; textBoxJitter.Text = $"{stats.Jitter:F2}ms"; } catch (Exception ex) { _logger.LogError("Error updating statistics display", ex); } } private void ResetStatisticsDisplay() { if (dataGridView2.Rows.Count > 0) { dataGridView2.Rows[0].Cells["MaxPing"].Value = "0 ms"; dataGridView2.Rows[0].Cells["MinPing"].Value = "0 ms"; dataGridView2.Rows[0].Cells["AvgPing"].Value = "0 ms"; } TextBoxMaxJitter.Text = "0 ms"; TextBoxMinJitter.Text = "0 ms"; TextBoxAvgJitter.Text = "0 ms"; TextBoxMaxLoss.Text = "0%"; TextBoxMinLoss.Text = "0%"; TextBoxAvgLoss.Text = "0%"; textBoxPacketLoss.Text = "0%"; textBoxJitter.Text = "0ms"; } #endregion #region Context Menu private void InitializeContextMenu() { contextMenuStrip1 = new ContextMenuStrip(); contextMenuStrip1.Items.Add("[1] Show Network Details"); contextMenuStrip1.Items.Add("[2] Auto Ping"); contextMenuStrip1.Items.Add("[3] Run with Windows"); contextMenuStrip1.Items.Add("[4] Proxy"); // Attach event handlers contextMenuStrip1.Items[0].Click += ShowNetworkDetails_Click; contextMenuStrip1.Items[1].Click += AutoPing_Click; contextMenuStrip1.Items[2].Click += RunWithWindows_Click; contextMenuStrip1.Items[3].Click += Proxy_Click; UpdateContextMenuItems(); } private async void ShowNetworkDetails_Click(object sender, EventArgs e) { try { var adapters = await _networkService.GetNetworkAdaptersAsync(); var activeAdapters = adapters.Where(a => a.IsActive).ToList(); if (!activeAdapters.Any()) { UIHelper.ShowInformation("No active network adapters found"); return; } var details = string.Join("\n\n", activeAdapters.Select(adapter => $"Description: {adapter.Description}\n" + $"Physical Address: {adapter.PhysicalAddress}\n" + $"DHCP Enabled: {(adapter.IsDhcpEnabled ? "Yes" : "No")}\n" + $"IPv4 Address: {adapter.IpAddress}\n" + $"IPv4 Subnet Mask: {adapter.SubnetMask}\n" + $"IPv4 Default Gateway: {adapter.DefaultGateway}\n" + $"IPv4 DHCP Server: {adapter.DhcpServer}\n" + $"IPv4 DNS Servers: {string.Join(", ", adapter.DnsServers)}" )); UIHelper.ShowInformation(details, "Network Details"); } catch (Exception ex) { _logger.LogError("Error showing network details", ex); UIHelper.ShowError("Failed to retrieve network details"); } } private async void AutoPing_Click(object sender, EventArgs e) { try { if (_pingService.IsRunning) { await _pingService.StopPingingAsync(); _configService.AutoPingEnabled = false; } else { await StartPingingAsync(); _configService.AutoPingEnabled = true; } _configService.Save(); UpdateContextMenuItems(); } catch (Exception ex) { _logger.LogError("Error toggling auto ping", ex); UIHelper.ShowError("Failed to toggle auto ping"); } } private async void RunWithWindows_Click(object sender, EventArgs e) { try { const string taskName = "Pingerino"; var executablePath = Application.ExecutablePath; var taskExists = await _systemService.IsStartupTaskExistsAsync(taskName); bool success; if (taskExists) { success = await _systemService.RemoveStartupTaskAsync(taskName); } else { success = await _systemService.CreateStartupTaskAsync(taskName, executablePath); } if (success) { UpdateContextMenuItems(); } else { UIHelper.ShowError("Failed to update startup task"); } } catch (Exception ex) { _logger.LogError("Error managing startup task", ex); UIHelper.ShowError("Failed to manage startup task"); } } private async void Proxy_Click(object sender, EventArgs e) { try { var scriptPath = System.IO.Path.Combine( System.IO.Path.GetDirectoryName(Application.ExecutablePath), "runPS1.bat"); var success = await _systemService.RunExternalScriptAsync(scriptPath); if (!success) { UIHelper.ShowError("Proxy script not found or failed to execute"); } } catch (Exception ex) { _logger.LogError("Error running proxy script", ex); UIHelper.ShowError("Failed to run proxy script"); } } private async void UpdateContextMenuItems() { try { // Update auto ping status contextMenuStrip1.Items[1].Text = _pingService.IsRunning ? "[2] Auto Ping ✔" : "[2] Auto Ping"; // Update startup task status var taskExists = await _systemService.IsStartupTaskExistsAsync("Pingerino"); contextMenuStrip1.Items[2].Text = taskExists ? "[3] Run with Windows ✔" : "[3] Run with Windows"; } catch (Exception ex) { _logger.LogError("Error updating context menu items", ex); } } #endregion } }