// // PlayerRegistration.swift // Padel Tournament // // Created by razmig on 10/03/2024. // import Foundation import LeStorage @Observable class PlayerRegistration: ModelObject, Storable { static func resourceName() -> String { "player-registrations" } var id: String = Store.randomId() var teamRegistration: String? var firstName: String var lastName: String var licenceId: String? var rank: Int? var registrationType: Int? var registrationDate: Date? var sex: Int var tournamentPlayed: Int? var points: Double? var clubName: String? var ligueName: String? var assimilation: String? var phoneNumber: String? var email: String? var birthdate: String? var weight: Int = 0 internal init(teamRegistration: String = "", firstName: String, lastName: String, licenceId: String? = nil, rank: Int? = nil, registrationType: Int? = nil, registrationDate: Date? = nil, sex: Int) { self.teamRegistration = teamRegistration self.firstName = firstName self.lastName = lastName self.licenceId = licenceId self.rank = rank self.registrationType = registrationType self.registrationDate = registrationDate self.sex = sex } internal init(importedPlayer: ImportedPlayer) { self.teamRegistration = "" self.firstName = importedPlayer.firstName ?? "" self.lastName = importedPlayer.lastName ?? "" self.licenceId = importedPlayer.license ?? nil self.rank = Int(importedPlayer.rank) self.sex = importedPlayer.male ? 1 : 0 self.tournamentPlayed = importedPlayer.tournamentPlayed self.points = importedPlayer.getPoints() self.clubName = importedPlayer.clubName self.ligueName = importedPlayer.ligueName self.assimilation = importedPlayer.assimilation } internal init(federalData: [String], sex: Int, sexUnknown: Bool) { lastName = federalData[0] firstName = federalData[1] birthdate = federalData[2] licenceId = federalData[3] clubName = federalData[4] rank = Int(federalData[5]) email = federalData[6] phoneNumber = federalData[7] // manuallyCreated = false if sexUnknown { if sex == 1 && FileImportManager.shared.foundInWomenData(license: federalData[3]) { self.sex = 0 } else if FileImportManager.shared.foundInMenData(license: federalData[3]) { self.sex = 1 } else { self.sex = -1 } } else { self.sex = sex } } func contains(_ searchField: String) -> Bool { firstName.localizedCaseInsensitiveContains(searchField) || lastName.localizedCaseInsensitiveContains(searchField) } func isSameAs(_ player: PlayerRegistration) -> Bool { firstName.localizedCaseInsensitiveCompare(player.firstName) == .orderedSame && lastName.localizedCaseInsensitiveCompare(player.lastName) == .orderedSame } func tournament() -> Tournament? { guard let tournament = team()?.tournament else { return nil } return Store.main.findById(tournament) } func team() -> TeamRegistration? { guard let teamRegistration else { return nil } return Store.main.findById(teamRegistration) } func hasPaid() -> Bool { registrationType != nil } var paymentType: PaymentType { get { PaymentType(rawValue: registrationType ?? -1) ?? .notPaid } set { registrationType = newValue.rawValue } } func playerLabel(_ displayStyle: DisplayStyle = .wide) -> String { lastName.trimmed.capitalized + " " + firstName.trimmed.capitalized } func rankLabel(_ displayStyle: DisplayStyle = .wide) -> String { if let rank, rank > 0 { return rank.formatted() } else { return "non classé" + (isMalePlayer() ? "" : "e") } } func getRank() -> Int { weight } @MainActor func updateRank(from sources: [CSVParser], lastRank: Int) async throws { if let value = try await history(from: sources)?.rank { rank = value } else { rank = lastRank } } func history(from sources: [CSVParser]) async throws -> Line? { guard let license = licenceId?.strippedLicense else { return try await historyFromName(from: sources) } return await withTaskGroup(of: Line?.self) { group in for source in sources.filter({ $0.maleData == isMalePlayer() }) { group.addTask { guard !Task.isCancelled else { print("Cancelled"); return nil } return try? await source.first(where: { line in line.rawValue.contains(";\(license);") }) } } if let first = await group.first(where: { $0 != nil }) { group.cancelAll() return first } else { return nil } } } func historyFromName(from sources: [CSVParser]) async throws -> Line? { return await withTaskGroup(of: Line?.self) { group in for source in sources.filter({ $0.maleData == isMalePlayer() }) { group.addTask { [lastName, firstName] in guard !Task.isCancelled else { print("Cancelled"); return nil } return try? await source.first(where: { line in line.rawValue.canonicalVersionWithPunctuation.contains(";\(lastName.canonicalVersionWithPunctuation);\(firstName.canonicalVersionWithPunctuation);") }) } } if let first = await group.first(where: { $0 != nil }) { group.cancelAll() return first } else { return nil } } } func setWeight(in tournament: Tournament) { let currentRank = rank ?? tournament.unrankValue(for: isMalePlayer()) ?? 100_000 switch tournament.tournamentCategory { case .men: weight = isMalePlayer() ? currentRank : currentRank + PlayerRegistration.addon(for: currentRank, manMax: tournament.maleUnrankedValue ?? 0, womanMax: tournament.femaleUnrankedValue ?? 0) default: weight = currentRank } } func isMalePlayer() -> Bool { sex == 1 } enum CodingKeys: String, CodingKey { case _id = "id" case _teamRegistration = "teamRegistration" case _firstName = "firstName" case _lastName = "lastName" case _licenceId = "licenceId" case _rank = "rank" case _registrationType = "registrationType" case _registrationDate = "registrationDate" case _sex = "sex" case _tournamentPlayed = "tournamentPlayed" case _points = "points" case _clubName = "clubName" case _ligueName = "ligueName" case _assimilation = "assimilation" case _birthdate = "birthdate" case _phoneNumber = "phoneNumber" case _email = "email" case _weight = "weight" } enum PaymentType: Int, CaseIterable, Identifiable { var id: Self { self } case notPaid = -1 case cash = 0 case lydia = 1 case gift = 2 case check = 3 case paylib = 4 var localizedLabel: String { switch self { case .notPaid: return "Non réglé" case .check: return "Chèque" case .cash: return "Cash" case .lydia: return "Lydia" case .paylib: return "Paylib" case .gift: return "Offert" } } } static func addon(for playerRank: Int, manMax: Int, womanMax: Int) -> Int { switch playerRank { case 0: return 0 case womanMax: return manMax - womanMax case manMax: return 0 case 1...10: return 400 case 11...30: return 1000 case 31...60: return 2000 case 61...100: return 3000 case 101...200: return 8000 case 201...500: return 12000 default: return 15000 } } } extension PlayerRegistration: Hashable { static func == (lhs: PlayerRegistration, rhs: PlayerRegistration) -> Bool { lhs.id == rhs.id } func hash(into hasher: inout Hasher) { hasher.combine(id) } } extension PlayerRegistration: PlayerHolder { func getFirstName() -> String { firstName } func getLastName() -> String { lastName } func getPoints() -> Double? { self.points } func getRank() -> Int? { rank } func isUnranked() -> Bool { rank == nil } func formattedRank() -> String { self.rankLabel() } func formattedLicense() -> String { if let licenceId { return licenceId.computedLicense } return "aucune licence" } var male: Bool { isMalePlayer() } }