import '../models/public_participant.dart'; import '../models/public_team.dart'; class PublicRankingUtils { static int capScore(int scoreWithHandicap) { if (scoreWithHandicap > 300) return 300; if (scoreWithHandicap < 0) return 0; return scoreWithHandicap; } // 평균(핸디 포함), 소수 1자리. 입력된 게임 수 기준으로 계산 static double participantAverageWithHc(PublicParticipant p) { int total = 0; int cnt = 0; for (int i = 0; i < p.rawScores.length; i++) { final raw = p.rawScores[i]; int hc = (i < p.handicaps.length) ? p.handicaps[i] : 0; if (hc == 0) { hc = p.handicap; } if (raw == null) continue; total += capScore(raw + hc); cnt += 1; } if (cnt == 0) return 0.0; return total / cnt; } // 개인별 총점(핸디캡 포함) 계산 static int participantTotalWithHc(PublicParticipant p) { int total = 0; for (int i = 0; i < p.rawScores.length; i++) { final raw = p.rawScores[i]; int hc = (i < p.handicaps.length) ? p.handicaps[i] : 0; if (hc == 0) { // per-game 핸디캡이 없으면 참가자 기본 핸디캡 사용 hc = p.handicap; } if (raw == null) continue; total += capScore(raw + hc); } return total; } // 동점자 순위 처리(1,1,3 방식) static List ranksFor(List valuesDesc) { if (valuesDesc.isEmpty) return const []; final ranks = List.filled(valuesDesc.length, 0); int rank = 1; for (int i = 0; i < valuesDesc.length; i++) { if (i > 0 && valuesDesc[i] == valuesDesc[i - 1]) { ranks[i] = ranks[i - 1]; } else { ranks[i] = rank; } rank = i + 2; // 다음 베이스 랭크 } return ranks; } static List> individualRanking(List list) { // 내림차순 정렬 final items = list .map((p) => { 'p': p, 'total': participantTotalWithHc(p), }) .toList(); items.sort((a, b) => (b['total'] as int).compareTo(a['total'] as int)); final totals = items.map((e) => e['total'] as int).toList(); final rs = ranksFor(totals); for (int i = 0; i < items.length; i++) { items[i]['rank'] = rs[i]; } return items; } // 동점자 순위 처리 for double 리스트 (1,1,3 방식) static List ranksForDoubles(List valuesDesc) { if (valuesDesc.isEmpty) return const []; final ranks = List.filled(valuesDesc.length, 0); int rank = 1; const eps = 1e-9; for (int i = 0; i < valuesDesc.length; i++) { if (i > 0 && (valuesDesc[i] - valuesDesc[i - 1]).abs() < eps) { ranks[i] = ranks[i - 1]; } else { ranks[i] = rank; } rank = i + 2; } return ranks; } // 평균(핸디 포함) 기준 개인 랭킹 (내림차순). item: {p, total, avg, rank} static List> individualRankingByAvg(List list) { final items = list .map((p) => { 'p': p, 'total': participantTotalWithHc(p), 'avg': participantAverageWithHc(p), }) .toList(); items.sort((a, b) => (b['avg'] as double).compareTo(a['avg'] as double)); final avgs = items.map((e) => e['avg'] as double).toList(); final rs = ranksForDoubles(avgs); for (int i = 0; i < items.length; i++) { items[i]['rank'] = rs[i]; } return items; } static List> teamRanking(List teams) { // 팀 총점 = 구성원 총점 합 + 팀 핸디캡 합(여기서는 팀 handicap만 단일 값으로 가정) final items = teams .map((t) { int membersTotal = 0; for (final m in t.members) { int mt = 0; for (int i = 0; i < m.rawScores.length; i++) { final raw = m.rawScores[i]; final hc = (i < m.handicaps.length) ? m.handicaps[i] : 0; if (raw == null) continue; mt += capScore(raw + hc); } membersTotal += mt; } final total = membersTotal + t.handicap; return {'team': t, 'total': total}; }) .toList(); items.sort((a, b) => (b['total'] as int).compareTo(a['total'] as int)); final totals = items.map((e) => e['total'] as int).toList(); final rs = ranksFor(totals); for (int i = 0; i < items.length; i++) { items[i]['rank'] = rs[i]; } return items; } // 참가자별 총점과 랭크 맵 생성 (participantId -> {total, rank}) static Map> buildParticipantRankMap(List list) { final ranked = individualRanking(list); final map = >{}; for (final item in ranked) { final p = item['p'] as PublicParticipant; map[p.participantId] = { 'total': item['total'] as int, 'rank': item['rank'] as int, }; } return map; } // 팀별 총점 맵 (teamNumber -> {total, rank}) static Map> buildTeamRankMap(List teams) { final ranked = teamRanking(teams); final map = >{}; for (final item in ranked) { final t = item['team'] as PublicTeam; map[t.teamNumber] = { 'total': item['total'] as int, 'rank': item['rank'] as int, }; } return map; } // 미배정 참가자 목록 계산 static List unassignedParticipants(List participants, List teams) { final assigned = {}; for (final t in teams) { for (final m in t.members) { final pid = m.participantId; if (pid != null && pid.isNotEmpty) assigned.add(pid); } } return participants.where((p) => !assigned.contains(p.participantId)).toList(); } }