You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
PadelClub/PadelClub/Utils/PadelRule.swift

2062 lines
58 KiB

//
// PadelRule.swift
// Padel Tournament
//
// Created by razmig on 27/02/2023.
//
import Foundation
import LeStorage
enum RankSource: Hashable {
case national
case ligue
case club(assimilation: Bool)
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .national:
return "Classement National"
case .ligue:
return "Classement Ligue"
case .club:
return "Classement Club"
}
}
}
protocol TournamentBuildHolder: Identifiable {
var id: String { get }
var category: TournamentCategory { get }
var level: TournamentLevel { get }
var age: FederalTournamentAge { get }
func buildHolderTitle(_ displayStyle: DisplayStyle) -> String
}
struct TournamentBuild: TournamentBuildHolder, Hashable, Codable, Identifiable {
var uniqueId: String = Store.randomId()
var id: String { uniqueId }
let category: TournamentCategory
let level: TournamentLevel
let age: FederalTournamentAge
// var japIdentifier: Int? = nil
// var japFirstName: String? = nil
// var japLastName: String? = nil
func buildHolderTitle(_ displayStyle: DisplayStyle) -> String {
computedLabel(displayStyle)
}
var identifier: String {
level.localizedLevelLabel()+":"+category.localizedLabel()+":"+age.localizedFederalAgeLabel()
}
func computedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
if age == .senior { return localizedLabel(displayStyle) }
return localizedLabel(displayStyle) + " " + localizedAge(displayStyle)
}
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
level.localizedLevelLabel(displayStyle) + " " + category.localizedLabel(displayStyle)
}
func localizedTitle(_ displayStyle: DisplayStyle = .wide) -> String {
level.localizedLevelLabel(displayStyle) + " " + category.localizedLabel(displayStyle)
}
func localizedAge(_ displayStyle: DisplayStyle = .wide) -> String {
age.localizedFederalAgeLabel(displayStyle)
}
}
extension TournamentBuild {
init?(category: String, level: String, age: FederalTournamentAge = .senior) {
guard let levelFound = TournamentLevel.allCases.first(where: { $0.localizedLevelLabel() == level }) else { return nil }
var c = category
if c.hasPrefix("ME") {
c = "H"
}
if c.hasPrefix("F") {
c = "D"
}
guard let categoryFound = TournamentCategory.allCases.first(where: { c.canonicalVersion.hasPrefix($0.buildLabel.canonicalVersion) }) else { return nil }
self.level = levelFound
self.category = categoryFound
self.age = age
}
}
enum FederalTournamentType: String, Hashable, Codable, CaseIterable, Identifiable {
case tournoi = "P"
case championnatParEquipe = "S"
case championnatParPaire = "L"
var id: String { self.rawValue }
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .tournoi:
return "Tournois"
case .championnatParEquipe:
return "Championnats par équipes"
case .championnatParPaire:
return "Championnats par paires"
}
}
}
enum TournamentDifficulty {
case rankS
case rankA
case rankB
case rankC
init?(gameDifference: Double) {
switch gameDifference {
case ..<3:
self = .rankS
case ..<5:
self = .rankA
case ..<7:
self = .rankB
case 7...:
self = .rankC
default:
return nil
}
}
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .rankS:
return "S"
case .rankA:
return "A"
case .rankB:
return "B"
case .rankC:
return "C"
}
}
var backgroundColor: String {
switch self {
case .rankS:
return "#d4af37"
case .rankA:
return "#c0c0c0"
case .rankB:
return "#cd7f32"
case .rankC:
return "#DCC2E0"
}
}
}
enum FederalTournamentAge: Int, Hashable, Codable, CaseIterable, Identifiable {
case unlisted = 0
case a11_12 = 120
case a13_14 = 140
case a15_16 = 160
case a17_18 = 180
case senior = 200
case a45 = 450
case a55 = 550
init?(rawValue: Int?) {
guard let value = rawValue else { return nil }
self.init(rawValue: value)
}
func computedBirthYear() -> (Int?, Int?) {
let year = Calendar.current.getSportAge()
switch self {
case .unlisted:
return (nil, nil)
case .a11_12:
return (year - 12, year - 11)
case .a13_14:
return (year - 14, year - 13)
case .a15_16:
return (year - 16, year - 15)
case .a17_18:
return (year - 18, year - 17)
case .senior:
return (nil, year - 19)
case .a45:
return (nil, year - 45)
case .a55:
return (nil, year - 55)
}
}
var importingRawValue: String {
switch self {
case .unlisted:
return "Animation"
case .a11_12:
return "11/12 ans"
case .a13_14:
return "13/14 ans"
case .a15_16:
return "15/16 ans"
case .a17_18:
return "17/18 ans"
case .senior:
return "Senior"
case .a45:
return "45 ans"
case .a55:
return "55 ans"
}
}
static func mostRecent(inTournaments tournaments: [Tournament]) -> Self {
return tournaments.first?.federalTournamentAge ?? .senior
}
static func mostUsed(inTournaments tournaments: [Tournament]) -> Self {
let countedSet = NSCountedSet(array: tournaments.map { $0.federalTournamentAge })
let mostFrequent = countedSet.max { countedSet.count(for: $0) < countedSet.count(for: $1) }
if mostFrequent != nil {
return mostFrequent as! FederalTournamentAge
} else {
return mostRecent(inTournaments: tournaments)
}
}
var id: Int { self.rawValue }
var order: Int {
switch self {
case .unlisted:
return 7
case .a11_12:
return 6
case .a13_14:
return 5
case .a15_16:
return 4
case .a17_18:
return 3
case .senior:
return 0
case .a45:
return 1
case .a55:
return 2
}
}
func localizedFederalAgeLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .unlisted:
return displayStyle == .title ? "Aucune" : ""
case .a11_12:
return "11/12 ans"
case .a13_14:
return "13/14 ans"
case .a15_16:
return "15/16 ans"
case .a17_18:
return "17/18 ans"
case .senior:
return displayStyle == .short ? "" : "Senior"
case .a45:
return "+45 ans"
case .a55:
return "+55 ans"
}
}
var tournamentDescriptionLabel: String {
return localizedFederalAgeLabel()
}
func isAgeValid(age: Int?) -> Bool {
guard let age else { return true }
switch self {
case .unlisted:
return true
case .a11_12:
return age < 13
case .a13_14:
return age < 15
case .a15_16:
return age < 17
case .a17_18:
return age < 19
case .senior:
return age >= 11
case .a45:
return age >= 45
case .a55:
return age >= 55
}
}
}
enum TournamentLevel: Int, Hashable, Codable, CaseIterable, Identifiable {
case unlisted = 0
case p25 = 25
case p100 = 100
case p250 = 250
case p500 = 500
case p1000 = 1000
case p1500 = 1500
case p2000 = 2000
case championship = 1
init?(rawValue: Int?) {
guard let value = rawValue else { return nil }
self.init(rawValue: value)
}
static var assimilationAllCases: [TournamentLevel] = {
return [.p25, .p100, .p250, .p500, .p1000, .p1500, .p2000]
}()
var entryFee: Double? {
switch self {
case .unlisted, .championship:
return nil
case .p25:
return 15
default:
return 20
}
}
func isAnimation() -> Bool {
switch self {
case .unlisted:
return true
case .championship:
return false
default:
return false
}
}
func searchRawValue() -> String {
String(describing: self)
}
func pointsRange(first: Int, last: Int, teamsCount: Int) -> String {
let range = [points(for: first - 1, count: teamsCount),
points(for: last - 1, count: teamsCount)]
return range.map { $0.formatted(.number.sign(strategy: .always())) }.joined(separator: " / ") + " pts"
}
func hideWeight() -> Bool {
switch self {
case .unlisted:
return true
default:
return false
}
}
func shouldShareTeams() -> Bool {
switch self {
case .p500, .p1000, .p1500, .p2000:
return true
default:
return false
}
}
static func mostRecent(inTournaments tournaments: [Tournament]) -> Self {
return tournaments.first?.tournamentLevel ?? .p100
}
static func mostUsed(inTournaments tournaments: [Tournament]) -> Self {
let countedSet = NSCountedSet(array: tournaments.map { $0.tournamentLevel })
let mostFrequent = countedSet.max { countedSet.count(for: $0) < countedSet.count(for: $1) }
if mostFrequent != nil {
return mostFrequent as! TournamentLevel
} else {
return mostRecent(inTournaments: tournaments)
}
}
var id: Int { self.rawValue }
func wildcardArePossible() -> Bool {
switch self {
case .p500, .p1000, .p1500, .p2000:
return true
default:
return false
}
}
func minimumPlayerRank(category: TournamentCategory, ageCategory: FederalTournamentAge) -> Int {
switch self {
case .p25:
switch ageCategory {
case .senior, .a45, .a55:
return category == .men ? 20000 : 1000
default:
return 0
}
case .p100:
switch ageCategory {
case .senior, .a45, .a55:
return category == .men ? 2000 : 300
default:
return 0
}
case .p250:
switch ageCategory {
case .senior, .a45, .a55:
if category == .mix { return 0 }
return category == .men ? 500 : 100
default:
return 0
}
default:
return 0
}
}
func federalFormatForGroupStage() -> MatchFormat {
federalFormatForBracketRound(5)
}
func federalFormatForBracketRound(_ roundIndex: Int) -> MatchFormat {
switch self {
case .p25:
return .superTie
case .p100:
return .nineGamesDecisivePoint
case .p250:
if roundIndex == 0 { //finale
return .twoSetsDecisivePointSuperTie
} else {
return .nineGamesDecisivePoint
}
case .p500:
if roundIndex == 0 { //finale
return .twoSetsDecisivePointSuperTie
} else if roundIndex == 1 { //demi-finale
return .twoSetsDecisivePointSuperTie
} else {
return .nineGamesDecisivePoint
}
case .p1000:
if roundIndex <= 3 { //demi / finale / quart / 8eme
return .twoSetsDecisivePoint
} else {
return .twoSetsDecisivePointSuperTie
}
case .p1500, .p2000:
if roundIndex <= 3 { //demi / finale / quart / 8eme
return .twoSetsDecisivePoint
} else {
return .twoSetsSuperTie
}
default:
return .superTie
}
}
func decisivePointRequired(ageCategory: FederalTournamentAge) -> Bool {
switch ageCategory {
case .a11_12, .a13_14, .a15_16, .a17_18:
return true
default:
return false
}
}
func federalFormatForLoserBracketRound(_ roundIndex: Int) -> MatchFormat {
switch self {
case .p25:
return .superTie
case .p100, .p250, .p500:
return .nineGamesDecisivePoint
case .p1000:
return .nineGamesDecisivePoint
case .p1500, .p2000:
return .twoSetsSuperTie
default:
return .nineGamesDecisivePoint
}
}
var defaultTeamSortingType: TeamSortingType {
switch self {
case .p25, .p100, .p250:
return .inscriptionDate
default:
return .rank
}
}
var order: Int {
switch self {
case .unlisted:
return 7
case .p25:
return 6
case .p100:
return 5
case .p250:
return 4
case .p500:
return 3
case .p1000:
return 2
case .p1500:
return 1
case .p2000:
return 0
case .championship:
return 8
}
}
func localizedLevelLabel(_ displayStyle: DisplayStyle = .wide) -> String {
if self == .unlisted {
if DeviceHelper.isBigScreen() {
return "Animation"
} else {
return displayStyle == .title ? "Animation" : "Anim."
}
}
if self == .championship {
if DeviceHelper.isBigScreen() {
return "Championnat"
} else {
return displayStyle == .title ? "Championnat" : "CHPT"
}
}
return String(describing: self).capitalized
}
var coachingIsAuthorized: Bool {
switch self {
case .p500, .p1000, .p1500, .p2000, .championship:
return true
default:
return false
}
}
func points(for rank: Int, count: Int) -> Int {
if self == .unlisted { return 0 }
if self == .championship { return 0 }
let points = points(for: count)
if rank >= points.count {
return points.last!
} else if rank == 0 {
return Int(self.rawValue)
} else {
return points[rank-1]
}
}
func allPoints(for count: Int) -> [Int] {
[Int(self.rawValue)] + points(for: count)
}
func points(for count: Int) -> [Int] {
switch self {
case .unlisted, .championship:
return []
case .p25:
switch count {
case 9...12:
return [17, 15, 13, 11, 9, 7, 5, 4, 3, 2, 1]
case 13...16:
return [18,16,15,14,13,12,11,10,9,7,5,4,3,2, 1]
case 17...20:
return [20,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, 1]
case 21...24:
return [20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, 1]
case 25...28:
return [21,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, 1]
case _ where count > 28:
return [23,21,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, 1]
default:
return [15, 12, 9, 6, 4, 2, 1]
}
case .p100:
switch count {
case 9...12:
return [65,55, 50, 35, 25, 20, 15, 10, 5, 3, 1]
case 13...16:
return [70, 60, 55, 45, 40, 35, 30, 25, 21, 18, 15, 10, 5, 3, 1]
case 17...20:
return [75,65,60,55,50,45,40,35,30,25,23,20,18,15,12,10,5,3, 1]
case 21...24:
return [75,70,65,60,55,50,47,43,40,37,33,30,28,25,23,20,18,15,12,10,5,3, 1]
case 25...28:
return [80,75,70,65,60,55,53,50,48,45,43,40,38,35,33,30,28,25,23,20,18,15,12,10,5,3, 1]
case _ where count > 28:
return [80,75,72,70,65,63,60,58,55,53,50,48,45,43,40,38,35,33,30,28,25,23,20,18,15,12,10,8,5,3, 1]
default:
return [60,50,40,25,10,5]
}
case .p250:
switch count {
case 9...12:
return [163,138,125,88,63,50,38,25,13,8,3]
case 13...16:
return [175,150,138,113,100,88,75,63,53,45,38,25,13,8,3]
case 17...20:
return [188,163,150,138,123,113,100,88,75,63,58,50,45,38,30,25,13,8,3]
case 21...24:
return [188,175,163,150,138,125,118,108,100,93,83,75,70,63,58,50,45,38,30,25,13,8,3]
case 25...28:
return [200,188,175,163,150,138,133,125,120,113,108,100,95,88,83,75,70,63,58,50,45,38,30,25,13,8,3]
case _ where count > 28:
return [200,188,180,175,163,158,150,145,138,133,125,120,113,108,100,95,88,83,75,70,63,58,50,45,38,30,25,20,13,8,3]
default:
return [150,125,100,63,25,13,3]
}
case .p500:
switch count {
case 9...12:
return [325,275,250,175,125,100,75,50,25,15,5]
case 13...16:
return [350,300,275,225,200,175,150,125,105,90,75,50,25,15,5]
case 17...20:
return [375,325,300,275,250,225,200,175,150,125,115,100,90,75,60,50,25,15,5]
case 21...24:
return [375,350,325,300,275,250,235,215,200,185,165,150,140,125,115,100,80,75,60,50,25,15,5]
case 25...28:
return [400,375,350,325,300,275,265,250,240,225,215,200,190,175,165,150,140,125,115,100,80,75,60,50,25,15,5]
case _ where count > 28:
return [400,375,360,350,325,315,300,290,275,265,250,240,225,215,200,190,175,165,150,140,125,115,100,80,75,60,50,40,25,15,5]
default:
return [300,250,200,125,50,25,5]
}
case .p1000:
switch count {
case 9...12:
return [650,550, 500, 350, 250, 200, 150, 100, 50, 30, 10]
case 13...16:
return [700, 600, 550, 450, 400, 350, 300, 250, 210, 180, 150, 100, 50, 30, 10]
case 17...20:
return [750,650,600,550,500,450,400,350,300,250,230,200,180,150,120,100,50,30, 10]
case 21...24:
return [750,700,650,600,550,500,470,430,400,370,330,300,280,250,230,200,180,150,120,100,50,30, 10]
case 25...28:
return [800,750,700,650,600,550,530,500,480,450,430,400,380,350,330,300,280,250,230,200,180,150,120,100,50,30, 10]
case _ where count > 28:
return [800,750,720,700,650,630,600,580,550,530,500,480,450,430,400,380,350,330,300,280,250,230,200,180,150,120,100,80,50,30, 10]
default:
return [600,500,400,250,100,50]
}
case .p1500:
switch count {
case 21...24:
return [1125,1050,975,900,825,750,705,645,600,555,495,450,420,375,345,300,270,225,180,150,75,45,15]
case 25...28:
return [1200,1125,1050,975,900,825,795,750,720,675,645,600,570,525,495,450,420,375,345,300,270,225,180,150,75,45,15]
case _ where count > 28:
return [1200,1125,1080,1050,975,945,900,870,825,795,750,720,675,645,600,570,525,495,450,420,375,345,300,270,225,180,150,120,75,45,15]
default:
return [1125,975,900,825,750,675,600,525,450,375,345,300,270,225,180,150,75,45,15]
}
case .p2000:
return [1600,1440,1300,1180,1120,1060,1000,880,840,800,760,720,680,640,600,540,520,500,480,460,440,420,400,260,200,40]
}
}
var ranges: [PlayersCountRange] {
switch self {
case .p1500:
return [.N16, .N24, .N28, .N32]
case .p2000:
return [.N32]
default:
return PlayersCountRange.allCases
}
}
// enum NewBallSystem {
// case perField
// case perMatch(fromRound: Int?)
//
// func localizedLabel(loserBracket: Bool = false) -> String {
// switch self {
// case .perField:
// return "3 / piste"
// case .perMatch(let fromRound):
// if fromRound != nil {
// if loserBracket {
// return "3 / match pour les perdants des \(RoundLabel.shortLabels[fromRound!].lowercased())s"
// } else {
// return "3 / match à partir des \(RoundLabel.shortLabels[fromRound!].lowercased())s"
// }
// } else {
// return "3 / match"
// }
// }
// }
// }
//
func minimumFormatFinalTableAndQualifier(roundIndex: Int) -> MatchFormat? {
switch self {
case .p25, .unlisted, .championship:
return nil
case .p100:
return .nineGamesDecisivePoint
case .p250:
if roundIndex == 0 { //final
return .twoSetsDecisivePointSuperTie
} else {
return .nineGamesDecisivePoint
}
case .p500:
if roundIndex == 0 { //final
return .twoSetsDecisivePoint
} else if roundIndex == 1 { //demi
return .twoSetsDecisivePointSuperTie
} else {
return .nineGamesDecisivePoint
}
case .p1000, .p1500, .p2000:
if roundIndex == 3 { //16eme
return .twoSetsDecisivePoint
} else {
return .twoSetsDecisivePointSuperTie
}
}
}
func minimumFormatLoserBracket(roundIndex: Int) -> MatchFormat? {
switch self {
case .p25, .unlisted, .championship:
return nil
case .p100, .p250, .p500:
return .nineGamesDecisivePoint
case .p1000, .p1500, .p2000:
if roundIndex == 1 { //demi
return .twoSetsDecisivePoint
} else {
return .nineGamesDecisivePoint
}
}
}
// func newBallsFinalTable() -> NewBallSystem? {
// switch self {
// case .p25, .p100:
// return .perField
// case .p250:
// return .perMatch(fromRound: 1) //demi
// case .p500:
// return .perMatch(fromRound: 2) //quart
// case .p1000, .p1500, .p2000:
// return .perMatch(fromRound: nil)
// }
// }
//
// func newBallsLoserBracket() -> NewBallSystem? {
// switch self {
// case .p25, .p100:
// return nil
// case .p250:
// return .perMatch(fromRound: 1) //demi
// case .p500, .p1000, .p1500, .p2000:
// return .perMatch(fromRound: 2) //quart
// }
// }
//
}
enum TournamentCategory: Int, Hashable, Codable, CaseIterable, Identifiable {
case men
case women
case mix
case unlisted
init?(rawValue: Int?) {
guard let value = rawValue else { return nil }
self.init(rawValue: value)
}
func mandatoryPlayerType() -> [Int] {
switch self {
case .unlisted:
return []
case .mix:
return [0, 1]
case .women:
return [0, 0]
case .men:
return [1, 1]
}
}
var localizedPlayerLabel: String {
switch self {
case .women:
return "joueuse"
default:
return "joueur"
}
}
var showFemaleInMaleAssimilation: Bool {
switch self {
case .men:
return true
default:
return false
}
}
static func femaleInMaleAssimilationAddition(_ rank: Int) -> Int {
switch rank {
case 1...10: return 400
case 11...30: return 1000
case 31...60: return 2000
case 61...100: return 3500
case 101...200: return 10000
case 201...500: return 15000
case 501...1000: return 25000
case 1001...2000: return 35000
case 2001...3000: return 45000
default:
return 50000
}
}
static func mostRecent(inTournaments tournaments: [Tournament]) -> Self {
return tournaments.first?.tournamentCategory ?? .men
}
static func mostUsed(inTournaments tournaments: [Tournament]) -> Self {
let countedSet = NSCountedSet(array: tournaments.map { $0.tournamentCategory })
let mostFrequent = countedSet.max { countedSet.count(for: $0) < countedSet.count(for: $1) }
if mostFrequent != nil {
return mostFrequent as! TournamentCategory
} else {
return mostRecent(inTournaments: tournaments)
}
}
var id: Int { self.rawValue }
var order: Int {
switch self {
case .unlisted:
return 0
case .men:
return 1
case .women:
return 2
case .mix:
return 3
}
}
var buildLabel: String {
switch self {
case .unlisted:
return ""
case .men:
return "H"
case .women:
return "D"
case .mix:
return "M"
}
}
var requestLabel: String {
switch self {
case .unlisted:
return ""
case .men:
return "DM"
case .women:
return "DD"
case .mix:
return "DX"
}
}
var importingRawValue: String {
switch self {
case .unlisted:
return "messieurs"
case .men:
return "messieurs"
case .women:
return "dames"
case .mix:
return "mixte"
}
}
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .unlisted:
return displayStyle == .title ? "Aucune" : ""
case .men:
switch displayStyle {
case .title:
return "Hommes"
case .wide:
return "Hommes"
case .short:
return "H"
}
case .women:
switch displayStyle {
case .title:
return "Dames"
case .wide:
return "Dames"
case .short:
return "D"
}
case .mix:
switch displayStyle {
case .title:
return "Mixte"
case .wide:
return "Mixte"
case .short:
return "MX"
}
}
}
var playerFilterOption: PlayerFilterOption {
switch self {
case .men, .unlisted:
return .all
case .women:
return .female
case .mix:
return .all
}
}
}
enum GroupStageOrderingMode: Int, Hashable, Codable, CaseIterable, Identifiable {
case random
case snake
case swiss
init?(rawValue: Int?) {
guard let value = rawValue else { return nil }
self.init(rawValue: value)
}
var id: Int { self.rawValue }
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .random:
return "Au hasard"
case .snake:
return "En serpentin"
case .swiss:
return "Suisse"
}
}
var systemImage: String {
switch self {
case .random:
return "dice.fill"
case .snake:
return "arrow.triangle.swap"
case .swiss:
return "cross.fill"
}
}
}
enum TournamentType: Int, Hashable, Codable, CaseIterable, Identifiable {
case classic
case doubleBrackets
var id: Int { self.rawValue }
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .classic:
return "Classique"
case .doubleBrackets:
return "Double Poules"
}
}
}
enum TeamPosition: Int, Identifiable, Hashable, Codable, CaseIterable {
case one
case two
var id: Int { self.rawValue }
var otherTeam: TeamPosition {
switch self {
case .one:
return .two
case .two:
return .one
}
}
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
var shortName: String {
switch self {
case .one:
return "#1"
case .two:
return "#2"
}
}
switch displayStyle {
case .wide, .title:
return "Équipe " + shortName
case .short:
return shortName
}
}
func localizedBranchLabel() -> String {
switch self {
case .one:
return "Branche du haut"
case .two:
return "Branche du bas"
}
}
}
enum SetFormat: Int, Hashable, Codable {
case nine
case four
case six
case superTieBreak
case megaTieBreak
func shouldTiebreak(scoreTeamOne: Int, scoreTeamTwo: Int) -> Bool {
if let tieBreak {
return (scoreTeamOne + scoreTeamTwo) >= (2 * tieBreak) && (scoreTeamOne == tieBreak || scoreTeamTwo == tieBreak)
} else {
return false
}
}
func winner(teamOne: Int, teamTwo: Int) -> TeamPosition {
return teamOne > teamTwo ? .one : .two
}
func hasEnded(teamOne: Int, teamTwo: Int) -> Bool {
switch self {
case .nine:
if teamOne == 9 || teamTwo == 9 {
return true
}
case .four:
if teamOne == 5 || teamTwo == 5 {
return true
}
case .six:
if teamOne == 7 || teamTwo == 7 {
return true
}
case .superTieBreak, .megaTieBreak:
if teamOne == 1 || teamTwo == 1 {
return true
}
}
return (teamOne >= scoreToWin && teamOne >= teamTwo + 2) || (teamTwo >= scoreToWin && teamTwo >= teamOne + 2)
}
var tieBreak: Int? {
switch self {
case .nine:
return 8
case .four:
return 4
case .six:
return 6
case .superTieBreak, .megaTieBreak:
return nil
}
}
func disableValuesForTeamTwo(with teamOneScore: Int) -> [Int] {
switch self {
case .nine:
if teamOneScore == 9 {
return [9]
}
case .four:
if teamOneScore == 4 {
return []
}
case .six:
if teamOneScore == 6 {
return []
}
case .superTieBreak:
if teamOneScore == 10 {
return []
}
case .megaTieBreak:
if teamOneScore == 15 {
return []
}
}
return []
}
var possibleValues: [Int] {
switch self {
case .nine:
return [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
case .four:
return [5, 4, 3, 2, 1, 0]
case .six:
return [7, 6, 5, 4, 3, 2, 1, 0]
case .superTieBreak:
return [10,9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
case .megaTieBreak:
return [15, 14, 13, 12, 11, 10,9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
}
}
var scoreToWin: Int {
switch self {
case .nine:
return 9
case .four:
return 4
case .six:
return 6
case .superTieBreak:
return 10
case .megaTieBreak:
return 15
}
}
var firstGameFormat: Format {
switch self {
case .megaTieBreak:
return .tiebreakFifteen
case .superTieBreak:
return .tiebreakTen
default:
return .normal
}
}
}
enum MatchType: String {
case bracket = "bracket"
case groupStage = "groupStage"
case loserBracket = "loserBracket"
}
enum MatchFormat: Int, Hashable, Codable, CaseIterable, Identifiable {
var id: Int { self.rawValue }
case twoSets
case twoSetsSuperTie
case twoSetsOfFourGames
case nineGames
case superTie
case megaTie
case twoSetsDecisivePoint
case twoSetsDecisivePointSuperTie
case twoSetsOfFourGamesDecisivePoint
case nineGamesDecisivePoint
case twoSetsOfSuperTie
case singleSet
case singleSetDecisivePoint
case singleSetOfFourGames
case singleSetOfFourGamesDecisivePoint
init?(rawValue: Int?) {
guard let value = rawValue else { return nil }
self.init(rawValue: value)
}
func defaultWalkOutScore(_ asWalkOutTeam: Bool) -> [Int] {
Array(repeating: asWalkOutTeam ? 0 : setFormat.scoreToWin, count: setsToWin)
}
var weight: Int {
switch self {
case .twoSets, .twoSetsDecisivePoint:
return 0
case .twoSetsSuperTie, .twoSetsDecisivePointSuperTie:
return 1
case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint:
return 2
case .nineGames, .nineGamesDecisivePoint:
return 3
case .superTie:
return 4
case .megaTie:
return 5
case .twoSetsOfSuperTie:
return 6
case .singleSet, .singleSetDecisivePoint:
return 7
case .singleSetOfFourGames, .singleSetOfFourGamesDecisivePoint:
return 8
}
}
var rank: Int {
switch self {
case .twoSets:
return 0
case .twoSetsDecisivePoint:
return 0
case .twoSetsSuperTie:
return 1
case .twoSetsDecisivePointSuperTie:
return 1
case .twoSetsOfFourGames:
return 2
case .twoSetsOfFourGamesDecisivePoint:
return 2
case .nineGames:
return 3
case .nineGamesDecisivePoint:
return 3
case .superTie:
return 4
case .megaTie:
return 5
case .twoSetsOfSuperTie:
return 6
case .singleSet, .singleSetDecisivePoint:
return 7
case .singleSetOfFourGames, .singleSetOfFourGamesDecisivePoint:
return 8
}
}
static func defaultFormatForMatchType(_ matchType: MatchType) -> MatchFormat {
switch matchType {
case .bracket:
DataStore.shared.user.bracketMatchFormatPreference ?? .nineGamesDecisivePoint
case .groupStage:
DataStore.shared.user.groupStageMatchFormatPreference ?? .nineGamesDecisivePoint
case .loserBracket:
DataStore.shared.user.loserBracketMatchFormatPreference ?? .nineGamesDecisivePoint
}
}
static var allCases: [MatchFormat] = {
[.twoSets, .twoSetsDecisivePoint, .twoSetsSuperTie, .twoSetsDecisivePointSuperTie, .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, .nineGames, .nineGamesDecisivePoint, .superTie, .megaTie, .twoSetsOfSuperTie, .singleSet, .singleSetDecisivePoint, .singleSetOfFourGames, .singleSetOfFourGamesDecisivePoint]
}()
func winner(scoreTeamOne: Int, scoreTeamTwo: Int) -> TeamPosition {
scoreTeamOne >= scoreTeamTwo ? .one : .two
}
func hasEnded(scoreTeamOne: Int, scoreTeamTwo: Int) -> Bool {
scoreTeamOne == setsToWin || scoreTeamTwo == setsToWin
}
var canSuperTie: Bool {
switch self {
case .twoSetsSuperTie, .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, .twoSetsDecisivePointSuperTie, .singleSetOfFourGames, .singleSetOfFourGamesDecisivePoint:
return true
default:
return false
}
}
func getEstimatedDuration(_ additionalDuration: Int = 0) -> Int {
estimatedDuration + additionalDuration
}
private var estimatedDuration: Int {
DataStore.shared.user.matchFormatsDefaultDuration?[self] ?? defaultEstimatedDuration
}
func formattedEstimatedDuration(_ additionalDuration: Int = 0) -> String {
Duration.seconds((estimatedDuration + additionalDuration) * 60).formatted(.units(allowed: [.minutes]))
}
func formattedEstimatedBreakDuration() -> String {
var label = Duration.seconds(breakTime.breakTime * 60).formatted(.units(allowed: [.minutes]))
if breakTime.matchCount > 1 {
label += " de pause après \(breakTime.matchCount) match"
label += breakTime.matchCount.pluralSuffix
} else {
label += " de pause"
}
return label
}
var defaultEstimatedDuration: Int {
switch self {
case .twoSets:
return 105
case .twoSetsDecisivePoint:
return 90
case .twoSetsSuperTie:
return 80
case .twoSetsDecisivePointSuperTie:
return 70
case .twoSetsOfFourGames:
return 60
case .twoSetsOfFourGamesDecisivePoint:
return 50
case .nineGames:
return 45
case .nineGamesDecisivePoint:
return 40
case .megaTie:
return 20
case .superTie:
return 15
case .twoSetsOfSuperTie:
return 25
case .singleSet:
return 30
case .singleSetDecisivePoint:
return 25
case .singleSetOfFourGames:
return 15
case .singleSetOfFourGamesDecisivePoint:
return 10
}
}
var estimatedTimeWithBreak: Int {
estimatedDuration + breakTime.breakTime
}
var breakTime: (breakTime: Int, matchCount: Int) {
switch self {
case .twoSets, .twoSetsDecisivePoint:
return (90, 1)
case .twoSetsSuperTie, .twoSetsDecisivePointSuperTie:
return (60, 1)
case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, .nineGames, .nineGamesDecisivePoint:
return (30, 1)
case .superTie:
return (15, 3)
default:
return (5, 1)
}
}
func maximumMatchPerDay(for matchCount: Int) -> Int {
switch self {
case .twoSets, .twoSetsDecisivePoint:
return matchCount < 5 ? 2 : 0
case .twoSetsSuperTie, .twoSetsDecisivePointSuperTie:
return matchCount < 6 ? 3 : 0
case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, .nineGames, .nineGamesDecisivePoint:
return matchCount < 7 ? 6 : 2
case .superTie:
return 7
default:
return 10
}
}
var hasDecisivePoint: Bool {
switch self {
case .nineGamesDecisivePoint, .twoSetsDecisivePoint, .twoSetsOfFourGamesDecisivePoint, .twoSetsDecisivePointSuperTie, .singleSetDecisivePoint, .singleSetOfFourGamesDecisivePoint:
return true
default:
return false
}
}
func newSetFormat(setCount: Int) -> SetFormat {
if setCount == 2 && canSuperTie {
return .superTieBreak
}
return setFormat
}
func formatTitle(_ displayStyle: DisplayStyle = .wide) -> String {
switch displayStyle {
case .short:
return ["Format ", shortFormat].joined()
default:
return ["Format ", shortFormat, suffix].joined()
}
}
var suffix: String {
switch self {
case .twoSetsDecisivePoint, .twoSetsDecisivePointSuperTie, .twoSetsOfFourGamesDecisivePoint, .nineGamesDecisivePoint, .singleSetDecisivePoint:
return " [Point Décisif]"
default:
return ""
}
}
var longPrefix: String {
return "Format \(format) : "
}
var shortPrefix: String {
return "\(format) : "
}
var isFederal: Bool {
switch self {
case .megaTie, .twoSetsOfSuperTie, .singleSet, .singleSetDecisivePoint, .singleSetOfFourGames, .singleSetOfFourGamesDecisivePoint:
return false
default:
return true
}
}
var format: String {
shortFormat + (isFederal ? "" : " (non officiel)")
}
var shortFormat: String {
switch self {
case .twoSets:
return "A1"
case .twoSetsSuperTie:
return "B1"
case .twoSetsOfFourGames:
return "C1"
case .nineGames:
return "D1"
case .superTie:
return "E"
case .twoSetsOfSuperTie:
return "G"
case .megaTie:
return "F"
case .singleSet:
return "H1"
case .singleSetDecisivePoint:
return "H2"
case .twoSetsDecisivePoint:
return "A2"
case .twoSetsDecisivePointSuperTie:
return "B2"
case .twoSetsOfFourGamesDecisivePoint:
return "C2"
case .nineGamesDecisivePoint:
return "D2"
case .singleSetOfFourGames:
return "I1"
case .singleSetOfFourGamesDecisivePoint:
return "I2"
}
}
var longLabel: String {
switch self {
case .singleSet, .singleSetDecisivePoint:
return "1 set de 6"
case .twoSets, .twoSetsDecisivePoint:
return "2 sets de 6"
case .twoSetsSuperTie, .twoSetsDecisivePointSuperTie:
return "2 sets de 6, supertie au 3ème"
case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint:
return "2 sets de 4, tiebreak à 4/4, supertie au 3ème"
case .nineGames, .nineGamesDecisivePoint:
return "9 jeux, tiebreak à 8/8"
case .twoSetsOfSuperTie:
return "2 sets de supertie de 10 points"
case .superTie:
return "supertie de 10 points"
case .megaTie:
return "supertie de 15 points"
case .singleSetOfFourGames, .singleSetOfFourGamesDecisivePoint:
return "1 set de 4 jeux, tiebreak à 4/4"
}
}
var computedShortLabelWithoutPrefix: String {
longLabel + suffix
}
var computedShortLabel: String {
shortPrefix + longLabel + suffix
}
var computedLongLabel: String {
longPrefix + longLabel + suffix
}
var setsToWin: Int {
switch self {
case .twoSets, .twoSetsSuperTie, .twoSetsOfFourGames, .twoSetsDecisivePoint, .twoSetsOfFourGamesDecisivePoint, .twoSetsDecisivePointSuperTie, .twoSetsOfSuperTie:
return 2
case .nineGames, .nineGamesDecisivePoint, .superTie, .megaTie, .singleSet, .singleSetDecisivePoint, .singleSetOfFourGames, .singleSetOfFourGamesDecisivePoint:
return 1
}
}
var setFormat: SetFormat {
switch self {
case .twoSets, .twoSetsSuperTie, .twoSetsDecisivePoint, .twoSetsDecisivePointSuperTie, .singleSet, .singleSetDecisivePoint:
return .six
case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, .singleSetOfFourGames, .singleSetOfFourGamesDecisivePoint:
return .four
case .nineGames, .nineGamesDecisivePoint:
return .nine
case .superTie, .twoSetsOfSuperTie:
return .superTieBreak
case .megaTie:
return .megaTieBreak
}
}
}
enum Format: Int, Hashable, Codable {
case normal
case tiebreakSeven
case tiebreakTen
case tiebreakFifteen
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .normal:
return "normal"
case .tiebreakSeven:
return "tie-break en 7"
case .tiebreakTen:
return "tie-break en 10"
case .tiebreakFifteen:
return "tie-break en 15"
}
}
var isTiebreak: Bool {
switch self {
case .normal:
return false
case .tiebreakSeven, .tiebreakTen, .tiebreakFifteen:
return true
}
}
var scoreToWin: Int? {
switch self {
case .normal:
return nil
case .tiebreakSeven:
return 7
case .tiebreakTen:
return 10
case .tiebreakFifteen:
return 15
}
}
}
enum ActionType: Int, Identifiable {
case fault
case winner
var id: Self {
self
}
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .fault:
return "Faute"
case .winner:
return "Point"
}
}
/*
Break points won
Break points
Errors
Smash winners
Smashes
Winners
Volley winners
Total points won
% points won
Total break points
Break points won
Gold points won
Gold points won returning
Gold points won with service
Most consecutive points won
Total set points
Set points won
Match points
*/
}
enum EventType: Int, CaseIterable, Identifiable {
case approvedTournament
case friendlyTournament
case simulation
case animation
var id: Self {
self
}
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .approvedTournament:
return "Tournoi homologué"
case .friendlyTournament:
return "Tournoi amical"
case .simulation:
return "Simulation"
case .animation:
return "Animation"
}
}
}
enum TeamSortingType: Int, Identifiable, CaseIterable, Hashable, Codable {
case rank = 1
case inscriptionDate = 2
var id: Int { rawValue }
init?(rawValue: Int?) {
guard let value = rawValue else { return nil }
self.init(rawValue: value)
}
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .rank:
return "Rang"
case .inscriptionDate:
return "Date d'inscription"
}
}
}
enum PlayersCountRange: Int, CaseIterable {
case N8 = 8
case N12 = 12
case N16 = 16
case N20 = 20
case N24 = 24
case N28 = 28
case N32 = 32
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .N8:
return "4 à 8"
case .N12:
return "9 à 12"
case .N16:
return "13 à 16"
case .N20:
return "17 à 20"
case .N24:
return "21 à 24"
case .N28:
return "25 à 28"
case .N32:
return "29 à 32"
}
}
}
enum RoundRule {
static let colors = ["#99ff99", "#66ff66", "#33cc33", "#009900", "#006600", "#336633", "#DD6600", "#EE6633", "#EE6633", "#EE6633"]
static func loserBrackets(index: Int) -> [String] {
switch index {
case 1:
return ["#3/#4"]
case 2:
return ["#5/#6", "#7/#8"]
default:
return ["#9/#10", "#11/#12", "#13/#14", "#15/#16", "#17/#18", "#19/#20", "#21/#22", "#23/#24", "#25/#26", "#27/#28", "#29/#30", "#31/#32"]
}
}
static func teamsInFirstRound(forTeams teams: Int) -> Int {
Int(pow(2.0, ceil(log2(Double(teams)))))
}
static func numberOfMatches(forTeams teams: Int) -> Int {
teamsInFirstRound(forTeams: teams) - 1
}
static func numberOfRounds(forTeams teams: Int) -> Int {
if teams == 0 { return 0 }
return Int(log2(Double(teamsInFirstRound(forTeams: teams))))
}
static func matchIndex(fromRoundIndex roundIndex: Int) -> Int {
guard roundIndex >= 0 else {
return -1 // Invalid round index
}
return (1 << roundIndex) - 1
}
static func matchIndex(fromBracketPosition: Int) -> Int {
roundIndex(fromMatchIndex: fromBracketPosition / 2) + fromBracketPosition%2
}
static func roundIndex(fromMatchIndex matchIndex: Int) -> Int {
Int(log2(Double(matchIndex + 1)))
}
static func numberOfMatches(forRoundIndex roundIndex: Int) -> Int {
Int(pow(2.0, Double(roundIndex)))
}
static func matchIndexWithinRound(fromMatchIndex matchIndex: Int) -> Int {
let roundIndex = roundIndex(fromMatchIndex: matchIndex)
let matchIndexWithinRound = matchIndex - (Int(pow(2.0, Double(roundIndex))) - 1)
return matchIndexWithinRound
}
static func roundName(fromMatchIndex matchIndex: Int) -> String {
let roundIndex = roundIndex(fromMatchIndex: matchIndex)
return roundName(fromRoundIndex: roundIndex)
}
static func roundName(fromRoundIndex roundIndex: Int, displayStyle: DisplayStyle = .wide) -> String {
switch roundIndex {
case 0:
return "Finale"
case 1:
if displayStyle == .short {
return "Demi"
}
return "Demi-finale"
case 2:
if displayStyle == .short {
return "Quart"
}
return "Quart de finale"
default:
return "\(Int(pow(2.0, Double(roundIndex))))ème"
}
}
}
enum AnimationType: Int, CaseIterable, Hashable, Identifiable {
case playerAnimation
case upAndDown
case brawl
var id: Int { rawValue }
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .playerAnimation:
return "Par joueur"
case .upAndDown:
return "Montante / Descendante"
case .brawl:
return "Brawl"
}
}
var descriptionLabel: String {
switch self {
case .playerAnimation:
return "Chaque joueur joue avec quelqu'un de différent à chaque rotation (8 à 12 joueurs)"
case .upAndDown:
return "Les gagnants montent sur le terrain d'à côté, les perdants descendent"
case .brawl:
return "A chaque rotation, les gagnants de la rotation précédente se jouent entre eux"
}
}
}
enum PadelTournamentStructurePreset: Int, Identifiable, CaseIterable {
var id: Int { self.rawValue }
case manual
case doubleGroupStage
case federalStructure_8
case federalStructure_12
case federalStructure_16
case federalStructure_20
case federalStructure_24
case federalStructure_32
case federalStructure_48
case federalStructure_64
// Maximum qualified pairs based on the structure preset
func tableDimension() -> Int {
switch self {
case .federalStructure_8:
return 8
case .federalStructure_12:
return 12
case .federalStructure_16:
return 16
case .federalStructure_20:
return 20
case .federalStructure_24:
return 24
case .federalStructure_32:
return 32
case .federalStructure_48:
return 48
case .federalStructure_64:
return 64
case .manual:
return 24
case .doubleGroupStage:
return 9
}
}
// Wildcards allowed in the Qualifiers
func wildcardBrackets() -> Int {
switch self {
case .federalStructure_8:
return 0
case .federalStructure_12:
return 1
case .federalStructure_16, .federalStructure_20, .federalStructure_24, .federalStructure_32:
return 2
case .federalStructure_48, .federalStructure_64:
return 4
case .manual, .doubleGroupStage:
return 0
}
}
// Wildcards allowed in the Qualifiers
func wildcardQualifiers() -> Int {
switch self {
case .federalStructure_8:
return 0
case .federalStructure_12, .federalStructure_16:
return 1
case .federalStructure_20, .federalStructure_24:
return 2
case .federalStructure_32:
return 4
case .federalStructure_48:
return 6
case .federalStructure_64:
return 8
case .manual, .doubleGroupStage:
return 0
}
}
// Number of teams admitted to the Qualifiers
func teamsInQualifiers() -> Int {
switch self {
case .federalStructure_8:
return 8
case .federalStructure_12:
return 12
case .federalStructure_16:
return 16
case .federalStructure_20:
return 20
case .federalStructure_24:
return 24
case .federalStructure_32:
return 32
case .federalStructure_48:
return 48
case .federalStructure_64:
return 64
case .manual, .doubleGroupStage:
return 0
}
}
// Maximum teams that can qualify from the Qualifiers to the Final Table
func maxTeamsFromQualifiers() -> Int {
switch self {
case .federalStructure_8, .federalStructure_12:
return 2
case .federalStructure_16, .federalStructure_20, .federalStructure_24:
return 4
case .federalStructure_32, .federalStructure_48, .federalStructure_64:
return 8
case .manual, .doubleGroupStage:
return 0
}
}
func localizedStructurePresetTitle() -> String {
switch self {
case .manual:
return "Défaut"
case .doubleGroupStage:
return "2 phases de poules"
case .federalStructure_8:
return "Structure fédérale 8"
case .federalStructure_12:
return "Structure fédérale 12"
case .federalStructure_16:
return "Structure fédérale 16"
case .federalStructure_20:
return "Structure fédérale 20"
case .federalStructure_24:
return "Structure fédérale 24"
case .federalStructure_32:
return "Structure fédérale 32"
case .federalStructure_48:
return "Structure fédérale 48"
case .federalStructure_64:
return "Structure fédérale 64"
}
}
func localizedDescriptionStructurePresetTitle() -> String {
switch self {
case .manual:
return "24 équipes, 4 poules de 4, 1 qualifié par poule"
case .doubleGroupStage:
return "Poules qui enchaînent sur une autre phase de poules: les premiers de chaque se retrouvent ensemble, puis les deuxièmes, etc."
case .federalStructure_8:
return "Tableau final à 8 paires, dont 2 qualifiées sortant de qualifications à 8 paires maximum. Aucune wildcard."
case .federalStructure_12, .federalStructure_16, .federalStructure_20, .federalStructure_24, .federalStructure_32, .federalStructure_48, .federalStructure_64:
return "Tableau final à \(tableDimension()) paires, dont \(maxTeamsFromQualifiers()) qualifiées sortant de qualifications à \(teamsInQualifiers()) paires maximum. \(wildcardBrackets()) wildcard\(wildcardBrackets().pluralSuffix) en tableau et \(wildcardQualifiers()) wildcard\(wildcardQualifiers().pluralSuffix) en qualifications."
}
}
func groupStageCount() -> Int {
switch self {
case .manual:
4
case .doubleGroupStage:
3
case .federalStructure_8:
2
case .federalStructure_12:
2
case .federalStructure_16:
4
case .federalStructure_20:
4
case .federalStructure_24:
4
case .federalStructure_32:
8
case .federalStructure_48:
8
case .federalStructure_64:
8
}
}
func teamsPerGroupStage() -> Int {
switch self {
case .manual:
4
case .doubleGroupStage:
3
case .federalStructure_8:
4
case .federalStructure_12:
6
case .federalStructure_16:
4
case .federalStructure_20:
5
case .federalStructure_24:
6
case .federalStructure_32:
4
case .federalStructure_48:
6
case .federalStructure_64:
8
}
}
func qualifiedPerGroupStage() -> Int {
switch self {
case .doubleGroupStage:
0
default:
1
}
}
func hasWildcards() -> Bool {
wildcardBrackets() > 0 || wildcardQualifiers() > 0
}
func isFederalPreset() -> Bool {
switch self {
case .manual:
return false
case .doubleGroupStage:
return false
case .federalStructure_8, .federalStructure_12, .federalStructure_16, .federalStructure_20, .federalStructure_24, .federalStructure_32, .federalStructure_48, .federalStructure_64:
return true
}
}
}
enum TournamentDeadlineType: String, CaseIterable {
case inscription = "Inscription"
case broadcastList = "Publication de la liste"
case wildcardRequest = "Demande de WC"
case wildcardLicensePurchase = "Prise de licence des WC"
case definitiveBroadcastList = "Publication définitive"
func daysOffset(level: TournamentLevel) -> Int {
if level == .p500 {
switch self {
case .inscription:
return -6
case .broadcastList:
return -6
case .wildcardRequest:
return -4
case .wildcardLicensePurchase, .definitiveBroadcastList:
return -4
}
} else {
switch self {
case .inscription:
return -13
case .broadcastList:
return -12
case .wildcardRequest:
return -9
case .wildcardLicensePurchase, .definitiveBroadcastList:
return -8
}
}
}
var timeOffset: DateComponents {
switch self {
case .broadcastList, .definitiveBroadcastList:
return DateComponents(hour: 12)
case .inscription, .wildcardRequest, .wildcardLicensePurchase:
return DateComponents(minute: -1)
}
}
}