package languages import ( "fmt" "sync" ) // Manager provides high-level management of the language system type Manager struct { masterLanguagesList *MasterLanguagesList database Database logger Logger mutex sync.RWMutex // Statistics languageLookups int64 playersWithLanguages int64 languageUsageCount map[int32]int64 // Language ID -> usage count } // NewManager creates a new language manager func NewManager(database Database, logger Logger) *Manager { return &Manager{ masterLanguagesList: NewMasterLanguagesList(), database: database, logger: logger, languageUsageCount: make(map[int32]int64), } } // Initialize loads languages from database func (m *Manager) Initialize() error { if m.logger != nil { m.logger.LogInfo("Initializing language manager...") } if m.database == nil { if m.logger != nil { m.logger.LogWarning("No database provided, starting with empty language list") } return nil } // Load languages from database languages, err := m.database.LoadAllLanguages() if err != nil { return fmt.Errorf("failed to load languages from database: %w", err) } for _, language := range languages { if err := m.masterLanguagesList.AddLanguage(language); err != nil { if m.logger != nil { m.logger.LogError("Failed to add language %d (%s): %v", language.GetID(), language.GetName(), err) } } } if m.logger != nil { m.logger.LogInfo("Loaded %d languages from database", len(languages)) } return nil } // GetMasterLanguagesList returns the master language list func (m *Manager) GetMasterLanguagesList() *MasterLanguagesList { return m.masterLanguagesList } // CreatePlayerLanguagesList creates a new player language list func (m *Manager) CreatePlayerLanguagesList() *PlayerLanguagesList { m.mutex.Lock() m.playersWithLanguages++ m.mutex.Unlock() return NewPlayerLanguagesList() } // GetLanguage returns a language by ID func (m *Manager) GetLanguage(id int32) *Language { m.mutex.Lock() m.languageLookups++ m.mutex.Unlock() return m.masterLanguagesList.GetLanguage(id) } // GetLanguageByName returns a language by name func (m *Manager) GetLanguageByName(name string) *Language { m.mutex.Lock() m.languageLookups++ m.mutex.Unlock() return m.masterLanguagesList.GetLanguageByName(name) } // AddLanguage adds a new language to the system func (m *Manager) AddLanguage(language *Language) error { if language == nil { return fmt.Errorf("language cannot be nil") } // Add to master list if err := m.masterLanguagesList.AddLanguage(language); err != nil { return fmt.Errorf("failed to add language to master list: %w", err) } // Save to database if available if m.database != nil { if err := m.database.SaveLanguage(language); err != nil { // Remove from master list if database save failed m.masterLanguagesList.RemoveLanguage(language.GetID()) return fmt.Errorf("failed to save language to database: %w", err) } } if m.logger != nil { m.logger.LogInfo("Added language %d: %s", language.GetID(), language.GetName()) } return nil } // UpdateLanguage updates an existing language func (m *Manager) UpdateLanguage(language *Language) error { if language == nil { return fmt.Errorf("language cannot be nil") } // Update in master list if err := m.masterLanguagesList.UpdateLanguage(language); err != nil { return fmt.Errorf("failed to update language in master list: %w", err) } // Save to database if available if m.database != nil { if err := m.database.SaveLanguage(language); err != nil { return fmt.Errorf("failed to save language to database: %w", err) } } if m.logger != nil { m.logger.LogInfo("Updated language %d: %s", language.GetID(), language.GetName()) } return nil } // RemoveLanguage removes a language from the system func (m *Manager) RemoveLanguage(id int32) error { // Check if language exists if !m.masterLanguagesList.HasLanguage(id) { return fmt.Errorf("language with ID %d does not exist", id) } // Remove from database first if available if m.database != nil { if err := m.database.DeleteLanguage(id); err != nil { return fmt.Errorf("failed to delete language from database: %w", err) } } // Remove from master list if !m.masterLanguagesList.RemoveLanguage(id) { return fmt.Errorf("failed to remove language from master list") } if m.logger != nil { m.logger.LogInfo("Removed language %d", id) } return nil } // RecordLanguageUsage records language usage for statistics func (m *Manager) RecordLanguageUsage(languageID int32) { m.mutex.Lock() defer m.mutex.Unlock() m.languageUsageCount[languageID]++ } // GetStatistics returns language system statistics func (m *Manager) GetStatistics() *LanguageStatistics { m.mutex.RLock() defer m.mutex.RUnlock() // Create language name mapping languagesByName := make(map[string]int32) allLanguages := m.masterLanguagesList.GetAllLanguages() for _, lang := range allLanguages { languagesByName[lang.GetName()] = lang.GetID() } // Copy usage count usageCount := make(map[int32]int64) for id, count := range m.languageUsageCount { usageCount[id] = count } return &LanguageStatistics{ TotalLanguages: len(allLanguages), PlayersWithLanguages: int(m.playersWithLanguages), LanguageUsageCount: usageCount, LanguageLookups: m.languageLookups, LanguagesByName: languagesByName, } } // ResetStatistics resets all statistics func (m *Manager) ResetStatistics() { m.mutex.Lock() defer m.mutex.Unlock() m.languageLookups = 0 m.playersWithLanguages = 0 m.languageUsageCount = make(map[int32]int64) } // ValidateAllLanguages validates all languages in the system func (m *Manager) ValidateAllLanguages() []string { allLanguages := m.masterLanguagesList.GetAllLanguages() var issues []string for _, lang := range allLanguages { if !lang.IsValid() { issues = append(issues, fmt.Sprintf("Language %d (%s) is invalid", lang.GetID(), lang.GetName())) } } return issues } // ReloadFromDatabase reloads all languages from database func (m *Manager) ReloadFromDatabase() error { if m.database == nil { return fmt.Errorf("no database available") } // Clear current languages m.masterLanguagesList.Clear() // Reload from database return m.Initialize() } // GetLanguageCount returns the total number of languages func (m *Manager) GetLanguageCount() int32 { return m.masterLanguagesList.Size() } // ProcessCommand handles language-related commands func (m *Manager) ProcessCommand(command string, args []string) (string, error) { switch command { case "stats": return m.handleStatsCommand(args) case "validate": return m.handleValidateCommand(args) case "list": return m.handleListCommand(args) case "info": return m.handleInfoCommand(args) case "reload": return m.handleReloadCommand(args) case "add": return m.handleAddCommand(args) case "remove": return m.handleRemoveCommand(args) case "search": return m.handleSearchCommand(args) default: return "", fmt.Errorf("unknown language command: %s", command) } } // handleStatsCommand shows language system statistics func (m *Manager) handleStatsCommand(args []string) (string, error) { stats := m.GetStatistics() result := "Language System Statistics:\n" result += fmt.Sprintf("Total Languages: %d\n", stats.TotalLanguages) result += fmt.Sprintf("Players with Languages: %d\n", stats.PlayersWithLanguages) result += fmt.Sprintf("Language Lookups: %d\n", stats.LanguageLookups) if len(stats.LanguageUsageCount) > 0 { result += "\nMost Used Languages:\n" // Show top 5 most used languages count := 0 for langID, usage := range stats.LanguageUsageCount { if count >= 5 { break } lang := m.GetLanguage(langID) name := "Unknown" if lang != nil { name = lang.GetName() } result += fmt.Sprintf(" %s (ID: %d): %d uses\n", name, langID, usage) count++ } } return result, nil } // handleValidateCommand validates all languages func (m *Manager) handleValidateCommand(args []string) (string, error) { issues := m.ValidateAllLanguages() if len(issues) == 0 { return "All languages are valid.", nil } result := fmt.Sprintf("Found %d issues with languages:\n", len(issues)) for i, issue := range issues { if i >= 10 { // Limit output result += "... (and more)\n" break } result += fmt.Sprintf("%d. %s\n", i+1, issue) } return result, nil } // handleListCommand lists languages func (m *Manager) handleListCommand(args []string) (string, error) { languages := m.masterLanguagesList.GetAllLanguages() if len(languages) == 0 { return "No languages loaded.", nil } result := fmt.Sprintf("Languages (%d):\n", len(languages)) count := 0 for _, language := range languages { if count >= 20 { // Limit output result += "... (and more)\n" break } result += fmt.Sprintf(" %d: %s\n", language.GetID(), language.GetName()) count++ } return result, nil } // handleInfoCommand shows information about a specific language func (m *Manager) handleInfoCommand(args []string) (string, error) { if len(args) == 0 { return "", fmt.Errorf("language ID or name required") } var language *Language // Try to parse as ID first var languageID int32 if _, err := fmt.Sscanf(args[0], "%d", &languageID); err == nil { language = m.GetLanguage(languageID) } else { // Try as name language = m.GetLanguageByName(args[0]) } if language == nil { return fmt.Sprintf("Language '%s' not found.", args[0]), nil } result := fmt.Sprintf("Language Information:\n") result += fmt.Sprintf("ID: %d\n", language.GetID()) result += fmt.Sprintf("Name: %s\n", language.GetName()) result += fmt.Sprintf("Save Needed: %v\n", language.GetSaveNeeded()) // Show usage statistics if available m.mutex.RLock() if usage, exists := m.languageUsageCount[language.GetID()]; exists { result += fmt.Sprintf("Usage Count: %d\n", usage) } else { result += "Usage Count: 0\n" } m.mutex.RUnlock() return result, nil } // handleReloadCommand reloads languages from database func (m *Manager) handleReloadCommand(args []string) (string, error) { if err := m.ReloadFromDatabase(); err != nil { return "", fmt.Errorf("failed to reload languages: %w", err) } count := m.GetLanguageCount() return fmt.Sprintf("Successfully reloaded %d languages from database.", count), nil } // handleAddCommand adds a new language func (m *Manager) handleAddCommand(args []string) (string, error) { if len(args) < 2 { return "", fmt.Errorf("usage: add ") } var id int32 if _, err := fmt.Sscanf(args[0], "%d", &id); err != nil { return "", fmt.Errorf("invalid language ID: %s", args[0]) } name := args[1] language := NewLanguage() language.SetID(id) language.SetName(name) language.SetSaveNeeded(true) if err := m.AddLanguage(language); err != nil { return "", fmt.Errorf("failed to add language: %w", err) } return fmt.Sprintf("Successfully added language %d: %s", id, name), nil } // handleRemoveCommand removes a language func (m *Manager) handleRemoveCommand(args []string) (string, error) { if len(args) == 0 { return "", fmt.Errorf("language ID required") } var id int32 if _, err := fmt.Sscanf(args[0], "%d", &id); err != nil { return "", fmt.Errorf("invalid language ID: %s", args[0]) } if err := m.RemoveLanguage(id); err != nil { return "", fmt.Errorf("failed to remove language: %w", err) } return fmt.Sprintf("Successfully removed language %d", id), nil } // handleSearchCommand searches for languages by name func (m *Manager) handleSearchCommand(args []string) (string, error) { if len(args) == 0 { return "", fmt.Errorf("search term required") } searchTerm := args[0] languages := m.masterLanguagesList.GetAllLanguages() var results []*Language // Search by name (case-insensitive partial match) for _, language := range languages { if contains(language.GetName(), searchTerm) { results = append(results, language) } } if len(results) == 0 { return fmt.Sprintf("No languages found matching '%s'.", searchTerm), nil } result := fmt.Sprintf("Found %d languages matching '%s':\n", len(results), searchTerm) for i, language := range results { if i >= 20 { // Limit output result += "... (and more)\n" break } result += fmt.Sprintf(" %d: %s\n", language.GetID(), language.GetName()) } return result, nil } // Shutdown gracefully shuts down the manager func (m *Manager) Shutdown() { if m.logger != nil { m.logger.LogInfo("Shutting down language manager...") } // Clear languages m.masterLanguagesList.Clear() } // contains checks if a string contains a substring (case-insensitive) func contains(str, substr string) bool { if len(substr) == 0 { return true } if len(str) < len(substr) { return false } // Convert to lowercase for case-insensitive comparison strLower := make([]byte, len(str)) substrLower := make([]byte, len(substr)) for i := 0; i < len(str); i++ { if str[i] >= 'A' && str[i] <= 'Z' { strLower[i] = str[i] + 32 } else { strLower[i] = str[i] } } for i := 0; i < len(substr); i++ { if substr[i] >= 'A' && substr[i] <= 'Z' { substrLower[i] = substr[i] + 32 } else { substrLower[i] = substr[i] } } for i := 0; i <= len(strLower)-len(substrLower); i++ { match := true for j := 0; j < len(substrLower); j++ { if strLower[i+j] != substrLower[j] { match = false break } } if match { return true } } return false }