package appcontext import ( "fmt" "log/slog" "github.com/AFASystems/presence/internal/pkg/model" "github.com/mitchellh/mapstructure" ) // AppState provides centralized access to application state type AppState struct { beacons model.BeaconsList settings model.Settings beaconEvents model.BeaconEventList beaconsLookup model.BeaconsLookup } // NewAppState creates a new application context AppState with default values func NewAppState() *AppState { return &AppState{ beacons: model.BeaconsList{ Beacons: make(map[string]model.Beacon), }, settings: model.Settings{ ID: 1, CurrentAlgorithm: "filter", // possible values filter or AI LocationConfidence: 4, LastSeenThreshold: 15, BeaconMetricSize: 30, HASendInterval: 5, HASendChangesOnly: false, RSSIEnforceThreshold: false, RSSIMinThreshold: 100, }, beaconEvents: model.BeaconEventList{ Beacons: make(map[string]model.BeaconEvent), }, beaconsLookup: model.BeaconsLookup{ Lookup: make(map[string]string), }, } } // GetBeacons returns thread-safe access to beacons list func (m *AppState) GetBeacons() *model.BeaconsList { m.beacons.Lock.RLock() defer m.beacons.Lock.RUnlock() return &m.beacons } // GetSettings returns thread-safe access to settings func (m *AppState) GetSettings() *model.Settings { return &m.settings } // GetBeaconEvents returns thread-safe access to beacon events func (m *AppState) GetBeaconEvents() *model.BeaconEventList { m.beaconEvents.Lock.RLock() defer m.beaconEvents.Lock.RUnlock() return &m.beaconEvents } // AddBeaconToLookup adds a beacon ID to the lookup map func (m *AppState) AddBeaconToLookup(id, value string) { m.beaconsLookup.Lock.Lock() m.beaconsLookup.Lookup[id] = value m.beaconsLookup.Lock.Unlock() } // RemoveBeaconFromLookup removes a beacon ID from the lookup map func (m *AppState) RemoveBeaconFromLookup(id string) { m.beaconsLookup.Lock.Lock() delete(m.beaconsLookup.Lookup, id) m.beaconsLookup.Lock.Unlock() } func (m *AppState) CleanLookup() { m.beaconsLookup.Lock.Lock() clear(m.beaconsLookup.Lookup) m.beaconsLookup.Lock.Unlock() } // BeaconExists checks if a beacon exists in the lookup func (m *AppState) BeaconExists(id string) (string, bool) { m.beaconsLookup.Lock.RLock() defer m.beaconsLookup.Lock.RUnlock() val, exists := m.beaconsLookup.Lookup[id] return val, exists } // GetBeacon returns a beacon by ID (thread-safe) func (m *AppState) GetBeacon(id string) (model.Beacon, bool) { m.beacons.Lock.RLock() defer m.beacons.Lock.RUnlock() beacon, exists := m.beacons.Beacons[id] return beacon, exists } // UpdateBeacon updates a beacon in the list (thread-safe) func (m *AppState) UpdateBeacon(id string, beacon model.Beacon) { m.beacons.Lock.Lock() defer m.beacons.Lock.Unlock() m.beacons.Beacons[id] = beacon } // GetBeaconEvent returns a beacon event by ID (thread-safe) func (m *AppState) GetBeaconEvent(id string) (model.BeaconEvent, bool) { m.beaconEvents.Lock.RLock() defer m.beaconEvents.Lock.RUnlock() event, exists := m.beaconEvents.Beacons[id] return event, exists } // UpdateBeaconEvent updates a beacon event in the list (thread-safe) func (m *AppState) UpdateBeaconEvent(id string, event model.BeaconEvent) { m.beaconEvents.Lock.Lock() defer m.beaconEvents.Lock.Unlock() m.beaconEvents.Beacons[id] = event } // GetAllBeacons returns a copy of all beacons func (m *AppState) GetAllBeacons() map[string]model.Beacon { m.beacons.Lock.RLock() defer m.beacons.Lock.RUnlock() beacons := make(map[string]model.Beacon) for id, beacon := range m.beacons.Beacons { beacons[id] = beacon } return beacons } // GetBeaconCount returns the number of tracked beacons func (m *AppState) GetBeaconCount() int { m.beacons.Lock.RLock() defer m.beacons.Lock.RUnlock() return len(m.beacons.Beacons) } // GetSettingsValue returns current settings as a value func (m *AppState) GetSettingsValue() model.Settings { return m.settings } // UpdateSettings updates the system settings (thread-safe) func (m *AppState) UpdateSettings(settings map[string]any) { if err := mapstructure.Decode(settings, &m.settings); err != nil { msg := fmt.Sprintf("Error in persisting settings: %v", err) slog.Error(msg) } }