"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.InkEngine = void 0;
exports.compileInkSource = compileInkSource;
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
const inkjs_1 = require("inkjs");
const tag_parser_1 = require("../utils/tag-parser");
const { Compiler } = require('inkjs/full');
function compileInkSource(sourcePath, outputPath) {
    const resolvedSource = path_1.default.resolve(sourcePath);
    const resolvedOutput = path_1.default.resolve(outputPath);
    if (!(0, fs_1.existsSync)(resolvedSource)) {
        throw new Error(`Ink source file not found: ${resolvedSource}`);
    }
    const warnings = [];
    const errors = [];
    const source = (0, fs_1.readFileSync)(resolvedSource, 'utf8').replace(/^\uFEFF/, '');
    const sourceDir = path_1.default.dirname(resolvedSource);
    const fileHandler = {
        ResolveInkFilename: (filename) => path_1.default.isAbsolute(filename) ? filename : path_1.default.resolve(sourceDir, filename),
        LoadInkFileContents: (filename) => (0, fs_1.readFileSync)(path_1.default.isAbsolute(filename) ? filename : path_1.default.resolve(sourceDir, filename), 'utf8')
            .replace(/^\uFEFF/, ''),
    };
    const compiler = new Compiler(source, {
        sourceFilename: resolvedSource,
        fileHandler,
        errorHandler: (message, type) => {
            if (type === 1) {
                warnings.push(message);
            }
            else {
                errors.push(message);
            }
        },
    });
    const story = compiler.Compile();
    if (!story || errors.length > 0) {
        throw new Error(`Ink compilation failed:\n${errors.join('\n')}`);
    }
    if (warnings.length > 0) {
        warnings.forEach((warning) => console.warn(`[ink] ${warning}`));
    }
    (0, fs_1.mkdirSync)(path_1.default.dirname(resolvedOutput), { recursive: true });
    (0, fs_1.writeFileSync)(resolvedOutput, story.ToJson(), 'utf8');
    return {
        sourcePath: resolvedSource,
        outputPath: resolvedOutput,
        warningCount: warnings.length,
    };
}
class InkEngine {
    constructor(storyPath) {
        this.storyPath = storyPath;
        this.story = null;
        this.nextTurnId = 1;
    }
    isRunning() {
        if (!this.story)
            return false;
        return this.story.canContinue || this.story.currentChoices.length > 0;
    }
    newGame() {
        this.story = this.loadStory();
        this.nextTurnId = 1;
        return this.continueStory();
    }
    chooseChoice(choiceIndex) {
        if (!this.story) {
            throw new Error('No active Ink story');
        }
        const choice = this.story.currentChoices.find((item) => item.index === choiceIndex);
        if (!choice) {
            throw new Error(`Ink choice ${choiceIndex} is not available`);
        }
        this.story.ChooseChoiceIndex(choice.index);
        return this.continueStory();
    }
    saveGame() {
        if (!this.story) {
            throw new Error('No active Ink story to save');
        }
        return JSON.stringify({
            inkState: this.story.state.toJson(),
            nextTurnId: this.nextTurnId,
        });
    }
    loadGame(savedState) {
        this.story = this.loadStory();
        let inkState = savedState;
        try {
            const parsed = JSON.parse(savedState);
            if (parsed && typeof parsed.inkState === 'string') {
                inkState = parsed.inkState;
                if (Number.isInteger(parsed.nextTurnId)) {
                    this.nextTurnId = Math.max(1, parsed.nextTurnId);
                }
            }
        }
        catch {
            // Backward compatibility with raw Ink state JSON.
        }
        this.story.state.LoadJson(inkState);
        return this.continueStory();
    }
    loadStory() {
        const resolvedPath = path_1.default.resolve(this.storyPath);
        if (!(0, fs_1.existsSync)(resolvedPath)) {
            throw new Error(`Ink story file not found: ${resolvedPath}`);
        }
        const storyJson = JSON.parse((0, fs_1.readFileSync)(resolvedPath, 'utf8'));
        return new inkjs_1.Story(storyJson);
    }
    continueStory() {
        if (!this.story) {
            throw new Error('No active Ink story');
        }
        const paragraphs = [];
        const globalTags = [];
        const turnTags = [];
        while (this.story.canContinue) {
            const rawText = this.story.Continue();
            const text = String(rawText || '').trim();
            const tags = (0, tag_parser_1.parseTags)(this.story.currentTags || []);
            turnTags.push(...tags);
            tags
                .filter((tag) => tag.key === 'title' || tag.key === 'author')
                .forEach((tag) => globalTags.push(tag));
            if (text) {
                paragraphs.push({ text, tags });
            }
            else {
                tags.forEach((tag) => globalTags.push(tag));
            }
        }
        const choices = this.story.currentChoices.map((choice) => {
            const tags = (0, tag_parser_1.parseTags)(choice.tags || []);
            const category = (0, tag_parser_1.getTagValue)(tags, 'action');
            const letter = (0, tag_parser_1.getTagValue)(tags, 'letter') || (0, tag_parser_1.getTagValue)(tags, 'key');
            return {
                index: choice.index,
                text: String(choice.text || '').trim(),
                tags,
                category,
                letter,
            };
        });
        const inputMode = choices.length > 0 ? 'choice' : 'end';
        const gameState = {};
        if (inputMode === 'end') {
            const errorTag = turnTags.find((tag) => tag.key === 'error');
            const scoreTag = turnTags.find((tag) => tag.key === 'score');
            if (!errorTag && !scoreTag) {
                const message = 'Ink story ended without an explicit #score ending tag.';
                const generatedErrorTag = { key: 'error', value: message };
                globalTags.push(generatedErrorTag);
                turnTags.push(generatedErrorTag);
            }
            const finalErrorTag = turnTags.find((tag) => tag.key === 'error');
            const finalScoreTag = turnTags.find((tag) => tag.key === 'score');
            if (finalErrorTag) {
                gameState.endState = {
                    type: 'error',
                    message: finalErrorTag.value || finalErrorTag.param,
                };
            }
            else if (finalScoreTag) {
                const numericScore = Number(finalScoreTag?.value);
                if (Number.isFinite(numericScore)) {
                    gameState.score = numericScore;
                }
                gameState.endState = {
                    type: 'intended',
                    message: finalScoreTag.value || finalScoreTag.param,
                };
            }
        }
        return {
            turnId: this.nextTurnId++,
            paragraphs,
            choices,
            inputMode,
            globalTags: globalTags.length > 0 ? globalTags : undefined,
            gameState: Object.keys(gameState).length > 0 ? gameState : undefined,
        };
    }
}
exports.InkEngine = InkEngine;
//# sourceMappingURL=ink-engine.js.map