eq2go/internal/widget/manager.go

314 lines
7.5 KiB
Go

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
}