using System; using System.Collections.ObjectModel; using System.Windows; using System.Windows.Controls; using AutoBidder.Utilities; namespace AutoBidder.Controls { /// /// Scheda Puntate: stato del riscatto automatico, contatori e registro. /// /// Come gli altri pannelli non conosce né la rete né gli archivi: mostra ciò che /// gli viene passato e segnala le richieste dell'utente con eventi. /// public partial class FreeBidsControl : UserControl { /// Una riga del registro dei riscatti. public sealed record ActivityEntry(string Time, string Message, bool IsProblem); /// /// Il registro è volutamente limitato e solo in memoria: serve a capire cosa è /// successo mentre si guardava altrove, non a essere uno storico — quello sono i /// contatori, che invece durano. /// private const int MaxActivityEntries = 200; private readonly ObservableCollection _activity = new(); /// Evita che riempire i campi dalle impostazioni scateni un salvataggio. private bool _loading; public FreeBidsControl() { InitializeComponent(); ActivityList.ItemsSource = _activity; } // ── Aggiornamento dello stato ──────────────────────────────────── public void SetBalance(int? bids) => BalanceText.Text = bids.HasValue ? $"{bids.Value} puntate sul conto" : "saldo non disponibile"; /// Allinea i campi alle impostazioni salvate senza scatenare eventi. public void SetOptions(bool autoClaimEnabled, int checkMinutes) { _loading = true; try { AutoClaimCheckBox.IsChecked = autoClaimEnabled; CheckMinutesTextBox.Text = checkMinutes.ToString(); } finally { _loading = false; } } public bool AutoClaimEnabled => AutoClaimCheckBox.IsChecked == true; /// Codice o collegamento digitato per il riscatto manuale. public string PromoCode => PromoCodeTextBox.Text?.Trim() ?? ""; /// Svuota il campo dopo un riscatto andato a buon fine. public void ClearPromoCode() => PromoCodeTextBox.Text = ""; /// /// Riga di stato della raccolta: da dove legge, quanti codici sono già stati presi e /// com'è andato l'ultimo giro. /// public void SetHarvestStatus(string sourceUrl, int claimedCodes, string? lastOutcome) { var known = claimedCodes == 1 ? "1 codice già preso" : $"{claimedCodes} codici già presi"; HarvestStatusText.Text = lastOutcome == null ? $"Sorgente: {sourceUrl} · {known}." : $"Sorgente: {sourceUrl} · {known} · ultimo giro: {lastOutcome}"; } /// /// Mostra dove sta il file dei riferimenti, ed eventualmente perché non è stato /// possibile usarlo: sono i valori che decidono ogni chiamata di riscatto, e sapere /// che se ne stanno usando altri è la differenza fra correggere il file e cercare un /// guasto che non c'è. /// public void SetConfigPath(string path, string? problem) { ConfigPathText.Text = problem == null ? $"Indirizzi, intestazioni e parametri del riscatto: {path}" : $"{problem} — {path}"; ConfigPathText.SetResourceReference(TextBlock.ForegroundProperty, problem == null ? "Brush.TextMuted" : "Brush.Warning"); } /// Minuti richiesti, già riportati entro il minimo consentito. public int CheckMinutes => int.TryParse(CheckMinutesTextBox.Text?.Trim(), out var minutes) ? Math.Max(5, minutes) : 30; /// Riga di stato: acceso o spento, e quando tocca al prossimo giro. public void SetSchedule(bool running, DateTime? lastCheck, DateTime? nextCheck) { StatusPillText.Text = running ? "riscatto attivo" : "riscatto spento"; StatusPillText.SetResourceReference(TextBlock.ForegroundProperty, running ? "Brush.Success" : "Brush.TextMuted"); if (!running) { ScheduleText.Text = "Riscatto automatico spento: puoi comunque usare «Controlla adesso»."; return; } var last = lastCheck.HasValue ? $"ultimo controllo alle {lastCheck.Value:HH:mm}" : "nessun controllo ancora eseguito"; var next = nextCheck.HasValue ? $", il prossimo alle {nextCheck.Value:HH:mm}" : ""; ScheduleText.Text = char.ToUpper(last[0]) + last[1..] + next + "."; } /// Ridisegna i contatori. public void SetCounters(FreeBidsStats.Snapshot s) { SessionBidsText.Text = s.SessionBids.ToString(); TodayBidsText.Text = s.BidsToday.ToString(); TotalBidsText.Text = s.TotalBids.ToString(); TotalClaimedText.Text = s.TotalClaimed.ToString(); var lastClaim = s.LastClaimAt.HasValue ? $"ultimo riscatto il {s.LastClaimAt.Value:dd/MM/yyyy} alle {s.LastClaimAt.Value:HH:mm}" : "nessun riscatto ancora andato a buon fine"; CountersDetailText.Text = $"{s.SessionChecks} controlli in questa sessione · {s.TotalChecks} da sempre · " + $"{s.SessionClaimed} ricompense prese in sessione · {lastClaim}."; } /// /// Aggiunge una riga al registro, a video e su file. /// /// Il file serve perché questo pannello è l'unico posto in cui si vede cosa ha /// fatto il riscatto automatico, e il riscatto lavora quando nessuno guarda: senza /// registro su disco, tutto ciò che è successo mentre l'applicazione era chiusa — /// o nelle duecento righe precedenti — sarebbe perso. /// public void AppendActivity(string message, bool isProblem = false) { TextLogService.FreeBids(message, isProblem); _activity.Insert(0, new ActivityEntry(DateTime.Now.ToString("HH:mm:ss"), message, isProblem)); while (_activity.Count > MaxActivityEntries) _activity.RemoveAt(_activity.Count - 1); ActivityEmptyText.Visibility = Visibility.Collapsed; } // ── Eventi dell'interfaccia ────────────────────────────────────── private void RefreshButton_Click(object sender, RoutedEventArgs e) => RaiseEvent(new RoutedEventArgs(RefreshBalanceClickedEvent, this)); private void CheckNowButton_Click(object sender, RoutedEventArgs e) => RaiseEvent(new RoutedEventArgs(CheckNowClickedEvent, this)); private void OpenExternalButton_Click(object sender, RoutedEventArgs e) => RaiseEvent(new RoutedEventArgs(OpenExternalClickedEvent, this)); private void OpenInternalButton_Click(object sender, RoutedEventArgs e) => RaiseEvent(new RoutedEventArgs(OpenInternalClickedEvent, this)); private void OpenVouchersButton_Click(object sender, RoutedEventArgs e) => RaiseEvent(new RoutedEventArgs(OpenVouchersClickedEvent, this)); private void OpenBuyBidsButton_Click(object sender, RoutedEventArgs e) => RaiseEvent(new RoutedEventArgs(OpenBuyBidsClickedEvent, this)); private void ResetCountersButton_Click(object sender, RoutedEventArgs e) => RaiseEvent(new RoutedEventArgs(ResetCountersClickedEvent, this)); private void AutoClaimCheckBox_Changed(object sender, RoutedEventArgs e) { if (_loading) return; RaiseEvent(new RoutedEventArgs(OptionsChangedEvent, this)); } private void CheckMinutesTextBox_LostFocus(object sender, RoutedEventArgs e) { if (_loading) return; // Si riscrive il valore corretto: chi ha digitato "2" deve vedere che il // minimo applicato è 5, invece di credere di aver impostato due minuti. var minutes = CheckMinutes; if (CheckMinutesTextBox.Text != minutes.ToString()) CheckMinutesTextBox.Text = minutes.ToString(); RaiseEvent(new RoutedEventArgs(OptionsChangedEvent, this)); } private void HarvestButton_Click(object sender, RoutedEventArgs e) => RaiseEvent(new RoutedEventArgs(HarvestClickedEvent, this)); private void OpenSourceButton_Click(object sender, RoutedEventArgs e) => RaiseEvent(new RoutedEventArgs(OpenPromoSourceClickedEvent, this)); private void ForgetPromosButton_Click(object sender, RoutedEventArgs e) => RaiseEvent(new RoutedEventArgs(ForgetPromosClickedEvent, this)); private void ClaimPromoButton_Click(object sender, RoutedEventArgs e) => RaiseEvent(new RoutedEventArgs(ClaimPromoClickedEvent, this)); /// Invio nel campo vale come clic: si incolla e si preme invio. private void PromoCodeTextBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e) { if (e.Key != System.Windows.Input.Key.Enter) return; e.Handled = true; RaiseEvent(new RoutedEventArgs(ClaimPromoClickedEvent, this)); } private void OpenConfigButton_Click(object sender, RoutedEventArgs e) => RaiseEvent(new RoutedEventArgs(OpenConfigClickedEvent, this)); private void ClearLogButton_Click(object sender, RoutedEventArgs e) { _activity.Clear(); ActivityEmptyText.Visibility = Visibility.Visible; } // ── Routed events ──────────────────────────────────────────────── public static readonly RoutedEvent RefreshBalanceClickedEvent = EventManager.RegisterRoutedEvent( "RefreshBalanceClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl)); public static readonly RoutedEvent CheckNowClickedEvent = EventManager.RegisterRoutedEvent( "CheckNowClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl)); public static readonly RoutedEvent OpenExternalClickedEvent = EventManager.RegisterRoutedEvent( "OpenExternalClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl)); public static readonly RoutedEvent OpenInternalClickedEvent = EventManager.RegisterRoutedEvent( "OpenInternalClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl)); public static readonly RoutedEvent OpenVouchersClickedEvent = EventManager.RegisterRoutedEvent( "OpenVouchersClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl)); public static readonly RoutedEvent OpenBuyBidsClickedEvent = EventManager.RegisterRoutedEvent( "OpenBuyBidsClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl)); public static readonly RoutedEvent ResetCountersClickedEvent = EventManager.RegisterRoutedEvent( "ResetCountersClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl)); public static readonly RoutedEvent OptionsChangedEvent = EventManager.RegisterRoutedEvent( "OptionsChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl)); public static readonly RoutedEvent ClaimPromoClickedEvent = EventManager.RegisterRoutedEvent( "ClaimPromoClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl)); public static readonly RoutedEvent HarvestClickedEvent = EventManager.RegisterRoutedEvent( "HarvestClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl)); public static readonly RoutedEvent OpenPromoSourceClickedEvent = EventManager.RegisterRoutedEvent( "OpenPromoSourceClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl)); public static readonly RoutedEvent ForgetPromosClickedEvent = EventManager.RegisterRoutedEvent( "ForgetPromosClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl)); public static readonly RoutedEvent OpenConfigClickedEvent = EventManager.RegisterRoutedEvent( "OpenConfigClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl)); public event RoutedEventHandler RefreshBalanceClicked { add { AddHandler(RefreshBalanceClickedEvent, value); } remove { RemoveHandler(RefreshBalanceClickedEvent, value); } } public event RoutedEventHandler CheckNowClicked { add { AddHandler(CheckNowClickedEvent, value); } remove { RemoveHandler(CheckNowClickedEvent, value); } } public event RoutedEventHandler OpenExternalClicked { add { AddHandler(OpenExternalClickedEvent, value); } remove { RemoveHandler(OpenExternalClickedEvent, value); } } public event RoutedEventHandler OpenInternalClicked { add { AddHandler(OpenInternalClickedEvent, value); } remove { RemoveHandler(OpenInternalClickedEvent, value); } } public event RoutedEventHandler OpenVouchersClicked { add { AddHandler(OpenVouchersClickedEvent, value); } remove { RemoveHandler(OpenVouchersClickedEvent, value); } } public event RoutedEventHandler OpenBuyBidsClicked { add { AddHandler(OpenBuyBidsClickedEvent, value); } remove { RemoveHandler(OpenBuyBidsClickedEvent, value); } } public event RoutedEventHandler ResetCountersClicked { add { AddHandler(ResetCountersClickedEvent, value); } remove { RemoveHandler(ResetCountersClickedEvent, value); } } public event RoutedEventHandler OptionsChanged { add { AddHandler(OptionsChangedEvent, value); } remove { RemoveHandler(OptionsChangedEvent, value); } } public event RoutedEventHandler ClaimPromoClicked { add { AddHandler(ClaimPromoClickedEvent, value); } remove { RemoveHandler(ClaimPromoClickedEvent, value); } } public event RoutedEventHandler HarvestClicked { add { AddHandler(HarvestClickedEvent, value); } remove { RemoveHandler(HarvestClickedEvent, value); } } public event RoutedEventHandler OpenPromoSourceClicked { add { AddHandler(OpenPromoSourceClickedEvent, value); } remove { RemoveHandler(OpenPromoSourceClickedEvent, value); } } public event RoutedEventHandler ForgetPromosClicked { add { AddHandler(ForgetPromosClickedEvent, value); } remove { RemoveHandler(ForgetPromosClickedEvent, value); } } public event RoutedEventHandler OpenConfigClicked { add { AddHandler(OpenConfigClickedEvent, value); } remove { RemoveHandler(OpenConfigClickedEvent, value); } } } }