// lib/features/tournament/presentation/provider/shuffle_provider.dart import 'dart:math'; import 'package:collection/collection.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../data/models/player_model.dart'; import 'active_players_provider.dart'; import 'shuffle_settings_provider.dart'; class ShuffleResult { final List> fields; // 3 Felder à max 12 final List bench; final String message; ShuffleResult(this.fields, this.bench, {this.message = ''}); } // Dies ist jetzt ein STATEFUL Provider → wird nur bei explizitem Aufruf neu berechnet final shuffleResultProvider = StateProvider((ref) => ShuffleResult([], [], message: 'Noch nicht gemischt')); final shuffleControllerProvider = Provider((ref) => ShuffleController(ref)); class ShuffleController { final Ref _ref; ShuffleController(this._ref); /// Hauptfunktion – wird nur vom Button aufgerufen void shuffle() { final activePlayers = _ref.read(activePlayersProvider); final settings = _ref.read(shuffleSettingsProvider); if (activePlayers.isEmpty) { _ref.read(shuffleResultProvider.notifier).state = ShuffleResult([], [], message: 'Keine aktiven Spieler'); return; } final result = ShuffleAlgorithm().run(activePlayers, settings); // Jetzt erst speichern (einmalig!) _commitAllChanges(result); // UI aktualisieren _ref.read(shuffleResultProvider.notifier).state = result; } void _commitAllChanges(ShuffleResult result) { // Aussetzer markieren for (final p in result.bench) { p.hasSatOut = true; p.save(); } // Alle anderen zurücksetzen (falls nötig) final allActive = _ref.read(activePlayersProvider); for (final p in allActive) { if (!result.bench.contains(p)) { p.hasSatOut = false; p.save(); } } } } class ShuffleAlgorithm { final Random _random = Random(); ShuffleResult run(List players, ShuffleSettings settings) { // 1. Aussetzer bestimmen final bench = _determineBench(players); final playing = players.where((p) => !bench.contains(p)).toList(); // 2. Mischen List ordered; switch (settings.mode) { case ShuffleMode.random: ordered = playing..shuffle(_random); break; default: ordered = _balancedShuffle(playing, settings); break; } // 3. Auf 3 Felder verteilen final fields = >[]; for (int i = 0; i < 3; i++) { final start = i * 12; final end = (start + 12).clamp(0, ordered.length); fields.add(start < ordered.length ? ordered.sublist(start, end) : []); } return ShuffleResult( fields, bench, message: 'Gemischte Teams (${ordered.length} Spieler)', ); } List _determineBench(List players) { if (players.length <= 36) return []; final excess = players.length - 36; final never = players.where((p) => !p.hasSatOut).toList(); final already = players.where((p) => p.hasSatOut).toList(); final bench = []; bench.addAll(never.take(excess)); if (bench.length < excess) { bench.addAll(already.take(excess - bench.length)); } return bench; } List _balancedShuffle(List players, ShuffleSettings settings) { players.sort((a, b) { if (settings.prioritizeGenderBalance) { final g = a.gender.index.compareTo(b.gender.index); if (g != 0) return g; } if (settings.prioritizeLevelBalance) { final l = b.skillLevel.index.compareTo(a.skillLevel.index); if (l != 0) return l; } if (settings.usePositions) { final az = a.positions.contains(Position.zuspieler) ? 1 : 0; final bz = b.positions.contains(Position.zuspieler) ? 1 : 0; if (az != bz) return bz.compareTo(az); } return 0; }); final teams = List.generate(6, (_) => []); for (int i = 0; i < players.length; i++) { teams[i % 6].add(players[i]); } for (final t in teams) t.shuffle(_random); final result = []; for (int i = 0; i < 3; i++) { result.addAll(teams[i]); result.addAll(teams[i + 3]); } return result; } }