#include "StateManager.h" #include "../../graphics/renderers/RenderManager.h" #include StateManager::StateManager(AppState initial) : m_currentState(initial) , m_previousState(initial) { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "StateManager initialized with state: %s", getStateName(initial)); } void StateManager::registerEventHandler(AppState state, EventHandler handler) { if (!isValidState(state)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid state for event handler registration"); return; } m_eventHandlers[stateToInt(state)].push_back(std::move(handler)); SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Event handler registered for state: %s", getStateName(state)); } void StateManager::registerUpdateHandler(AppState state, UpdateHandler handler) { if (!isValidState(state)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid state for update handler registration"); return; } m_updateHandlers[stateToInt(state)].push_back(std::move(handler)); SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Update handler registered for state: %s", getStateName(state)); } void StateManager::registerRenderHandler(AppState state, RenderHandler handler) { if (!isValidState(state)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid state for render handler registration"); return; } m_renderHandlers[stateToInt(state)].push_back(std::move(handler)); SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Render handler registered for state: %s", getStateName(state)); } void StateManager::registerOnEnter(AppState state, Hook hook) { if (!isValidState(state)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid state for enter hook registration"); return; } m_onEnterHooks[stateToInt(state)].push_back(std::move(hook)); SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Enter hook registered for state: %s", getStateName(state)); } void StateManager::registerOnExit(AppState state, Hook hook) { if (!isValidState(state)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid state for exit hook registration"); return; } m_onExitHooks[stateToInt(state)].push_back(std::move(hook)); SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Exit hook registered for state: %s", getStateName(state)); } // Legacy compatibility: overload accepting a no-arg function as handler void StateManager::registerHandler(AppState s, std::function h) { EventHandler wrapper = [h = std::move(h)](const SDL_Event&) { h(); }; registerEventHandler(s, std::move(wrapper)); } bool StateManager::setState(AppState newState) { if (!isValidState(newState)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Attempted to set invalid state"); return false; } if (newState == m_currentState) { SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Attempted to set same state: %s", getStateName(newState)); return true; } if (!canTransitionTo(newState)) { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Invalid state transition from %s to %s", getStateName(m_currentState), getStateName(newState)); return false; } SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "State transition: %s -> %s", getStateName(m_currentState), getStateName(newState)); // Persistent trace for debugging abrupt exits { FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "setState start %s -> %s\n", getStateName(m_currentState), getStateName(newState)); fclose(f); } } // Execute exit hooks for current state executeExitHooks(m_currentState); // Update state m_previousState = m_currentState; m_currentState = newState; // Execute enter hooks for new state executeEnterHooks(m_currentState); // Trace completion { FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "setState end %s\n", getStateName(m_currentState)); fclose(f); } } return true; } void StateManager::handleEvent(const SDL_Event& event) { auto it = m_eventHandlers.find(stateToInt(m_currentState)); if (it == m_eventHandlers.end()) { return; } for (auto& handler : it->second) { try { handler(event); } catch (const std::exception& e) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Exception in event handler for state %s: %s", getStateName(m_currentState), e.what()); } } } void StateManager::update(float deltaTime) { auto it = m_updateHandlers.find(stateToInt(m_currentState)); if (it == m_updateHandlers.end()) { return; } for (auto& handler : it->second) { try { handler(deltaTime); } catch (const std::exception& e) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Exception in update handler for state %s: %s", getStateName(m_currentState), e.what()); } } } void StateManager::render(RenderManager& renderer) { auto it = m_renderHandlers.find(stateToInt(m_currentState)); if (it == m_renderHandlers.end()) { return; } for (auto& handler : it->second) { try { handler(renderer); } catch (const std::exception& e) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Exception in render handler for state %s: %s", getStateName(m_currentState), e.what()); } } } bool StateManager::isValidState(AppState state) const { switch (state) { case AppState::Loading: case AppState::Video: case AppState::Menu: case AppState::Options: case AppState::LevelSelector: case AppState::Playing: case AppState::LevelSelect: case AppState::GameOver: return true; default: return false; } } bool StateManager::canTransitionTo(AppState newState) const { // For now, allow all transitions. Can be enhanced later with state machine validation return isValidState(newState); } const char* StateManager::getStateName(AppState state) const { switch (state) { case AppState::Loading: return "Loading"; case AppState::Video: return "Video"; case AppState::Menu: return "Menu"; case AppState::Options: return "Options"; case AppState::LevelSelector: return "LevelSelector"; case AppState::Playing: return "Playing"; case AppState::LevelSelect: return "LevelSelect"; case AppState::GameOver: return "GameOver"; default: return "Unknown"; } } void StateManager::executeEnterHooks(AppState state) { auto it = m_onEnterHooks.find(stateToInt(state)); if (it == m_onEnterHooks.end()) { return; } int idx = 0; for (auto& hook : it->second) { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Executing enter hook %d for state %s", idx, getStateName(state)); // Also write to trace file for persistent record { FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "executeEnterHook %d %s\n", idx, getStateName(state)); fclose(f); } } try { hook(); } catch (const std::exception& e) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Exception in enter hook for state %s: %s", getStateName(state), e.what()); } ++idx; } } void StateManager::executeExitHooks(AppState state) { auto it = m_onExitHooks.find(stateToInt(state)); if (it == m_onExitHooks.end()) { return; } int idx = 0; for (auto& hook : it->second) { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Executing exit hook %d for state %s", idx, getStateName(state)); { FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "executeExitHook %d %s\n", idx, getStateName(state)); fclose(f); } } try { hook(); } catch (const std::exception& e) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Exception in exit hook for state %s: %s", getStateName(state), e.what()); } ++idx; } }