window.onload = () => {

    var element = document.getElementById("lighting");
    window.running = false;
    window.fastForwardingAll = false;
    window.speech = false;

    function setRandomDuration(event) {
      var randomDuration = Math.random() * (5 - 0.1) + 0.1;
      var previousDirection = event.animationName;
    //   element.style.animation = null;
    //   console.log("Animation restarts from:", element.style.animationName, event);
      if (previousDirection == 'gradient-animation-grow')
        element.style.animation = `gradient-animation-shrink ${randomDuration}s 1`;
      else
        element.style.animation = `gradient-animation-grow ${randomDuration}s 1`;
    }

    async function fetch_include(filename) {
        const response = await fetch(filename);
        code = await response.text();
        // console.log("Loaded include:", JSON.parse(code));
        return JSON.parse(code);
    }

    var storyContent = {};
    // const storyContent = fetch_include('Herrenhaus.js');
    // const file_contents_1 = fetch_include("code/Main.ink");
    // const file_contents_2 = fetch_include("code/Stats.ink");

    // const fileHandler = new inkjs.JsonFileHandler({
    //     "Main.ink": file_contents_1,
    //     "Stats.ink": file_contents_2
    // });
    // const errorHandler = (message, errorType) => {
    //     console.log(message + "\n");
    // }
    // const story = new inkjs.Compiler(file_contents_1, {fileHandler, errorHandler}).Compile();
    // // story is an inkjs.Story that can be played right away
    
    // storyContent = story.ToJson();
    // // the generated json can be further re-used
    
const translations = {
   'en-us': {
        by: "",
        speed: "speed<sup>*<sup>",
        title_speed: "Set speed of text animation",
        restart: "restart",
        title_restart: "Restart story from beginning",
        save: "save",
        title_save: "Save progress",
        load: "load",
        title_load: "Reload from save point",
        prompt: "<i>What do you want to do?</i>",
        remark: "<i><sup>*</sup><b>click</b> on the right page or press the <b>spacebar</b><br />to fast forward the text-animation</i>",
        end: "The End",
        action_examine: "objects to examine",
        action_comment: "topics to comment on",
        action_ask: "things to ask about",
        action_interact: "things to interact with",
        action_reflect: "things to reflect on",
        action_inventory: "things you carry with you",
        speech: "Speech",
        title_speech: "Toggle text to speech"
    },
    'de': {
        by: "",
        speed: "Geschwindikeit<sup>*<sup>",
        title_speed: "Geschwindigkeit der Textanimation einstellen",
        restart: "Neustart",
        title_restart: "Die Geschichte von vorne beginnen",
        save: "Speichern",
        title_save: "Den Fortschritt der Geschichte speichern",
        load: "Laden",
        title_load: "Zum gespeicherten Spielfortschritt zurückkehren",
        prompt: "Was möchtest du tun?",
        remark: "<i><sup>*</sup><b>Klicke</b> auf die rechte Buchseite oder drücke die <b>Leertaste</b><br /> um die Textanimation zu überspringen</i>",
        end: "Ende",
        action_examine: "Untersuchen",
        action_comment: "Kommentieren",
        action_ask: "Fragen",
        action_interact: "Interagieren",
        action_reflect: "Reflektieren",
        action_inventory: "Inventar",
        speech: "Sprachausgabe",
        title_speech: "Sprachausgabe ein und ausschalten"
    }
};

// Function to change locale
function setLocale(locale) {
    if (translations[locale]) {
        Object.keys(translations[locale]).forEach(key => {
            const prefix = key.substring(0, 5);
            const postfix = key.substring(6, key.length);
            // console.log("Detected translation:", key, prefix, postfix);
            const elements = document.querySelectorAll(`.l10n-${(prefix === 'title' ? postfix : key)}`);
            elements.forEach(element => {
                // console.log("Translating:", element, locale, key);
                if(prefix === "title")
                    element.title = translations[locale][key];
                else
                    element.innerHTML = translations[locale][key];
            });
        });
    } else {
        console.error(`Locale ${locale} is not defined`);
    }
}

setLocale(locale);
// console.log("SmartyPants:", SmartyPants.smartypants('"Dies ist ein Test...", sagte Georg.', 1));

Hyphenopoly.config({
    require: {
        "en-us": "FORCEHYPHENOPOLY",
        "de": "FORCEHYPHENOPOLY"
    },
    paths: {
        maindir: "./",
        patterndir: "./patterns/"
    },
    setup: {
        selectors: {
            ".hyphenate": {
                hyphen: "\u00AD"
            },
            ".hyphenatePipe": {
                hyphen: "|"
            }
        }
    }
});

Hyphenopoly.hyphenators[locale].then((hyphenator_en) => {
    (async function(storyContent) {
        // const response = await fetch('TheIntercept.ink.json');
        const response = await fetch('Herrenhaus.ink.json');
        storyContent = await response.json();
        // console.log("Loading game:", response, storyContent);

        // Create ink story from the content using inkjs
        var story = new inkjs.Story(storyContent);
        var cev = () => {};
        var savePoint = "";
        var rstack = [];
        var ruler = document.getElementById('ruler');
        var measure = [];

        rstack.push(ruler);

        var hasSave = false;
        element.addEventListener("animationend", setRandomDuration); // Set new duration each time animation ends
        window.addEventListener("turnCompleteEvent", event => {
            // console.log("Turn ended:", event);
            window.running = false;
            window.fastForwardingAll = false;
            window.indented_paragraphs = 0;

            if (hasSave) {
                document.getElementById("reload").removeAttribute("disabled");
            }
            document.getElementById("rewind").removeAttribute("disabled");
        });    

        const speedSlider = document.getElementById('speed');
        window.speed = Math.pow(100.0 - speedSlider.value, 3) / 10000 * 10 + 0.01;
        window.delay = 0.0;
        speedSlider.oninput = function() {
          window.speed = Math.pow(100.0 - this.value, 3) / 10000 * 10 + 0.01;
        //   console.log(`Speed: ${speed}ms`); // Replace this with your animation speed setting function
        };        

        let fade_in = true;

        // Global tags - those at the top of the ink file
        // We support:
        //  # title: Your Title
        //  # author: Your Name
        //  # subtitle: Your Subtitle
        var globalTags = story.globalTags;
        if( globalTags ) {
            for(var i=0; i<story.globalTags.length; i++) {
                var globalTag = story.globalTags[i];
                var splitTag = splitPropertyTag(globalTag);

                // title: Your Title
                if( splitTag && splitTag.property == "title" ) {
                    var title = document.querySelector('.title');
                    title.innerHTML = splitTag.val;
                }

                // author: Your Name
                else if( splitTag && splitTag.property == "author" ) {
                    var byline = document.querySelector('.byline');
                    byline.textContent += splitTag.val;
                }

                // subtitle: Your Subtitle
                else if( splitTag && splitTag.property == "subtitle" ) {
                    var byline = document.querySelector('.subtitle');
                    byline.textContent += splitTag.val;
                }
            }
        }

        var storyContainer = document.querySelector('#story');
        var choiceContainer = document.querySelector('#choices');
        choiceContainer.lang = locale;
        var outerScrollContainer = document.querySelector('#book');

        let timeoutQueue = [];

        function scheduleTimeout(func, delay, ...args) {
            const timeoutObject = {
                execute: () => func(...args),
                timeoutId: null
            };

            timeoutObject.timeoutId = setTimeout(() => {
                timeoutObject.execute();
                timeoutQueue = timeoutQueue.filter(t => t !== timeoutObject);
                if(timeoutQueue.length <= 0){
                    let event = new CustomEvent("allWordsSetEvent", {
                        detail: { messages: "All scheduled word fade in animations were played."},
                        bubbles: true,
                        cancelable: false
                    });
                    document.dispatchEvent(event);
                }
            }, delay);

            timeoutQueue.push(timeoutObject);

            return timeoutObject.timeoutId;
        }

        function fastForward() {
            window.delay = 0.0;
            // Sort the queue based on timeoutId (assuming that smaller ids are scheduled earlier)
            timeoutQueue.sort((a, b) => a.timeoutId - b.timeoutId);
            // Clear and execute all timeouts
            timeoutQueue.forEach(timeoutObject => {
                clearTimeout(timeoutObject.timeoutId);
                timeoutObject.execute();
            });
            timeoutQueue = [];
            let event = new CustomEvent("allWordsSetEvent", {
                detail: { messages: "All scheduled word fade in animations were played."},
                bubbles: true,
                cancelable: false
            });
            document.dispatchEvent(event);
            document.getElementById("page_right").scrollTo({top: document.getElementById("page_right").scrollHeight, behavior: 'smooth'});
        }

        function fastForwardAll() {
            window.fastForwardingAll = true;
            fastForward();
        }


        function smoothScroll(target, duration) {
            var display = document.getElementById('page_right');
            var targetPosition = target.getBoundingClientRect().top;
            var startPosition = display.scrollTop;
            var distance = targetPosition;
            var startTime = null;
            // console.log("Scheduled scrolldown to:", target, duration);
            if(duration < 5) {
                display.scrollTo(0, targetPosition);
                return;
            }
        
            function animation(currentTime) {
                if (startTime === null) startTime = currentTime;
                var timeElapsed = currentTime - startTime;
                var run = ease(timeElapsed, startPosition, distance, duration);
                display.scrollTo(0, run);
                if (timeElapsed < duration) requestAnimationFrame(animation);
            }
        
            function ease(t, b, c, d) {
                // console.log("Easing:", t, b, c, d);
                t /= d / 2;
                if (t < 1) return c / 2 * t * t + b;
                t--;
                return -c / 2 * (t * (t - 2) - 1) + b;
            }
        
            requestAnimationFrame(animation);
        }

        function typesetParagraph(paragraph_data, delay = 0, measure = []) {
            var stack = [];
            var left = 0;
            var p = document.createElement("p");
            p.style.position = 'relative';
            p.classList.add("latest-paragraph");
            p.dataset.numberOfLines = paragraph_data.breaks.length - 1;
            var line_height = parseFloat(window.getComputedStyle(document.querySelector('#ruler')).lineHeight);
            var line_width = parseFloat(window.getComputedStyle(document.getElementById('story')).width);
            var page_height = parseFloat(window.getComputedStyle(document.getElementById('page_right')).height);
            p.style.height = line_height * (paragraph_data.breaks.length - 1) + 'px';
            var paragraph_height = parseFloat(p.style.height);
            p.dataset.vpc = paragraph_height * 100 / page_height;
            p.style.marginBlockEnd = 0;
            stack.push(p);
            for(let i = 1; i < paragraph_data.breaks.length; i++) {
                left = measure[measure.length - 1] - measure[Math.min(i - 1, measure.length - 1)];
                var lastChild = null;
                var syllable = "";
                for(let j = paragraph_data.breaks[i-1].position; j <= paragraph_data.breaks[i].position; j++) {
                    if(paragraph_data.nodes[j].type === 'box' && paragraph_data.nodes[j].value !== '' &&  j < paragraph_data.breaks[i].position) {
                        if(j > paragraph_data.breaks[i-1].position + 1 && paragraph_data.nodes[j-1].type === 'penalty' && lastChild) {
                            syllable += '\u200c' +  paragraph_data.nodes[j].value;
                            lastChild.innerHTML = syllable;
                            left += paragraph_data.nodes[j].width;
                        } else {
                            let word = document.createElement("span");
                            word.style.position = 'absolute';
                            word.classList.add("fade-in");
                            word.style.animationDuration = speed * 10 + 'ms';
                            word.style.top = line_height * (i - 1) * 100 / paragraph_height + '%';
                            // word.style.left = left + 'px';
                            word.style.left = left * 100 / line_width + '%';
                            syllable = paragraph_data.nodes[j].value;
                            word.innerHTML = syllable;
                            lastChild = word;
                            if(!window.fastForwardingAll)
                                insertAfter(delay, stack[stack.length-1], word);
                            delay += window.speed;
                            left += paragraph_data.nodes[j].width;
                        }
                    } else if(paragraph_data.nodes[j].type === 'tag') {
                        if(paragraph_data.nodes[j].value.substr(0,2) == '</') {
                            stack.pop();
                        } else {
                            let tmp = document.createElement('div');
                            tmp.innerHTML = paragraph_data.nodes[j].value;
                            word = tmp.firstChild;
                            // word.style.left = left + 'px';
                            word.style.left = left * 100 / line_width + '%';
                            stack[stack.length-1].appendChild(word);
                            stack.push(word);
                        }
                    } else if(j > paragraph_data.breaks[i-1].position && paragraph_data.nodes[j].type === 'glue' && paragraph_data.nodes[j].width !== 0 && j <= paragraph_data.breaks[i].position) {
                        // Insert space character
                        if(paragraph_data.breaks[i].ratio > 0) {
                            left += paragraph_data.nodes[j].width + paragraph_data.breaks[i].ratio * paragraph_data.nodes[j].stretch;
                        } else {
                            left += paragraph_data.nodes[j].width + paragraph_data.breaks[i].ratio * paragraph_data.nodes[j].shrink;
                        }
                        let word = document.createElement("span");
                        word.style.position = 'absolute';
                        word.classList.add("fade-in");
                        word.style.top = line_height * (i - 1) * 100 / paragraph_height + '%';
                        // word.style.left = left + 'px';
                        word.style.left = left * 100 / line_width + '%';
                        word.innerHTML = " ";
                        if(!window.fastForwardingAll)
                            insertAfter(delay, stack[stack.length-1], word);
                    } else if(paragraph_data.nodes[j].type === 'penalty' && paragraph_data.nodes[j].penalty === 100 && j === paragraph_data.breaks[i].position) {
                        let word = document.createElement("span");
                        word.style.position = 'absolute';
                        word.style.top = line_height * (i - 1) * 100 / paragraph_height + '%';
                        // word.style.left = left + 'px';    
                        word.style.left = left * 100 / line_width + '%';
                        word.innerHTML = "-";
                        if(!window.fastForwardingAll)
                            insertAfter(delay, stack[stack.length-1], word);
                        delay += window.speed;
                    }
                }
            };
            return [p, delay];
        }

        function measureText(str) {
            if(str.substr(0, 2) == '</') {
                let child = rstack.pop();
                ruler = rstack[rstack.length-1];
                ruler.removeChild(child);
                return 0;
            } else if(str.substr(0, 1) == '<') {
                let tmp = document.createElement('div');
                tmp.innerHTML = str;
                word = tmp.firstChild;
                ruler = rstack[rstack.length-1];
                rstack.push(word);
                ruler.appendChild(word);
                return 0;
            } else if(str === '|') {
                return 0;
            }else if (str === ' ') {
                str = '\u00A0';
            }
            ruler = rstack[rstack.length-1];
            let textNode = document.createTextNode(str);
            ruler.appendChild(textNode);
            let width = ruler.getClientRects()[0].width;
            // console.log("Measuring:", str, ruler.cloneNode(true), width);
            ruler.removeChild(textNode);
            return width;
        }

        function updateBookDimensions() {
            const vw = window.innerWidth;
            const vh = window.innerHeight;
            const viewportAspectRatio = vw / vh;
            const imageAspectRatio = 2727 / 1691;
        
            let bookWidth, bookHeight;
        
            if (viewportAspectRatio > imageAspectRatio) {
                bookWidth = vh * imageAspectRatio;
                bookHeight = vh;
            } else {
                bookWidth = vw;
                bookHeight = vw / imageAspectRatio;
            }
        
            document.documentElement.style.setProperty('--book-width', `${bookWidth}px`);
            document.documentElement.style.setProperty('--book-height', `${bookHeight}px`);
            
            // Setting a CSS variable that will be either vw or vh depending on the viewport aspect ratio
            document.documentElement.style.setProperty(
            "--viewport-dimension", 
            viewportAspectRatio > imageAspectRatio ? 'vw' : 'vh'
            );
            document.documentElement.style.setProperty('--viewport-aspect-ratio', viewportAspectRatio);
            let story = document.getElementById("story");
            let paddingTop = window.getComputedStyle(story).paddingTop;
            let paddingBottom = window.getComputedStyle(story).paddingBottom;
            document.documentElement.style.setProperty('--story-line-height', (story.clientHeight - paddingTop - paddingBottom) / 28);
            updateParagraphHeight();
        }

        function updateParagraphHeight() {
            document.querySelectorAll("#story p").forEach((element) => {
                let pHeight = parseFloat(window.getComputedStyle(document.getElementById('page_right')).height);
                let newHeight = pHeight * element.dataset.vpc / 100 + 'px';
                element.style.height = newHeight;
            });
        }
        
        // Update the aspect ratio when the page loads
        updateBookDimensions();
        
        // Update the aspect ratio whenever the window is resized
        window.addEventListener('resize', updateBookDimensions);

        window.addEventListener('keydown', (event) => {
            if (event.code === 'Space') {
                fade_in = false;
                fastForward();
            }
        });

        document.getElementById('page_right').addEventListener('click', (event) => {
            fade_in = false;
            fastForward();
        });

        // page features setup
        hasSave = loadSavePoint();
        setupButtons(hasSave);

        // Set initial save point
        savePoint = story.state.toJson();

        // Kick off the start of the story!
        continueStory();

        // Main story processing function. Each time this is called it generates
        // all the next content up as far as the next set of choices.
        async function continueStory(first_time = true) {

            createChoiceContainer = (categoryContainers, categoryNumbers, action, prompt, choice, tagDebug, registerKeys = false) => {
                var choiceCategoryContainer = categoryContainers[action];
                if(!choiceCategoryContainer) {
                    console.log("Creating new category choice container for:", categoryContainers, categoryNumbers, action, prompt, choice, registerKeys, choiceContainer);
                    choiceCategoryContainer = document.createElement('ol');
                    var p = document.createElement('p');
                    p.innerHTML = (prompt);
                    choiceCategoryContainer.appendChild(p);
                    choiceCategoryContainer.classList.add("choice");
                    choiceCategoryContainer.classList.add("fade-in");
                    if(!registerKeys)
                        choiceCategoryContainer.classList.add("categorized");
                    if(story.currentChoices.length && !window.fastForwardingAll)
                        choiceContainer.appendChild(choiceCategoryContainer);
                }
                categoryContainers[action] = choiceCategoryContainer;
                var choiceNumber = categoryNumbers[action];
                if(choiceNumber === undefined)
                    choiceNumber = 0;
                choiceNumber++;
                var choiceParagraphElement = document.createElement('li');
                choiceParagraphElement.classList.add("choice");
                choiceParagraphElement.lang = locale;
                choiceParagraphElement.title = tagDebug;
                choiceParagraphElement.innerHTML = `<a href='#'>${SmartyPants.smartypantsu(choice.text, 1)}</a>`
                if(!window.fastForwardingAll)
                    insertAfter(window.delay, choiceCategoryContainer, choiceParagraphElement, fade_in);
                window.delay += window.speed;
                // Press choice key
                if(registerKeys) {
                    choiceParagraphElement.value = choiceNumber;
                    registerKey('Digit' + choiceNumber, choice.index);
                } else {
                    var categorizedNumber = categoryNumbers['categorized'];
                    categorizedNumber++;
                    var keyLetter = String.fromCharCode(64 + categorizedNumber);
                    console.log("Registering key:", keyLetter, categorizedNumber, choice.index);
                    choiceParagraphElement.value = categorizedNumber;
                    registerKey('Key' + keyLetter, choice.index);
                    categoryNumbers['categorized'] = categorizedNumber;
                }
                // Click on choice
                var choiceAnchorEl = choiceParagraphElement.querySelectorAll("a")[0];
                choiceAnchorEl.addEventListener("click", (event) => {
                    // Don't follow <a> link
                    event.preventDefault();
                    choose(choice.index);
                });
                categoryNumbers[action] = choiceNumber;
            }
    
            var fade_in = true;
            window.running = true;
            window.fastForwardingAll = false;
            chapter_begin = false;
            this.keyRegistry = {};
            if(measure.length == 1)
                measure.pop(); // Remove lingering measures if all that is left is the full line.

            document.querySelectorAll('#story p').forEach((p) => { p.classList.remove("latest-paragraph")});

            // Generate story text - loop through available content
            while(story.canContinue) {
                if(window.fastForwardingAll)
                    return;
                window.delay = 0.0;
                // Get ink to generate the next paragraph
                var paragraphText = story.Continue();
                var tags = story.currentTags;

                // Any special tags included with this line
                var customClasses = [];
                var tagDebug = "";
                for(var i=0; i<tags.length; i++) {
                    var tag = tags[i];
                    tagDebug += tag + ";";
                    

                    // Detect tags of the form "X: Y". Currently used for IMAGE and CLASS but could be
                    // customised to be used for other things too.
                    var splitTag = splitPropertyTag(tag);

                    // AUDIO: src
                    if( splitTag && splitTag.property == "AUDIO" ) {
                    if('audio' in this) {
                        this.audio.pause();
                        this.audio.removeAttribute('src');
                        this.audio.load();
                    }
                    this.audio = new Audio(splitTag.val);
                    this.audio.play();
                    }

                    // AUDIOLOOP: src
                    else if( splitTag && splitTag.property == "AUDIOLOOP" ) {
                    if('audioLoop' in this) {
                        this.audioLoop.pause();
                        this.audioLoop.removeAttribute('src');
                        this.audioLoop.load();
                    }
                    this.audioLoop = new Audio(splitTag.val);
                    this.audioLoop.play();
                    this.audioLoop.loop = true;
                    }

                    // IMAGE: src
                    if( splitTag && splitTag.property == "IMAGE" ) {
                        var imageElement = document.createElement('img');
                        imageElement.src = splitTag.val;
                        storyContainer.appendChild(imageElement);

                        showAfter(window.delay, imageElement);
                        window.delay += window.speed;
                    }

                    // LINK: url
                    else if( splitTag && splitTag.property == "LINK" ) {
                        window.location.href = splitTag.val;
                    }

                    // LINKOPEN: url
                    else if( splitTag && splitTag.property == "LINKOPEN" ) {
                        window.open(splitTag.val);
                    }

                    // BACKGROUND: src
                    else if( splitTag && splitTag.property == "BACKGROUND" ) {
                        outerScrollContainer.style.backgroundImage = 'url('+splitTag.val+')';
                    }

                    // CLASS: className
                    else if( splitTag && splitTag.property == "CLASS" ) {
                        customClasses.push(splitTag.val);
                    }

                    // CLEAR - removes all existing content.
                    // RESTART - clears everything and restarts the story from the beginning
                    else if( tag == "CLEAR" || tag == "RESTART" ) {
                        removeAll("p");
                        removeAll("img");

                        // Comment out this line if you want to leave the header visible when clearing
                        // setVisible(".header", false);

                        if( tag == "RESTART" ) {
                            restart();
                            return;
                        }
                    }

                    // CHAPTER: Chapter Heading
                    else if( splitTag.property == "CHAPTER" ) {
                        var h = document.createElement('H2');
                        h.appendChild(document.createTextNode(splitTag.val));
                        h.classList.add("chapter-heading");
                        h.classList.add("fade-in");
                        storyContainer.appendChild(h);
                        chapter_begin = true;
                        window.indented_paragraphs = 2;
                    }

                    // SEPARATOR
                    else if( tag == "SEPARATOR") {
                        var d = document.createElement('double');
                        d.appendChild(document.createTextNode('\u2766'));
                        d.classList.add("fade-in");
                        d.classList.add("separator");
                        storyContainer.appendChild(d);
                        chapter_begin = true;
                    }
                }

                // Create paragraph element (initially hidden)
                if(paragraphText.trim().length === 0)
                    continue;

                var indentWidth = 2 * parseFloat(window.getComputedStyle(document.querySelector("#indent")).lineHeight);
                var text = paragraphText;
                var drop_cap = null;
                var drop_quote = null;
                if(chapter_begin) {
                    measure.push(parseFloat(window.getComputedStyle(document.getElementById("story")).width));
                    measure.push(parseFloat(window.getComputedStyle(document.getElementById("story")).width) - indentWidth);
                    measure.push(parseFloat(window.getComputedStyle(document.getElementById("story")).width) - indentWidth * 0.9);

                    let words = paragraphText.split(" ");
                    let first_word = words[0].substr(1,words[0].length);
                    let opening_quote = "";
                    let first_letter = words[0].substr(0,1);
                    if(first_letter == "\"" || first_letter == "'") {
                        opening_quote = SmartyPants.smartypantsu(first_letter, 1);
                        first_letter = words[0].substr(1,1);
                        first_word = words[0].substr(2,words[0].length);
                    }
                    if(first_word.length < 5 && words.length > 1)
                        first_word += ' ' + words[1];
                    text = '<cap>' + first_word + '</cap> ' + paragraphText.substr(first_word.length + 2 + opening_quote.length, paragraphText.length);
                    console.log("Created chapter begin:", words, first_word, first_letter);
                    drop_cap = document.createElement("span");
                    drop_cap.classList.add("drop-cap");
                    drop_cap.appendChild(document.createTextNode(first_letter));
                    drop_cap.style.left = '0%';
                    drop_cap.style.top = '0%';
                    drop_cap.style.position = 'absolute';
                    if(opening_quote) {
                        drop_quote = document.createElement("span");
                        drop_quote.classList.add("drop-quote");
                        drop_quote.appendChild(document.createTextNode(opening_quote));
                        drop_quote.style.left = '-4.45%';
                        drop_quote.style.top = '-16%';
                        drop_quote.style.position = 'absolute';    
                    }
                } else {
                    if(measure.length < 1) {
                        measure.push(parseFloat(window.getComputedStyle(document.getElementById("story")).width));
                        measure.push(parseFloat(window.getComputedStyle(document.getElementById("story")).width) - indentWidth * 0.5);
                    }
                }
                var preview_data = kap(hyphenator_en(SmartyPants.smartypantsu(text, 1), '.hyphenatePipe'), measureText, measure.toReversed(), true);
                var p, d;
                [p, d] = typesetParagraph(preview_data, window.delay, measure.toReversed());
                for(let k = 0; k < parseInt(p.dataset.numberOfLines); k++) {
                    measure.pop();
                }
                window.indented_paragraphs -= p.dataset.numberOfLines;
                console.log("Reducing indented_paragraphes to:", window.indented_paragraphs, p, preview_data, measure);
                if(drop_quote)
                    insertAfter(0, p, drop_quote, true);
                if(drop_cap)
                    insertAfter(0, p, drop_cap, true);
                // window.delay = d;
                // Add any custom classes derived from ink tags
                for(var i=0; i<customClasses.length; i++)
                    p.classList.add(customClasses[i]);
                p.lang = locale;
                p.title = tagDebug;
                storyContainer.appendChild(p);
                smoothScroll(p, window.speed * 10 * p.dataset.numberOfLines);
                chapter_begin = false;
                await Promise.all([new Promise(resolve => {
                    document.addEventListener('allWordsSetEvent', resolve, { once: true });
                }), new Promise(async resolve => {
                    if(!window.speech) {
                        resolve();
                        return;
                    }
                    let filepath = await window.elevenlabs.getSpeech(text);
                    const audio = new Audio(`${filepath}`);
                    audio.onended = resolve; // Resolve the promise when the audio ends
                    audio.play();
                    // Listen for a click event to fade out the audio
                    storyContainer.addEventListener('click', fadeOutAudio);
    
                    // Listen for a keypress event to fade out the audio
                    window.addEventListener('keydown', fadeOutAudio);
    
                    audio.play();
    
                    function fadeOutAudio(event) {
                        if((event instanceof KeyboardEvent && event.key ===  ' ') || (event instanceof MouseEvent && event.type === 'click')) {
                            // Stop listening for the click and keypress events            
                            storyContainer.removeEventListener('click', fadeOutAudio);
                            window.removeEventListener('keydown', fadeOutAudio);
            
                            // Fade out the audio by decrementing the volume
                            let volume = 1.0;
                            const fadeInterval = setInterval(() => {
                                if (volume > 0.1) {
                                volume -= 0.1; // Change this to make the fade out faster or slower
                                audio.volume = volume;
                                } else {
                                // Stop the fade out
                                clearInterval(fadeInterval);
            
                                // Stop the audio
                                audio.pause();
                                resolve();
                                }
                            }, window.speed); // Change this to make the fade out faster or slower
            
                        }
                    }                        
                })])
        }

            window.delay = 0.0;
            // Create HTML choices from ink choices
            var categoryContainers = { default: null }
            var categoryNumbers = { default: 0, categorized: 0 }
            story.currentChoices.forEach(function(choice) {
                if(window.fastForwardingAll)
                    return;
                // Create paragraph with anchor element
                var tagDebug = "";
                var action = "default";
                choice.tags.forEach(tag => {
                    tagDebug += tag + ";"
                    var splitTag = splitPropertyTag(tag);
                    // console.log("Split choice tag:", splitTag);
                    if(splitTag.property === "ACTION")
                        action = splitTag.val;
                });

                if(action != "default") {
                    createChoiceContainer(categoryContainers, categoryNumbers, action, translations[locale]['action_' + action], choice, tagDebug);
                } else {
                    createChoiceContainer(categoryContainers, categoryNumbers, "default", translations[locale]['prompt'], choice, tagDebug, true);
                }
            });

            cev = (event) => {
                console.log("Key pressed:", event, this.keyRegistry);
                for(const key in this.keyRegistry) {
                    if(event.code === key) {
                        window.removeEventListener('keypress', cev);
                        choose(this.keyRegistry[key]);
                        break;
                    }
                }
            };
            window.addEventListener('keypress', cev);

            function choose(index) {
                // Remove all existing choices
                removeAll(".choice", true);
                clearKeyRegistry();
    
                // Tell the story where to go next
                story.ChooseChoiceIndex(index);
    
                // This is where the save button will save from
                savePoint = story.state.toJson();
    
                // Aaand loop
                continueStory(false);
            }
    
            function registerKey(key, choice) {
                this.keyRegistry[key] = choice;
            }
    
            function clearKeyRegistry() {
                this.keyRegistry = {};
            }
            var tce = new CustomEvent("turnCompleteEvent", {
                detail: { messages: "All text and choices have been set up."},
                bubbles: true,
                cancelable: false
            });
            document.dispatchEvent(tce);
            if(story.canContinue === false && story.currentChoices.length === 0) {
                var end = document.createElement("p");
                end.style.textTransform = "uppercase";
                end.style.textAlign = "center";
                end.classList.add("fade-in");
                end.classList.add("choice");
                end.appendChild(document.createTextNode(translations[locale]['end']));
                choiceContainer.appendChild(end);
            }
        }

        function restartStory() {
            window.delay = 0.0;
            story.ResetState();
            fastForwardAll();
            setVisible(".header", true);
            removeAll("p");
            removeAll("img");
            removeAll("h2");
            removeAll("double");
            removeAll(".choice", true);
            window.removeEventListener('keypress', cev);

            // set save point to here
            savePoint = story.state.toJson();
        }

        // -----------------------------------
        // Various Helper functions
        // -----------------------------------

        // Fades in an element after a specified delay
        function showAfter(delay, el) {
            el.classList.add("hide");
            setTimeout(function() {
                setTimeout(function() { el.classList.remove("hide") }, delay);
            });
        }

        function insertAfter(delay, target, el, fade_in = true) {
            if(fade_in) {
                el.classList.add("fade-in");
                scheduleTimeout(function() {
                    target.appendChild(el);
                    // el.scrollIntoView({ behavior: 'smooth'});
                }, delay);
            } else {
                scheduleTimeout(function() {
                    target.appendChild(el);
                }, delay);
            }
        }

        // Remove all elements that match the given selector. Used for removing choices after
        // you've picked one, as well as for the CLEAR and RESTART tags.
        function removeAll(selector, choices = false)
        {
            if(choices)
                var allElements = choiceContainer.querySelectorAll(selector);
            else
                var allElements = storyContainer.querySelectorAll(selector);
            for(var i=0; i<allElements.length; i++) {
                var el = allElements[i];
                el.parentNode.removeChild(el);
            }
        }

        // Used for hiding and showing the header when you CLEAR or RESTART the story respectively.
        function setVisible(selector, visible)
        {
            var allElements = storyContainer.querySelectorAll(selector);
            for(var i=0; i<allElements.length; i++) {
                var el = allElements[i];
                if( !visible )
                    el.classList.add("invisible");
                else
                    el.classList.remove("invisible");
            }
        }

        // Helper for parsing out tags of the form:
        //  # PROPERTY: value
        // e.g. IMAGE: source path
        function splitPropertyTag(tag) {
            var propertySplitIdx = tag.indexOf(":");
            if( propertySplitIdx != null ) {
                var property = tag.substr(0, propertySplitIdx).trim();
                var val = tag.substr(propertySplitIdx+1).trim();
                return {
                    property: property,
                    val: val
                };
            }

            return null;
        }

        // Loads save state if exists in the browser memory
        function loadSavePoint() {
            try {
                let savedState = window.localStorage.getItem('save-state');
                let savedHistory = window.localStorage.getItem('save-history');
                if (savedState && savedHistory) {
                    let history = JSON.parse(savedHistory);
                    // console.log('Loaded history:', history);
                    history.forEach(p => {
                        let d = document.createElement('div');
                        d.innerHTML = p;
                        document.getElementById('story').appendChild(d.firstChild);
                    });
                    story.state.LoadJson(savedState);
                    updateParagraphHeight();
                    window.removeEventListener('keypress', cev);
                    return true;
                }
            } catch (e) {
                console.debug("Couldn't load save state");
            }
            return false;
        }

        // Used to hook up the functionality for global functionality buttons
        function setupButtons(hasSave) {

            let rewindEl = document.getElementById("rewind");
            let saveEl = document.getElementById("save");
            let reloadEl = document.getElementById("reload");
            let speedEl = document.getElementById("speed_reset");
            let speechEl = document.getElementById("speech");

            if (rewindEl) rewindEl.addEventListener("click", function(event) {
                if (rewindEl.getAttribute("disabled") == "disabled")
                    return;
                rewindEl.setAttribute("disabled", "disabled");
                reloadEl.setAttribute("disabled", "disabled");
                restartStory();
                if(window.running)
                    window.addEventListener("turnCompleteEvent", continueStory());
                else {
                    // if (hasSave) {
                    //     document.getElementById("reload").removeAttribute("disabled");
                    // }
                    // document.getElementById("rewind").removeAttribute("disabled");
                    continueStory();            
                }
            });

            if (saveEl) saveEl.addEventListener("click", function(event) {
                if (save.getAttribute("disabled") == "disabled")
                    return;
                try {
                    let history = Array.from(document.querySelectorAll("#story p:not(.latest-paragraph)")).map(p => p.outerHTML);
                    // console.log("Saving history:", history);
                    window.localStorage.setItem('save-history', JSON.stringify(history));
                    window.localStorage.setItem('save-state', savePoint);
                    hasSave = true;
                    reloadEl.removeAttribute("disabled");
                } catch (e) {
                    console.warn("Couldn't save state");
                }

            });

            reloadEl.addEventListener("click", function(event) {
                if (reloadEl.getAttribute("disabled") == "disabled")
                    return;
                reloadEl.setAttribute("disabled", "disabled");
                rewindEl.setAttribute("disabled", "disabled");
                fastForwardAll();
                removeAll("p");
                removeAll("img");
                removeAll("h2");
                removeAll("double");    
                removeAll(".choice", true);
                loadSavePoint();
                if(window.running)
                    window.addEventListener("turnCompleteEvent", continueStory());
                else {
                    // if (hasSave) {
                    //     document.getElementById("reload").removeAttribute("disabled");
                    // }
                    // document.getElementById("rewind").removeAttribute("disabled");
                    continueStory();                    
                }
            });

            speedEl.addEventListener('click', () => {
                let range = document.getElementById('speed');
                range.value = 50;
                range.dispatchEvent(new Event('input'));

            });

            speechEl.addEventListener('click', () => {
                window.speech = !window.speech;
                if(speechEl.getAttribute('disabled') === 'disabled')
                    speechEl.removeAttribute('disabled');
                else
                    speechEl.setAttribute('disabled', 'disabled');
            })
        }

    })(storyContent);
});
};