class chatPostprocessor{
    constructor(){
    }

    preprocess(chatEntry, rawData){
        this.rawData = rawData;
        //Set current chat nodes
        this.chatEntry = chatEntry;
        this.chatBody = this.chatEntry.querySelector(".chat-entry-body");
        //Split the chat body into an array of objects representing each word
        //We could pass this through arguments but these functions wont be very interoperable anyways since they expect a purpose-made hashtable
        this.splitBody();

        //Inject links into un-processed placeholders
        this.processLinks();

        //Inject whitespace into un-processed words
        this.addWhitespace();

        //Handle non-standard chat types
        this.handleChatType();
        
        //Inject the pre-processed chat into the chatEntry node
        this.injectBody();
        //Return the pre-processed node
        return this.chatEntry;
    }

    splitBody(){
        //Create an empty array to hold the body
        this.bodyArray = [];
        //Split string by word-boundries, with negative/forward lookaheads to exclude file seperators so we don't split link placeholders
        const splitString = this.chatBody.innerHTML.split(/(?<!␜)\b/g);

        //for each word in the splitstring
        splitString.forEach((string) => {
            //create a word object
            const wordObj = {
                string: string,
                type: "word"
            }

            //Add it to our body array
            this.bodyArray.push(wordObj);
        });
    }

    injectBody(){
        //Clear our chat body
        this.chatBody.innerHTML = "";
        
        //Extract strings into the empty array
        this.bodyArray.forEach((wordObj) => {
            if(wordObj.type == 'word'){
                //Inject current wordObj string into the chat body
                this.chatBody.innerHTML += wordObj.string
            }else if(wordObj.type == 'link'){
                //Create a link node from our link
                const link = document.createElement('a');
                link.classList.add('chat-link');
                link.href = wordObj.link;
                link.innerHTML = wordObj.link;

                //Append node to chatBody
                this.injectNode(wordObj, link);
            }else if(wordObj.type == 'image'){
                //Create an img node from our link
                const img = document.createElement('img');
                img.classList.add('chat-img');
                img.src = wordObj.link;

                //stringArray.push(wordObj.string.replace('␜',img.outerHTML));
                //Append node to chatBody
                this.injectNode(wordObj, img);
            }else if(wordObj.type == 'video'){
                //Create a video node from our link
                const vid = document.createElement('video');
                vid.classList.add('chat-video');
                vid.src = wordObj.link;
                vid.controls = false;
                vid.autoplay = true;
                vid.loop = true;
                vid.muted = true;

                //stringArray.push(wordObj.string.replace('␜',vid.outerHTML));
                this.injectNode(wordObj, vid);
            }
        });
    }

    //Like string.replace except it actually injects the node so we can keep things like event handlers
    injectNode(wordObj, node, placeholder = '␜'){
        //Split string by the placeholder so we can keep surrounding whitespace
        const splitWord = wordObj.string.split(placeholder, 2);

        //Append the first half of the string
        this.chatBody.innerHTML += splitWord[0];

        //Append the node
        this.chatBody.appendChild(node);

        //Append the second half of the string
        this.chatBody.innerHTML += splitWord[1];
    }

    addWhitespace(){
        //for each word object in the body
        this.bodyArray.forEach((wordObj, wordIndex) => {
            //if the word object hasn't been pre-processed elsewhere
            if(wordObj.type == "word"){
                var wordArray = [];
                //Add invisible whitespace in-between characters to keep it from breaking page layout
                this.bodyArray[wordIndex].string.split("").forEach((char, charIndex) => {
                    wordArray.push(char);
                    //After eight characters
                    if(charIndex > 8){
                        //Push an invisible line-break character between every character
                        wordArray.push("ㅤ");
                    }

                });

                this.bodyArray[wordIndex].string = wordArray.join("");
            }
        });
    }

    processLinks(){
        //If we don't have links
        if(this.rawData.links == null){
            //Don't bother
            return;
        }

        this.rawData.links.forEach((link, linkIndex) => {
            this.bodyArray.forEach((wordObj, wordIndex) => {
                //Check current wordobj for link (placeholder may contain whitespace with it)
                if(wordObj.string.match(`␜${linkIndex}`)){
                    //Set current word object in the body array to the new link object
                    this.bodyArray[wordIndex] = {
                        //Don't want to use a numbered placeholder to make this easier during body injection
                        //but we also don't want to clobber any surrounding whitespace
                        string: wordObj.string.replace(`␜${linkIndex}`, '␜'),
                        link: link.link,
                        type: link.type
                    }
                }
            })
        });
    }

    handleChatType(){
        if(this.rawData.type == "whisper"){
            //add whisper class
            this.chatBody.classList.add('whisper');
        }else if(this.rawData.type == "announcement"){
            //Squash the high-level
            this.chatEntry.querySelector('.high-level').remove();

            //Get the username and make it into an announcement title (little hacky but this *IS* postprocessing)
            const userNode = this.chatEntry.querySelector('.chat-entry-username');
            userNode.innerHTML = `${userNode.innerHTML.slice(0,-2)} Announcement`;

            //Add/remove relevant classes
            userNode.classList.remove('chat-entry-username');
            userNode.classList.add('announcement-title');
            this.chatBody.classList.add('announcement-body');
            this.chatEntry.classList.add('announcement');
        }else if(this.rawData.type == "toke" || this.rawData.type == "tokewhisper"){
            //Squash the high-level
            this.chatEntry.querySelector('.high-level').remove();

            //remove the username
            this.chatEntry.querySelector('.chat-entry-username').remove();

            //Add toke/tokewhisper class
            this.chatBody.classList.add(this.rawData.type);
        }
    }
}