"use strict";
/**
 * Core Game Engine
 * Manages game state and processes actions
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.TextAdventureEngine = void 0;
const fs = __importStar(require("fs/promises"));
const yaml_parser_1 = require("../world-model/yaml-parser");
class TextAdventureEngine {
    constructor() {
        this.worldModel = null;
        this.gameState = null;
        this.actionHandlers = {};
        this.registerDefaultActionHandlers();
    }
    /**
     * Load a world model from a file
     */
    async loadWorld(worldModelPath) {
        try {
            this.worldModel = await yaml_parser_1.YamlWorldParser.loadFromFile(worldModelPath);
            this.gameState = { ...this.worldModel.initialState };
            // Mark the initial room as visited
            if (!this.gameState.visitedRooms.includes(this.gameState.currentRoomId)) {
                this.gameState.visitedRooms.push(this.gameState.currentRoomId);
            }
        }
        catch (error) {
            console.error(`Failed to load world from ${worldModelPath}:`, error);
            throw new Error(`Could not load world: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    /**
     * Get the current game state
     */
    getCurrentState() {
        if (!this.gameState) {
            throw new Error('Game state not initialized. Please load a world first.');
        }
        return { ...this.gameState };
    }
    /**
     * Get the world model
     */
    getWorldModel() {
        if (!this.worldModel) {
            throw new Error('World model not initialized. Please load a world first.');
        }
        return this.worldModel;
    }
    /**
     * Process an action from the player
     */
    processAction(action) {
        if (!this.worldModel || !this.gameState) {
            return {
                success: false,
                message: 'Game not initialized',
                stateChanged: false
            };
        }
        const handler = this.actionHandlers[action.action.toLowerCase()];
        if (!handler) {
            return {
                success: false,
                message: `I don't know how to "${action.action}"`,
                stateChanged: false
            };
        }
        return handler(this.gameState, this.worldModel, action);
    }
    /**
     * Save the current game state to a file
     */
    async saveGame(filename) {
        if (!this.gameState || !this.worldModel) {
            throw new Error('Cannot save: game not initialized');
        }
        const saveData = {
            worldModelName: this.worldModel.title,
            worldModelVersion: this.worldModel.version,
            timestamp: new Date().toISOString(),
            gameState: this.gameState
        };
        try {
            await fs.writeFile(filename, JSON.stringify(saveData, null, 2), 'utf8');
        }
        catch (error) {
            console.error(`Failed to save game to ${filename}:`, error);
            throw new Error(`Could not save game: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    /**
     * Load a game state from a save file
     */
    async loadGame(filename) {
        try {
            const fileContents = await fs.readFile(filename, 'utf8');
            const saveData = JSON.parse(fileContents);
            // Check if the save file matches the current world model
            if (!this.worldModel) {
                throw new Error('World model not loaded');
            }
            if (saveData.worldModelName !== this.worldModel.title ||
                saveData.worldModelVersion !== this.worldModel.version) {
                throw new Error('Save file is for a different world or version');
            }
            // Load the game state
            this.gameState = saveData.gameState;
        }
        catch (error) {
            console.error(`Failed to load game from ${filename}:`, error);
            throw new Error(`Could not load save file: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    /**
     * Get a list of available actions in the current context
     */
    getAvailableActions() {
        if (!this.worldModel)
            return [];
        // Common actions always available
        const availableActions = ['look', 'inventory', 'help'];
        // Add movement actions based on current room exits
        const currentRoom = this.getCurrentRoom();
        if (currentRoom) {
            currentRoom.exits.forEach(exit => {
                availableActions.push(`go ${exit.direction.toLowerCase()}`);
            });
        }
        // Add object interactions based on visible objects
        const visibleObjects = this.getVisibleObjects();
        const objects = this.worldModel.objects;
        visibleObjects.forEach(objId => {
            const obj = objects[objId];
            if (obj) {
                obj.allowedActions.forEach(action => {
                    availableActions.push(`${action} ${obj.name.toLowerCase()}`);
                });
            }
        });
        // Add character interactions
        const visibleCharacters = this.getVisibleCharacters();
        visibleCharacters.forEach(charId => {
            availableActions.push(`talk to ${this.worldModel.characters[charId].name.toLowerCase()}`);
        });
        // Add inventory object actions
        this.gameState.inventory.forEach(objId => {
            const obj = objects[objId];
            if (obj) {
                obj.allowedActions.forEach(action => {
                    availableActions.push(`${action} ${obj.name.toLowerCase()}`);
                });
            }
        });
        return Array.from(new Set(availableActions)); // Remove duplicates
    }
    /**
     * Get a list of visible objects in the current room
     */
    getVisibleObjects() {
        if (!this.worldModel || !this.gameState)
            return [];
        const currentRoom = this.getCurrentRoom();
        if (!currentRoom)
            return [];
        const visibleObjects = [...currentRoom.objects];
        // Add objects from open containers
        currentRoom.objects.forEach(objId => {
            const obj = this.worldModel.objects[objId];
            if (obj && obj.traits.includes('container') && obj.states?.open && obj.containedObjects) {
                visibleObjects.push(...obj.containedObjects);
            }
        });
        return visibleObjects;
    }
    /**
     * Get a list of visible characters in the current room
     */
    getVisibleCharacters() {
        if (!this.worldModel || !this.gameState)
            return [];
        const currentRoom = this.getCurrentRoom();
        return currentRoom ? currentRoom.characters : [];
    }
    /**
     * Get the description of the current room
     */
    getCurrentRoomDescription() {
        const currentRoom = this.getCurrentRoom();
        if (!currentRoom)
            return 'You are in a void. Something has gone wrong.';
        return currentRoom.description;
    }
    /**
     * Start the game and return the introduction text
     */
    async start() {
        if (!this.worldModel) {
            throw new Error('World not loaded. Please load a world before starting.');
        }
        // Reset game state to initial state
        this.gameState = { ...this.worldModel.initialState };
        return this.worldModel.introduction;
    }
    /**
     * End the game (cleanup resources if needed)
     */
    end() {
        // Cleanup could happen here if needed
        console.log('Game ended');
    }
    /**
     * Get the current room object
     */
    getCurrentRoom() {
        if (!this.worldModel || !this.gameState)
            return null;
        const roomId = this.gameState.currentRoomId;
        return this.worldModel.rooms[roomId] || null;
    }
    /**
     * Register default action handlers
     */
    registerDefaultActionHandlers() {
        // Look action
        this.actionHandlers['look'] = (state, world, action) => {
            const room = world.rooms[state.currentRoomId];
            // If an object is specified, look at that object
            if (action.object) {
                // Try to find the object in the room or inventory
                const visibleObjects = this.getVisibleObjects();
                const objId = this.findObjectByName(action.object, [...visibleObjects, ...state.inventory]);
                if (!objId) {
                    return {
                        success: false,
                        message: `You don't see any ${action.object} here.`,
                        stateChanged: false
                    };
                }
                const obj = world.objects[objId];
                return {
                    success: true,
                    message: obj.description,
                    stateChanged: false
                };
            }
            // Look at the room
            const objectDescriptions = room.objects
                .map(id => world.objects[id])
                .map(obj => `You can see ${obj.name.toLowerCase()} here.`);
            const characterDescriptions = room.characters
                .map(id => world.characters[id])
                .map(char => `${char.name} is here.`);
            const exitDescriptions = room.exits
                .map(exit => `There is an exit ${exit.direction.toLowerCase()}${exit.description ? ` (${exit.description})` : ''}.`);
            const fullDescription = [
                room.description,
                ...objectDescriptions,
                ...characterDescriptions,
                ...exitDescriptions
            ].join('\n');
            return {
                success: true,
                message: fullDescription,
                stateChanged: false
            };
        };
        // Go action
        this.actionHandlers['go'] = (state, world, action) => {
            const room = world.rooms[state.currentRoomId];
            if (!action.object) {
                return {
                    success: false,
                    message: 'Go where?',
                    stateChanged: false
                };
            }
            // Find the exit that matches the direction
            const direction = action.object.toLowerCase();
            const exit = room.exits.find(e => e.direction.toLowerCase() === direction);
            if (!exit) {
                return {
                    success: false,
                    message: `You can't go ${direction} from here.`,
                    stateChanged: false
                };
            }
            if (exit.isLocked) {
                if (!exit.keyId) {
                    return {
                        success: false,
                        message: `The way ${direction} is locked.`,
                        stateChanged: false
                    };
                }
                if (!state.inventory.includes(exit.keyId)) {
                    return {
                        success: false,
                        message: `The way ${direction} is locked and you don't have the key.`,
                        stateChanged: false
                    };
                }
                // Player has the key, unlock the exit
                exit.isLocked = false;
                return {
                    success: true,
                    message: `You unlock the way ${direction} and proceed.`,
                    stateChanged: true,
                    newState: {
                        ...state,
                        currentRoomId: exit.targetRoomId,
                        visitedRooms: state.visitedRooms.includes(exit.targetRoomId)
                            ? state.visitedRooms
                            : [...state.visitedRooms, exit.targetRoomId]
                    }
                };
            }
            // Exit is not locked, just move
            return {
                success: true,
                message: `You go ${direction}.`,
                stateChanged: true,
                newState: {
                    ...state,
                    currentRoomId: exit.targetRoomId,
                    visitedRooms: state.visitedRooms.includes(exit.targetRoomId)
                        ? state.visitedRooms
                        : [...state.visitedRooms, exit.targetRoomId]
                }
            };
        };
        // Take action
        this.actionHandlers['take'] = (state, world, action) => {
            if (!action.object) {
                return {
                    success: false,
                    message: 'Take what?',
                    stateChanged: false
                };
            }
            // Find the object in the current room
            const visibleObjects = this.getVisibleObjects();
            const objId = this.findObjectByName(action.object, visibleObjects);
            if (!objId) {
                return {
                    success: false,
                    message: `You don't see any ${action.object} here.`,
                    stateChanged: false
                };
            }
            const obj = world.objects[objId];
            // Check if the object can be taken
            if (!obj.traits.includes('takeable')) {
                return {
                    success: false,
                    message: `You can't take the ${obj.name.toLowerCase()}.`,
                    stateChanged: false
                };
            }
            // Remove object from room and add to inventory
            const room = world.rooms[state.currentRoomId];
            const newRoomObjects = room.objects.filter(id => id !== objId);
            room.objects = newRoomObjects;
            // Update state
            return {
                success: true,
                message: `You take the ${obj.name.toLowerCase()}.`,
                stateChanged: true,
                newState: {
                    ...state,
                    inventory: [...state.inventory, objId]
                }
            };
        };
        // Inventory action
        this.actionHandlers['inventory'] = (state, world) => {
            if (state.inventory.length === 0) {
                return {
                    success: true,
                    message: 'Your inventory is empty.',
                    stateChanged: false
                };
            }
            const items = state.inventory
                .map(id => world.objects[id])
                .map(obj => obj.name)
                .join(', ');
            return {
                success: true,
                message: `You are carrying: ${items}.`,
                stateChanged: false
            };
        };
        // Drop action
        this.actionHandlers['drop'] = (state, world, action) => {
            if (!action.object) {
                return {
                    success: false,
                    message: 'Drop what?',
                    stateChanged: false
                };
            }
            // Find the object in the inventory
            const objId = this.findObjectByName(action.object, state.inventory);
            if (!objId) {
                return {
                    success: false,
                    message: `You don't have any ${action.object}.`,
                    stateChanged: false
                };
            }
            const obj = world.objects[objId];
            // Remove object from inventory and add to room
            const room = world.rooms[state.currentRoomId];
            room.objects.push(objId);
            // Update state
            return {
                success: true,
                message: `You drop the ${obj.name.toLowerCase()}.`,
                stateChanged: true,
                newState: {
                    ...state,
                    inventory: state.inventory.filter(id => id !== objId)
                }
            };
        };
        // Use action
        this.actionHandlers['use'] = (state, world, action) => {
            if (!action.object) {
                return {
                    success: false,
                    message: 'Use what?',
                    stateChanged: false
                };
            }
            // Find the object in inventory or visible objects
            const visibleObjects = this.getVisibleObjects();
            const objId = this.findObjectByName(action.object, [...state.inventory, ...visibleObjects]);
            if (!objId) {
                return {
                    success: false,
                    message: `You don't see any ${action.object} here.`,
                    stateChanged: false
                };
            }
            const obj = world.objects[objId];
            // Check if the object can be used
            if (!obj.allowedActions.includes('use')) {
                return {
                    success: false,
                    message: `You can't use the ${obj.name.toLowerCase()}.`,
                    stateChanged: false
                };
            }
            // Check if there's a target
            if (action.target) {
                const targetId = this.findObjectByName(action.target, [...state.inventory, ...visibleObjects]);
                if (!targetId) {
                    return {
                        success: false,
                        message: `You don't see any ${action.target} here.`,
                        stateChanged: false
                    };
                }
                const target = world.objects[targetId];
                // TODO: Implement object-specific use logic (could be extended with a more sophisticated system)
                return {
                    success: true,
                    message: `You use the ${obj.name.toLowerCase()} on the ${target.name.toLowerCase()}.`,
                    stateChanged: false
                };
            }
            // Simple use without target
            return {
                success: true,
                message: `You use the ${obj.name.toLowerCase()}.`,
                stateChanged: false
            };
        };
        // Talk action
        this.actionHandlers['talk'] = (state, world, action) => {
            if (!action.object) {
                return {
                    success: false,
                    message: 'Talk to whom?',
                    stateChanged: false
                };
            }
            // Find the character in the room
            const visibleCharacters = this.getVisibleCharacters();
            const charId = this.findCharacterByName(action.object, visibleCharacters);
            if (!charId) {
                return {
                    success: false,
                    message: `You don't see anyone called ${action.object} here.`,
                    stateChanged: false
                };
            }
            const character = world.characters[charId];
            // If a topic is provided
            if (action.parameters?.topic) {
                const topic = action.parameters.topic.toLowerCase();
                const response = character.dialogue[topic] || character.defaultResponse;
                return {
                    success: true,
                    message: `${character.name}: "${response}"`,
                    stateChanged: false
                };
            }
            // No specific topic
            return {
                success: true,
                message: `${character.name} looks ready to talk. You could ask about: ${Object.keys(character.dialogue).join(', ')}.`,
                stateChanged: false
            };
        };
        // Help action
        this.actionHandlers['help'] = () => {
            return {
                success: true,
                message: [
                    'Available commands:',
                    '- look: Examine your surroundings or a specific object',
                    '- go [direction]: Move in a direction',
                    '- take [object]: Pick up an object',
                    '- drop [object]: Put down an object',
                    '- inventory: Check what you\'re carrying',
                    '- use [object] (on [target]): Use an object, optionally on another object',
                    '- talk to [character] (about [topic]): Speak with a character',
                    '- help: Show this help text',
                    '',
                    'You can type commands in natural language. The AI will interpret your intent.'
                ].join('\n'),
                stateChanged: false
            };
        };
        // Examine action (alias for look)
        this.actionHandlers['examine'] = this.actionHandlers['look'];
    }
    /**
     * Find an object by name in a list of object IDs
     */
    findObjectByName(name, objectIds) {
        if (!this.worldModel)
            return null;
        const normalizedName = name.toLowerCase();
        for (const id of objectIds) {
            const obj = this.worldModel.objects[id];
            if (obj && obj.name.toLowerCase() === normalizedName) {
                return id;
            }
        }
        return null;
    }
    /**
     * Find a character by name in a list of character IDs
     */
    findCharacterByName(name, characterIds) {
        if (!this.worldModel)
            return null;
        const normalizedName = name.toLowerCase();
        for (const id of characterIds) {
            const character = this.worldModel.characters[id];
            if (character && character.name.toLowerCase() === normalizedName) {
                return id;
            }
        }
        return null;
    }
}
exports.TextAdventureEngine = TextAdventureEngine;
//# sourceMappingURL=game-engine.js.map