package appearances import ( "fmt" "sync" ) // Appearances manages a collection of appearance objects with thread-safe operations type Appearances struct { appearanceMap map[int32]*Appearance // Map of appearance ID to appearance mutex sync.RWMutex // Thread safety for concurrent access } // NewAppearances creates a new appearances manager func NewAppearances() *Appearances { return &Appearances{ appearanceMap: make(map[int32]*Appearance), } } // Reset clears all appearances from the manager func (a *Appearances) Reset() { a.ClearAppearances() } // ClearAppearances removes all appearances from the manager func (a *Appearances) ClearAppearances() { a.mutex.Lock() defer a.mutex.Unlock() // Clear the map - Go's garbage collector will handle cleanup a.appearanceMap = make(map[int32]*Appearance) } // InsertAppearance adds an appearance to the manager func (a *Appearances) InsertAppearance(appearance *Appearance) error { if appearance == nil { return fmt.Errorf("appearance cannot be nil") } a.mutex.Lock() defer a.mutex.Unlock() a.appearanceMap[appearance.GetID()] = appearance return nil } // FindAppearanceByID retrieves an appearance by its ID func (a *Appearances) FindAppearanceByID(id int32) *Appearance { a.mutex.RLock() defer a.mutex.RUnlock() if appearance, exists := a.appearanceMap[id]; exists { return appearance } return nil } // HasAppearance checks if an appearance exists by ID func (a *Appearances) HasAppearance(id int32) bool { a.mutex.RLock() defer a.mutex.RUnlock() _, exists := a.appearanceMap[id] return exists } // GetAppearanceCount returns the total number of appearances func (a *Appearances) GetAppearanceCount() int { a.mutex.RLock() defer a.mutex.RUnlock() return len(a.appearanceMap) } // GetAllAppearances returns a copy of all appearances func (a *Appearances) GetAllAppearances() map[int32]*Appearance { a.mutex.RLock() defer a.mutex.RUnlock() // Return a copy to prevent external modification result := make(map[int32]*Appearance) for id, appearance := range a.appearanceMap { result[id] = appearance } return result } // GetAppearanceIDs returns all appearance IDs func (a *Appearances) GetAppearanceIDs() []int32 { a.mutex.RLock() defer a.mutex.RUnlock() ids := make([]int32, 0, len(a.appearanceMap)) for id := range a.appearanceMap { ids = append(ids, id) } return ids } // FindAppearancesByName finds all appearances with names containing the given substring func (a *Appearances) FindAppearancesByName(nameSubstring string) []*Appearance { a.mutex.RLock() defer a.mutex.RUnlock() var results []*Appearance for _, appearance := range a.appearanceMap { if contains(appearance.GetName(), nameSubstring) { results = append(results, appearance) } } return results } // FindAppearancesByMinClient finds all appearances with a specific minimum client version func (a *Appearances) FindAppearancesByMinClient(minClient int16) []*Appearance { a.mutex.RLock() defer a.mutex.RUnlock() var results []*Appearance for _, appearance := range a.appearanceMap { if appearance.GetMinClientVersion() == minClient { results = append(results, appearance) } } return results } // GetCompatibleAppearances returns all appearances compatible with the given client version func (a *Appearances) GetCompatibleAppearances(clientVersion int16) []*Appearance { a.mutex.RLock() defer a.mutex.RUnlock() var results []*Appearance for _, appearance := range a.appearanceMap { if appearance.IsCompatibleWithClient(clientVersion) { results = append(results, appearance) } } return results } // RemoveAppearance removes an appearance by ID func (a *Appearances) RemoveAppearance(id int32) bool { a.mutex.Lock() defer a.mutex.Unlock() if _, exists := a.appearanceMap[id]; exists { delete(a.appearanceMap, id) return true } return false } // UpdateAppearance updates an existing appearance or inserts it if it doesn't exist func (a *Appearances) UpdateAppearance(appearance *Appearance) error { if appearance == nil { return fmt.Errorf("appearance cannot be nil") } a.mutex.Lock() defer a.mutex.Unlock() a.appearanceMap[appearance.GetID()] = appearance return nil } // GetAppearancesByIDRange returns all appearances within the given ID range (inclusive) func (a *Appearances) GetAppearancesByIDRange(minID, maxID int32) []*Appearance { a.mutex.RLock() defer a.mutex.RUnlock() var results []*Appearance for id, appearance := range a.appearanceMap { if id >= minID && id <= maxID { results = append(results, appearance) } } return results } // ValidateAppearances checks all appearances for consistency func (a *Appearances) ValidateAppearances() []string { a.mutex.RLock() defer a.mutex.RUnlock() var issues []string for id, appearance := range a.appearanceMap { if appearance == nil { issues = append(issues, fmt.Sprintf("Appearance ID %d is nil", id)) continue } if appearance.GetID() != id { issues = append(issues, fmt.Sprintf("Appearance ID mismatch: map key %d != appearance ID %d", id, appearance.GetID())) } if len(appearance.GetName()) == 0 { issues = append(issues, fmt.Sprintf("Appearance ID %d has empty name", id)) } if appearance.GetMinClientVersion() < 0 { issues = append(issues, fmt.Sprintf("Appearance ID %d has negative min client version: %d", id, appearance.GetMinClientVersion())) } } return issues } // IsValid returns true if all appearances are valid func (a *Appearances) IsValid() bool { issues := a.ValidateAppearances() return len(issues) == 0 } // GetStatistics returns statistics about the appearance collection func (a *Appearances) GetStatistics() map[string]any { a.mutex.RLock() defer a.mutex.RUnlock() stats := make(map[string]any) stats["total_appearances"] = len(a.appearanceMap) // Count by minimum client version versionCounts := make(map[int16]int) for _, appearance := range a.appearanceMap { versionCounts[appearance.GetMinClientVersion()]++ } stats["appearances_by_min_client"] = versionCounts // Find ID range if len(a.appearanceMap) > 0 { var minID, maxID int32 first := true for id := range a.appearanceMap { if first { minID = id maxID = id first = false } else { if id < minID { minID = id } if id > maxID { maxID = id } } } stats["min_id"] = minID stats["max_id"] = maxID stats["id_range"] = maxID - minID } return stats } // contains checks if a string contains a substring (case-sensitive) func contains(str, substr string) bool { if len(substr) == 0 { return true } if len(str) < len(substr) { return false } for i := 0; i <= len(str)-len(substr); i++ { if str[i:i+len(substr)] == substr { return true } } return false }