// state.js import { LOCAL_STORAGE_KEY, DEFAULT_PLAYERS, GAME_STATES, DEFAULT_PLAYER_TIME_SECONDS } from '../config.js'; let players = []; let currentPlayerIndex = 0; let gameState = GAME_STATES.SETUP; // --- State Accessors --- export function getPlayers() { return [...players]; // Return a copy to prevent direct mutation } export function getCurrentPlayer() { if (players.length === 0) return null; return players[currentPlayerIndex]; } export function getPlayerById(id) { return players.find(p => p.id === id); } export function getCurrentPlayerIndex() { return currentPlayerIndex; } export function getGameState() { return gameState; } // --- State Mutators --- export function setPlayers(newPlayers) { players = newPlayers; saveData(); } export function setCurrentPlayerIndex(index) { if (index >= 0 && index < players.length) { currentPlayerIndex = index; saveData(); } else { console.error(`Invalid player index: ${index}`); } } export function setGameState(newState) { if (Object.values(GAME_STATES).includes(newState)) { gameState = newState; saveData(); } else { console.error(`Invalid game state: ${newState}`); } } export function updatePlayerTime(index, remainingTime) { if (index >= 0 && index < players.length) { players[index].remainingTime = Math.max(0, remainingTime); // Ensure time doesn't go below 0 saveData(); // Save data whenever time updates } } export function addPlayer(name, timeInMinutes, image = null) { const timeInSeconds = timeInMinutes * 60; const newId = Date.now(); players.push({ id: newId, name: name, timeInSeconds: timeInSeconds, remainingTime: timeInSeconds, image: image }); currentPlayerIndex = players.length - 1; // Focus new player saveData(); return players[players.length - 1]; // Return the newly added player } export function updatePlayer(index, name, timeInMinutes, remainingTimeSeconds, image) { if (index >= 0 && index < players.length) { const player = players[index]; const timeInSeconds = timeInMinutes * 60; player.name = name; player.timeInSeconds = timeInSeconds; // Update remaining time carefully based on game state if (gameState === GAME_STATES.SETUP) { player.remainingTime = timeInSeconds; } else if (gameState === GAME_STATES.PAUSED || gameState === GAME_STATES.OVER) { // Allow direct setting of remaining time only when paused or over player.remainingTime = remainingTimeSeconds; } // If running, remaining time is managed by the timer, don't override here unless intended if (image !== undefined) { // Allow updating image (null means remove image) player.image = image; } saveData(); return player; } return null; } export function deletePlayer(index) { if (players.length <= 2) { console.warn('Cannot delete player, minimum 2 players required.'); return false; // Indicate deletion failed } if (index >= 0 && index < players.length) { players.splice(index, 1); if (currentPlayerIndex >= players.length) { currentPlayerIndex = players.length - 1; } else if (currentPlayerIndex > index) { // Adjust index if deleting someone before the current player // No adjustment needed if deleting current or after current } saveData(); return true; // Indicate success } return false; // Indicate deletion failed } export function resetPlayersTime() { players.forEach(player => { player.remainingTime = player.timeInSeconds; }); saveData(); } export function resetToDefaults() { // Deep copy default players to avoid modifying the constant players = JSON.parse(JSON.stringify(DEFAULT_PLAYERS)); gameState = GAME_STATES.SETUP; currentPlayerIndex = 0; saveData(); } export function areAllTimersFinished() { return players.every(player => player.remainingTime <= 0); } // Returns the index of the next player with time > 0, or -1 if none export function findNextPlayerWithTime() { if (players.length === 0) return -1; const startIndex = (currentPlayerIndex + 1) % players.length; let index = startIndex; do { if (players[index].remainingTime > 0) { return index; } index = (index + 1) % players.length; } while (index !== startIndex); // Check current player last if no others found if(players[currentPlayerIndex].remainingTime > 0) { return currentPlayerIndex; } return -1; // No player has time left } // Find next player with time in specified direction (1 for next, -1 for prev) export function findNextPlayerWithTimeCircular(direction) { if (players.length === 0) return -1; let index = currentPlayerIndex; for (let i = 0; i < players.length; i++) { index = (index + direction + players.length) % players.length; if (players[index]?.remainingTime > 0) { // Check if player exists and has time return index; } } // If no other player found, check if current player has time (only relevant if direction search fails) if (players[currentPlayerIndex]?.remainingTime > 0) { return currentPlayerIndex; } return -1; // No player has time left } // --- Persistence --- export function saveData() { const dataToSave = { players, gameState, currentPlayerIndex }; try { localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(dataToSave)); } catch (error) { console.error("Error saving data to localStorage:", error); // Maybe notify the user that settings won't be saved } } export function loadData() { const savedData = localStorage.getItem(LOCAL_STORAGE_KEY); if (savedData) { try { const parsedData = JSON.parse(savedData); players = parsedData.players || JSON.parse(JSON.stringify(DEFAULT_PLAYERS)); gameState = parsedData.gameState || GAME_STATES.SETUP; currentPlayerIndex = parsedData.currentPlayerIndex || 0; // Basic validation/migration if needed if (currentPlayerIndex >= players.length) { currentPlayerIndex = 0; } // Ensure all players have necessary properties players = players.map(p => ({ id: p.id || Date.now() + Math.random(), // Ensure ID exists name: p.name || 'Player', timeInSeconds: p.timeInSeconds || DEFAULT_PLAYER_TIME_SECONDS, remainingTime: p.remainingTime !== undefined ? p.remainingTime : (p.timeInSeconds || DEFAULT_PLAYER_TIME_SECONDS), image: p.image || null })); } catch (error) { console.error("Error parsing data from localStorage:", error); resetToDefaults(); // Reset to defaults if stored data is corrupt } } else { resetToDefaults(); // Use defaults if no saved data } // No saveData() here, loadData just loads the state }