eq2go/internal/collections/master_list.go

337 lines
8.6 KiB
Go

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
}