package collections import ( "context" "fmt" "sort" "strings" ) // NewMasterCollectionList creates a new master collection list func NewMasterCollectionList(database CollectionDatabase) *MasterCollectionList { return &MasterCollectionList{ collections: make(map[int32]*Collection), database: database, } } // Initialize loads all collections from the database func (mcl *MasterCollectionList) Initialize(ctx context.Context, itemLookup ItemLookup) error { mcl.mu.Lock() defer mcl.mu.Unlock() // Load collection data collectionData, err := mcl.database.LoadCollections(ctx) if err != nil { return fmt.Errorf("failed to load collections: %w", err) } totalItems := 0 totalRewards := 0 for _, data := range collectionData { collection := NewCollection() collection.SetID(data.ID) collection.SetName(data.Name) collection.SetCategory(data.Category) collection.SetLevel(data.Level) // Load collection items items, err := mcl.database.LoadCollectionItems(ctx, data.ID) if err != nil { return fmt.Errorf("failed to load items for collection %d: %w", data.ID, err) } for _, item := range items { // Validate item exists if itemLookup != nil && !itemLookup.ItemExists(item.ItemID) { continue // Skip non-existent items } collection.AddCollectionItem(item) totalItems++ } // Load collection rewards rewards, err := mcl.database.LoadCollectionRewards(ctx, data.ID) if err != nil { return fmt.Errorf("failed to load rewards for collection %d: %w", data.ID, err) } if err := collection.LoadFromRewardData(rewards); err != nil { return fmt.Errorf("failed to load reward data for collection %d: %w", data.ID, err) } totalRewards += len(rewards) // Validate collection before adding if err := collection.Validate(); err != nil { return fmt.Errorf("invalid collection %d (%s): %w", data.ID, data.Name, err) } if !mcl.addCollectionNoLock(collection) { return fmt.Errorf("duplicate collection ID: %d", data.ID) } } return nil } // AddCollection adds a collection to the master list func (mcl *MasterCollectionList) AddCollection(collection *Collection) bool { mcl.mu.Lock() defer mcl.mu.Unlock() return mcl.addCollectionNoLock(collection) } // addCollectionNoLock adds a collection without acquiring the lock func (mcl *MasterCollectionList) addCollectionNoLock(collection *Collection) bool { if collection == nil { return false } id := collection.GetID() if _, exists := mcl.collections[id]; exists { return false } mcl.collections[id] = collection return true } // GetCollection retrieves a collection by ID func (mcl *MasterCollectionList) GetCollection(collectionID int32) *Collection { mcl.mu.RLock() defer mcl.mu.RUnlock() return mcl.collections[collectionID] } // GetCollectionCopy retrieves a copy of a collection by ID func (mcl *MasterCollectionList) GetCollectionCopy(collectionID int32) *Collection { mcl.mu.RLock() defer mcl.mu.RUnlock() if collection, exists := mcl.collections[collectionID]; exists { return NewCollectionFromData(collection) } return nil } // ClearCollections removes all collections func (mcl *MasterCollectionList) ClearCollections() { mcl.mu.Lock() defer mcl.mu.Unlock() mcl.collections = make(map[int32]*Collection) } // Size returns the number of collections func (mcl *MasterCollectionList) Size() int { mcl.mu.RLock() defer mcl.mu.RUnlock() return len(mcl.collections) } // NeedsItem checks if any collection needs a specific item func (mcl *MasterCollectionList) NeedsItem(itemID int32) bool { mcl.mu.RLock() defer mcl.mu.RUnlock() for _, collection := range mcl.collections { if collection.NeedsItem(itemID) { return true } } return false } // GetCollectionsByCategory returns collections in a specific category func (mcl *MasterCollectionList) GetCollectionsByCategory(category string) []*Collection { mcl.mu.RLock() defer mcl.mu.RUnlock() var result []*Collection for _, collection := range mcl.collections { if collection.GetCategory() == category { result = append(result, collection) } } return result } // GetCollectionsByLevel returns collections for a specific level range func (mcl *MasterCollectionList) GetCollectionsByLevel(minLevel, maxLevel int8) []*Collection { mcl.mu.RLock() defer mcl.mu.RUnlock() var result []*Collection for _, collection := range mcl.collections { level := collection.GetLevel() if level >= minLevel && level <= maxLevel { result = append(result, collection) } } return result } // GetAllCollections returns all collections func (mcl *MasterCollectionList) GetAllCollections() []*Collection { mcl.mu.RLock() defer mcl.mu.RUnlock() result := make([]*Collection, 0, len(mcl.collections)) for _, collection := range mcl.collections { result = append(result, collection) } return result } // GetCollectionIDs returns all collection IDs func (mcl *MasterCollectionList) GetCollectionIDs() []int32 { mcl.mu.RLock() defer mcl.mu.RUnlock() ids := make([]int32, 0, len(mcl.collections)) for id := range mcl.collections { ids = append(ids, id) } sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] }) return ids } // GetCategories returns all unique categories func (mcl *MasterCollectionList) GetCategories() []string { mcl.mu.RLock() defer mcl.mu.RUnlock() categoryMap := make(map[string]bool) for _, collection := range mcl.collections { category := collection.GetCategory() if category != "" { categoryMap[category] = true } } categories := make([]string, 0, len(categoryMap)) for category := range categoryMap { categories = append(categories, category) } sort.Strings(categories) return categories } // GetCollectionsRequiringItem returns collections that need a specific item func (mcl *MasterCollectionList) GetCollectionsRequiringItem(itemID int32) []*Collection { mcl.mu.RLock() defer mcl.mu.RUnlock() var result []*Collection for _, collection := range mcl.collections { if collection.NeedsItem(itemID) { result = append(result, collection) } } return result } // GetStatistics returns master collection list statistics func (mcl *MasterCollectionList) GetStatistics() CollectionStatistics { mcl.mu.RLock() defer mcl.mu.RUnlock() stats := CollectionStatistics{ TotalCollections: len(mcl.collections), } for _, collection := range mcl.collections { if collection.GetCompleted() { stats.CompletedCollections++ } if collection.GetTotalItemsCount() > 0 { stats.ActiveCollections++ } stats.TotalItems += collection.GetTotalItemsCount() stats.FoundItems += collection.GetFoundItemsCount() stats.TotalRewards += len(collection.GetRewardItems()) + len(collection.GetSelectableRewardItems()) if collection.GetRewardCoin() > 0 { stats.TotalRewards++ } if collection.GetRewardXP() > 0 { stats.TotalRewards++ } } return stats } // ValidateIntegrity checks the integrity of all collections func (mcl *MasterCollectionList) ValidateIntegrity(itemLookup ItemLookup) []error { mcl.mu.RLock() defer mcl.mu.RUnlock() var errors []error for _, collection := range mcl.collections { if err := collection.Validate(); err != nil { errors = append(errors, fmt.Errorf("collection %d (%s): %w", collection.GetID(), collection.GetName(), err)) } // Check if all required items exist if itemLookup != nil { for _, item := range collection.GetCollectionItems() { if !itemLookup.ItemExists(item.ItemID) { errors = append(errors, fmt.Errorf("collection %d (%s) references non-existent item %d", collection.GetID(), collection.GetName(), item.ItemID)) } } // Check reward items for _, item := range collection.GetRewardItems() { if !itemLookup.ItemExists(item.ItemID) { errors = append(errors, fmt.Errorf("collection %d (%s) has non-existent reward item %d", collection.GetID(), collection.GetName(), item.ItemID)) } } for _, item := range collection.GetSelectableRewardItems() { if !itemLookup.ItemExists(item.ItemID) { errors = append(errors, fmt.Errorf("collection %d (%s) has non-existent selectable reward item %d", collection.GetID(), collection.GetName(), item.ItemID)) } } } } return errors } // FindCollectionsByName searches for collections by name (case-insensitive) func (mcl *MasterCollectionList) FindCollectionsByName(searchTerm string) []*Collection { mcl.mu.RLock() defer mcl.mu.RUnlock() var result []*Collection searchLower := strings.ToLower(searchTerm) for _, collection := range mcl.collections { if strings.Contains(strings.ToLower(collection.GetName()), searchLower) { result = append(result, collection) } } // Sort by name sort.Slice(result, func(i, j int) bool { return result[i].GetName() < result[j].GetName() }) return result }