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.
 
 
PadelClubData/PadelClubData/Data/PlayerRegistration.swift

306 lines
10 KiB

//
// PlayerRegistration.swift
// Padel Tournament
//
// Created by razmig on 10/03/2024.
//
import Foundation
import LeStorage
@Observable
final public class PlayerRegistration: BasePlayerRegistration, SideStorable {
public func localizedSourceLabel() -> String {
switch source {
case .frenchFederation:
return "base fédérale"
case .beachPadel:
return "beach-padel"
case nil:
if registeredOnline {
return "à vérifier vous-même"
} else {
return "créé par vous-même"
}
}
}
public var tournamentStore: TournamentStore? {
guard let storeId else {
fatalError("missing store id for \(String(describing: type(of: self)))")
}
return TournamentLibrary.shared.store(tournamentId: storeId)
// if let store = self.store as? TournamentStore {
// return store
// }
}
public var computedAge: Int? {
if let birthdate {
let components = birthdate.components(separatedBy: "/")
if let age = components.last, let ageInt = Int(age) {
let year = Calendar.current.getSportAge()
if age.count == 2 { //si l'année est sur 2 chiffres dans le fichier
if ageInt < 23 {
return year - 2000 - ageInt
} else {
return year - 2000 + 100 - ageInt
}
} else { //si l'année est représenté sur 4 chiffres
return year - ageInt
}
}
}
return nil
}
public func pasteData(_ exportFormat: ExportFormat = .rawText) -> String {
switch exportFormat {
case .rawText:
return [firstName.capitalized, lastName.capitalized, licenceId?.computedLicense].compactMap({ $0 }).joined(separator: exportFormat.separator())
case .csv:
return [lastName.uppercased() + " " + firstName.capitalized].joined(separator: exportFormat.separator())
}
}
public func isPlaying() -> Bool {
team()?.isPlaying() == true
}
public func contains(_ searchField: String) -> Bool {
let nameComponents = searchField.canonicalVersion.split(separator: " ")
if nameComponents.count > 1 {
let pairs = nameComponents.pairs()
return pairs.contains(where: {
(firstName.canonicalVersion.localizedCaseInsensitiveContains(String($0)) &&
lastName.canonicalVersion.localizedCaseInsensitiveContains(String($1))) ||
(firstName.canonicalVersion.localizedCaseInsensitiveContains(String($1)) &&
lastName.canonicalVersion.localizedCaseInsensitiveContains(String($0)))
})
} else {
return nameComponents.contains { component in
firstName.canonicalVersion.localizedCaseInsensitiveContains(component) ||
lastName.canonicalVersion.localizedCaseInsensitiveContains(component)
}
}
}
public func isSameAs(_ player: PlayerRegistration) -> Bool {
firstName.trimmedMultiline.canonicalVersion.localizedCaseInsensitiveCompare(player.firstName.trimmedMultiline.canonicalVersion) == .orderedSame &&
lastName.trimmedMultiline.canonicalVersion.localizedCaseInsensitiveCompare(player.lastName.trimmedMultiline.canonicalVersion) == .orderedSame
}
public func tournament() -> Tournament? {
guard let tournament = team()?.tournament else { return nil }
return Store.main.findById(tournament)
}
public func team() -> TeamRegistration? {
guard let teamRegistration else { return nil }
return self.tournamentStore?.teamRegistrations.findById(teamRegistration)
}
public func isHere() -> Bool {
hasArrived
}
public func hasPaid() -> Bool {
paymentType != nil
}
public func playerLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch displayStyle {
case .wide, .title:
return lastName.trimmed.capitalized + " " + firstName.trimmed.capitalized
case .short:
let names = lastName.components(separatedBy: .whitespaces)
if lastName.components(separatedBy: .whitespaces).count > 1 {
if let firstLongWord = names.first(where: { $0.count > 3 }) {
return firstLongWord.trimmed.capitalized.trunc(length: 10) + " " + firstName.trimmed.prefix(1).capitalized + "."
}
}
return lastName.trimmed.capitalized.trunc(length: 10) + " " + firstName.trimmed.prefix(1).capitalized + "."
}
}
public func isImported() -> Bool {
source == .beachPadel
}
public func unrankedOrUnknown() -> Bool {
source == nil
}
public func isValidLicenseNumber(year: Int) -> Bool {
guard let licenceId else { return false }
guard licenceId.isLicenseNumber else { return false }
guard licenceId.suffix(6) == "(\(year))" else { return false }
return true
}
@objc
public var canonicalName: String {
playerLabel().folding(options: .diacriticInsensitive, locale: .current).lowercased()
}
public func rankLabel(_ displayStyle: DisplayStyle = .wide) -> String {
if let rank, rank > 0 {
if rank != computedRank {
return computedRank.formatted() + " (" + rank.formatted() + ")"
} else {
return rank.formatted()
}
} else {
return "non classé" + (isMalePlayer() ? "" : "e")
}
}
public func setComputedRank(in tournament: Tournament) {
let currentRank = rank ?? tournament.unrankValue(for: isMalePlayer()) ?? 90_000
switch tournament.tournamentCategory {
case .men:
computedRank = isMalePlayer() ? currentRank : currentRank + PlayerRegistration.addon(for: currentRank, manMax: tournament.maleUnrankedValue ?? 0, womanMax: tournament.femaleUnrankedValue ?? 0)
default:
computedRank = currentRank
}
}
public func setClubMember(for tournament: Tournament) {
guard let clubCode, clubCode.isEmpty == false, let code = tournament.eventObject()?.clubObject()?.code, code.isEmpty == false else {
return
}
self.clubMember = clubCode.replaceCharactersFromSet(characterSet: .whitespaces).caseInsensitiveCompare(code.replaceCharactersFromSet(characterSet: .whitespaces)) == .orderedSame
}
public func isMalePlayer() -> Bool {
sex == .male
}
public func validateLicenceId(_ year: Int) {
if let currentLicenceId = licenceId {
if currentLicenceId.trimmed.hasSuffix("(\(year-1))") {
self.licenceId = currentLicenceId.replacingOccurrences(of: "\(year-1)", with: "\(year)")
} else if let computedLicense = currentLicenceId.strippedLicense?.computedLicense {
self.licenceId = computedLicense + " (\(year))"
}
}
}
public func hasPaidOnline() -> Bool {
registrationStatus == .confirmed && paymentId != nil && paymentType == .creditCard
}
public func hasConfirmed() -> Bool {
registrationStatus == .confirmed
}
public func confirmRegistration() {
registrationStatus = .confirmed
}
public func paidAmount(_ tournament: Tournament, accountForGiftOrForfeit: Bool = false) -> Double {
if accountForGiftOrForfeit == false, paymentType == .gift {
return 0.0
}
if accountForGiftOrForfeit == false, paymentType == .forfeit {
return 0.0
}
if hasPaid(), let entryFee = tournament.entryFee {
if clubMember, let clubMemberFeeDeduction = tournament.clubMemberFeeDeduction {
return entryFee - clubMemberFeeDeduction
} else {
return entryFee
}
} else {
return 0.0
}
}
public func remainingAmount(_ tournament: Tournament) -> Double {
if let entryFee = tournament.entryFee {
if clubMember, let clubMemberFeeDeduction = tournament.clubMemberFeeDeduction {
return entryFee - clubMemberFeeDeduction - paidAmount(tournament, accountForGiftOrForfeit: true)
} else {
return entryFee - paidAmount(tournament, accountForGiftOrForfeit: true)
}
} else {
return 0.0
}
}
public func totalIncome(_ tournament: Tournament) -> Double {
if let entryFee = tournament.entryFee {
if clubMember, let clubMemberFeeDeduction = tournament.clubMemberFeeDeduction {
return entryFee - clubMemberFeeDeduction
} else {
return entryFee
}
} else {
return 0.0
}
}
public enum PlayerDataSource: Int, Codable {
case frenchFederation = 0
case beachPadel = 1
}
public enum RegistrationStatus: Int, Codable, CaseIterable, Identifiable {
case waiting = 0
case pending = 1
case confirmed = 2
case canceled = 3
public var id: Int { self.rawValue }
public func localizedRegistrationStatus() -> String {
switch self {
case .waiting:
return "En attente"
case .pending:
return "En cours"
case .confirmed:
return "Confirmé"
case .canceled:
return "Annulé"
}
}
}
public 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
default:
return TournamentCategory.femaleInMaleAssimilationAddition(playerRank)
}
}
func insertOnServer() {
self.tournamentStore?.playerRegistrations.writeChangeAndInsertOnServer(instance: self)
}
}
enum PlayerDataSource: Int, Codable {
case frenchFederation = 0
case beachPadel = 1
}
public enum PlayerSexType: Int, Hashable, CaseIterable, Identifiable, Codable {
init?(rawValue: Int?) {
guard let value = rawValue else { return nil }
self.init(rawValue: value)
}
public var id: Self {
self
}
case female = 0
case male = 1
}