package widget import ( "sync" "time" ) // Manager manages widgets within a zone type Manager struct { widgets map[int32]*Widget // Widget ID -> Widget widgetsByDBID map[int32]*Widget // Database ID -> Widget widgetTimers map[*Widget]*time.Timer // Active timers for widgets timerCallbacks map[*Widget]func() // Timer callbacks mutex sync.RWMutex timerMutex sync.Mutex } // NewManager creates a new widget manager func NewManager() *Manager { return &Manager{ widgets: make(map[int32]*Widget), widgetsByDBID: make(map[int32]*Widget), widgetTimers: make(map[*Widget]*time.Timer), timerCallbacks: make(map[*Widget]func()), } } // AddWidget adds a widget to the manager func (m *Manager) AddWidget(widget *Widget) { m.mutex.Lock() defer m.mutex.Unlock() if widget.GetWidgetID() > 0 { m.widgets[widget.GetWidgetID()] = widget } if widget.GetDatabaseID() > 0 { m.widgetsByDBID[widget.GetDatabaseID()] = widget } } // RemoveWidget removes a widget from the manager func (m *Manager) RemoveWidget(widgetID int32) { m.mutex.Lock() defer m.mutex.Unlock() if widget, exists := m.widgets[widgetID]; exists { // Cancel any active timers m.cancelWidgetTimer(widget) // Remove from maps delete(m.widgets, widgetID) if widget.GetDatabaseID() > 0 { delete(m.widgetsByDBID, widget.GetDatabaseID()) } } } // GetWidget gets a widget by ID func (m *Manager) GetWidget(widgetID int32) *Widget { m.mutex.RLock() defer m.mutex.RUnlock() return m.widgets[widgetID] } // GetWidgetByDatabaseID gets a widget by database ID func (m *Manager) GetWidgetByDatabaseID(databaseID int32) *Widget { m.mutex.RLock() defer m.mutex.RUnlock() return m.widgetsByDBID[databaseID] } // GetAllWidgets returns all widgets func (m *Manager) GetAllWidgets() []*Widget { m.mutex.RLock() defer m.mutex.RUnlock() widgets := make([]*Widget, 0, len(m.widgets)) for _, widget := range m.widgets { widgets = append(widgets, widget) } return widgets } // GetWidgetsByType returns all widgets of a specific type func (m *Manager) GetWidgetsByType(widgetType int8) []*Widget { m.mutex.RLock() defer m.mutex.RUnlock() widgets := make([]*Widget, 0) for _, widget := range m.widgets { if widget.GetWidgetType() == widgetType { widgets = append(widgets, widget) } } return widgets } // AddWidgetTimer adds a timer for a widget func (m *Manager) AddWidgetTimer(widget *Widget, seconds float32, callback func()) { m.timerMutex.Lock() defer m.timerMutex.Unlock() // Cancel existing timer if any m.cancelWidgetTimerLocked(widget) // Create new timer duration := time.Duration(seconds * float32(time.Second)) timer := time.AfterFunc(duration, func() { m.handleWidgetTimer(widget) }) m.widgetTimers[widget] = timer if callback != nil { m.timerCallbacks[widget] = callback } else { // Default callback for auto-close m.timerCallbacks[widget] = func() { widget.HandleTimerUpdate() } } } // HasWidgetTimer checks if a widget has an active timer func (m *Manager) HasWidgetTimer(widget *Widget) bool { m.timerMutex.Lock() defer m.timerMutex.Unlock() _, exists := m.widgetTimers[widget] return exists } // CancelWidgetTimer cancels a widget's timer func (m *Manager) CancelWidgetTimer(widget *Widget) { m.timerMutex.Lock() defer m.timerMutex.Unlock() m.cancelWidgetTimerLocked(widget) } // cancelWidgetTimerLocked cancels a timer (must hold timerMutex) func (m *Manager) cancelWidgetTimerLocked(widget *Widget) { if timer, exists := m.widgetTimers[widget]; exists { timer.Stop() delete(m.widgetTimers, widget) delete(m.timerCallbacks, widget) } } // cancelWidgetTimer cancels a timer (must hold timerMutex) func (m *Manager) cancelWidgetTimer(widget *Widget) { m.timerMutex.Lock() defer m.timerMutex.Unlock() m.cancelWidgetTimerLocked(widget) } // handleWidgetTimer handles a widget timer expiration func (m *Manager) handleWidgetTimer(widget *Widget) { m.timerMutex.Lock() callback, exists := m.timerCallbacks[widget] if exists { delete(m.widgetTimers, widget) delete(m.timerCallbacks, widget) } m.timerMutex.Unlock() // Execute callback outside of lock if callback != nil { callback() } } // ProcessWidgetTimers processes all widget timers (called periodically) func (m *Manager) ProcessWidgetTimers() { // Timers are handled automatically by time.AfterFunc // This method is here for compatibility but doesn't need to do anything } // GetDoorWidgets returns all door widgets func (m *Manager) GetDoorWidgets() []*Widget { return m.GetWidgetsByType(WidgetTypeDoor) } // GetLiftWidgets returns all lift widgets func (m *Manager) GetLiftWidgets() []*Widget { return m.GetWidgetsByType(WidgetTypeLift) } // GetOpenWidgets returns all open widgets func (m *Manager) GetOpenWidgets() []*Widget { m.mutex.RLock() defer m.mutex.RUnlock() widgets := make([]*Widget, 0) for _, widget := range m.widgets { if widget.IsOpen() { widgets = append(widgets, widget) } } return widgets } // GetWidgetsByHouseID returns all widgets for a house func (m *Manager) GetWidgetsByHouseID(houseID int32) []*Widget { m.mutex.RLock() defer m.mutex.RUnlock() widgets := make([]*Widget, 0) for _, widget := range m.widgets { if widget.GetHouseID() == houseID { widgets = append(widgets, widget) } } return widgets } // GetLinkedWidgets returns all widgets linked to the given widget func (m *Manager) GetLinkedWidgets(widget *Widget) []*Widget { m.mutex.RLock() defer m.mutex.RUnlock() linked := make([]*Widget, 0) // Add linked spawn if widget.GetLinkedSpawnID() > 0 { if linkedWidget := m.widgetsByDBID[widget.GetLinkedSpawnID()]; linkedWidget != nil { linked = append(linked, linkedWidget) } } // Add action spawn if widget.GetActionSpawnID() > 0 { if actionWidget := m.widgetsByDBID[widget.GetActionSpawnID()]; actionWidget != nil { linked = append(linked, actionWidget) } } // Find widgets that link to this one for _, w := range m.widgets { if w.GetLinkedSpawnID() == widget.GetDatabaseID() || w.GetActionSpawnID() == widget.GetDatabaseID() { linked = append(linked, w) } } return linked } // ResolveLinkedSpawns resolves all linked spawn references func (m *Manager) ResolveLinkedSpawns() { m.mutex.RLock() defer m.mutex.RUnlock() for _, widget := range m.widgets { // Resolve linked spawn if widget.GetLinkedSpawnID() > 0 && widget.GetLinkedSpawn() == nil { if linked := m.widgetsByDBID[widget.GetLinkedSpawnID()]; linked != nil { widget.SetLinkedSpawn(linked) } } // Resolve action spawn if widget.GetActionSpawnID() > 0 && widget.GetActionSpawn() == nil { if action := m.widgetsByDBID[widget.GetActionSpawnID()]; action != nil { widget.SetActionSpawn(action) } } } } // Clear removes all widgets and cancels all timers func (m *Manager) Clear() { m.mutex.Lock() defer m.mutex.Unlock() // Cancel all timers m.timerMutex.Lock() for widget, timer := range m.widgetTimers { timer.Stop() delete(m.widgetTimers, widget) delete(m.timerCallbacks, widget) } m.timerMutex.Unlock() // Clear maps m.widgets = make(map[int32]*Widget) m.widgetsByDBID = make(map[int32]*Widget) } // GetStatistics returns widget statistics func (m *Manager) GetStatistics() map[string]any { m.mutex.RLock() defer m.mutex.RUnlock() stats := make(map[string]any) stats["total_widgets"] = len(m.widgets) stats["door_count"] = len(m.GetDoorWidgets()) stats["lift_count"] = len(m.GetLiftWidgets()) stats["open_count"] = len(m.GetOpenWidgets()) m.timerMutex.Lock() stats["active_timers"] = len(m.widgetTimers) m.timerMutex.Unlock() return stats }