using System; using System.ComponentModel; using System.Linq; using System.Windows.Forms; using PolyTrader.Modules.CopyTrading.Persistence; using PolyTraderSharp; using PolyTraderSharp.Models; namespace PolyTrader.Modules.CopyTrading.Ui { /// /// Verwaltung der Master-Trader (mod_copytrading_traders): Liste, Detail-Editor /// (PropertyGrid) und Account-Zuweisung (welche Accounts diesen Trader kopieren). /// Persistiert über das Repo und hält den Hot-Path-State /// () synchron. /// Layout im Designer (MasterTradersView.Designer.cs), Daten/Logik hier. /// public partial class MasterTradersView : Form { private ITrackedTraderRepository? _repo; private TradingState? _state; private CopyTradingState? _copyState; private BindingList _binding = new(); private TrackedTrader? _current; // Parameterloser Konstruktor für den WinForms-Designer. public MasterTradersView() { InitializeComponent(); colWinrate.DefaultCellStyle.Format = "F1"; colPnl.DefaultCellStyle.Format = "F2"; tsNew.Click += (_, _) => AddNew(); tsSave.Click += (_, _) => SaveCurrent(); tsDelete.Click += (_, _) => DeleteCurrent(); tsRefresh.Click += (_, _) => LoadData(); grid.SelectionChanged += (_, _) => OnSelectionChanged(); } /// Injiziert die Abhängigkeiten (nach der DI-Auflösung) und lädt die Trader. public void Initialize(ITrackedTraderRepository repo, TradingState state, CopyTradingState copyState) { _repo = repo; _state = state; _copyState = copyState; LoadData(); } private void LoadData() { if (_repo == null) return; var traders = _repo.GetAll().OrderBy(t => t.Id).ToList(); _binding = new BindingList(traders); grid.DataSource = _binding; if (traders.Count > 0) grid.CurrentCell = grid.Rows[0].Cells[0]; else ClearDetail(); lblHint.Text = $"{traders.Count} Master-Trader geladen."; } private void OnSelectionChanged() { if (grid.CurrentRow?.DataBoundItem is TrackedTrader t) BindDetail(t); } private void BindDetail(TrackedTrader trader) { _current = trader; pgDetail.SelectedObject = trader; clbAccounts.Items.Clear(); if (_state == null) return; foreach (var acc in _state.Accounts.Values.OrderBy(a => a.AccountId)) { string label = string.IsNullOrEmpty(acc.Name) ? $"#{acc.AccountId}" : $"{acc.Name} (#{acc.AccountId}){(acc.IsDemo ? " · Demo" : "")}"; int idx = clbAccounts.Items.Add(new AccountItem(acc.AccountId, label)); clbAccounts.SetItemChecked(idx, trader.AssignedAccountIds.Contains(acc.AccountId)); } } private void ClearDetail() { _current = null; pgDetail.SelectedObject = null; clbAccounts.Items.Clear(); } private void AddNew() { int nextId = _binding.Count > 0 ? _binding.Max(t => t.Id) + 1 : 1; var trader = new TrackedTrader { Id = nextId, DisplayName = $"Neuer Trader {nextId}" }; _binding.Add(trader); grid.CurrentCell = grid.Rows[_binding.Count - 1].Cells[0]; lblHint.Text = $"Neuer Master-Trader #{nextId} – Felder ausfüllen und Speichern."; } private void SaveCurrent() { if (_current == null || _repo == null || _copyState == null) { lblHint.Text = "Kein Trader ausgewählt."; return; } _current.AssignedAccountIds = clbAccounts.CheckedItems.Cast().Select(a => a.Id).ToHashSet(); _repo.Upsert(_current); _copyState.Traders[_current.Id] = _current; grid.Refresh(); lblHint.Text = $"Gespeichert: #{_current.Id} {_current.DisplayName} ({_current.AssignedAccountIds.Count} Account(s)) um {DateTime.Now:HH:mm:ss}."; } private void DeleteCurrent() { if (_current == null || _repo == null || _copyState == null) { lblHint.Text = "Kein Trader ausgewählt."; return; } var id = _current.Id; if (MessageBox.Show($"Master-Trader #{id} ({_current.DisplayName}) wirklich löschen?", "Löschen bestätigen", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) != DialogResult.Yes) return; _repo.Delete(id); _copyState.Traders.TryRemove(id, out _); _binding.Remove(_current); ClearDetail(); lblHint.Text = $"Master-Trader #{id} gelöscht."; } private sealed record AccountItem(int Id, string Label) { public override string ToString() => Label; } } }