GIVCE THE WHOLE CODE IN CODE FORMAT. DONT REMOVE THE COMMENTS AND THE COMMENTS ALSO TELL YOU WHAT TO CODE. REWRITE THE CODE SO IT LOOKS DIFFERENT BUT OUTPUT IS THE SAME. DONT CHANGE THE CODE TOO MUCH JSUT HOW TI LOOKS LIKE. DONT CHANGE STRUCTURE JSUT NAMES AND WAYS THEY ARE ACHIEVED. /* This class handles the graphical user interface and is yours to complete. */
/* config.js contains important configuration data in an object. It's used e.g. with config.mapWidth. */
import { config } from './config.js';
import { AdventureGame } from "./adventuregame.js";
import { AdventureMap } from './adventuremap.js';

export class Gui {
    constructor(game) {
        this.game = game;
        this.icons = this.preloadImages();
    }
    
    // Draws the map. Parameter "map" contains a two-dimensional array of MapUnit objects. Each square is 40x40 pixels.
    // Width and height in squares are specified in config file.
    drawMap(map) {
        let canvas = document.getElementById(config.mapCanvas);
        let context = canvas.getContext("2d");

        // draw background color (lightgreen)
        context.fillStyle = "lightgreen";
        context.fillRect(0, 0, canvas.width, canvas.height);
        
        // go through the map (loop inside loop) and draw contents to map canvas
        for (let y = 0; y < map.length; y++) {
            const row = map[y];

            for (let x = 0; x < row.length; x++) {
                const rowItem = row[x]; 

                if (rowItem.terrain === "forest") {
                    const image = new Image;
                    image.src = "./images/tree.png";
    
                    context.drawImage(image, x*40, y*40);
                } else if (rowItem.terrain === "town") {
                    const image = new Image;
                    image.src = "./images/town.png";

                    context.drawImage(image, x*40, y*40);
                }

                if (rowItem.enemy === "enemy") {
                    const image = new Image;
                    image.src = "./images/enemy.png";

                    context.drawImage(image, x*40, y*40);
                }
            }
        }


        // items are not shown (they're hidden)

    }

    // draw player character (NOTE: x and y are actual coordinates, not grid squares)
    drawPlayer(x, y) {
        let canvas = document.getElementById(config.mapCanvas);
        let context = canvas.getContext('2d');

        const image = new Image;
        image.src = "./images/player.png";
        
        context.drawImage(image, x, y);
    }
    
    // draw the inventory items, given as list (max number of inventory items is 4 - return immediately if more)
    drawInventory(items) {
        let canvas = document.getElementById(config.inventoryCanvas);
        let context = canvas.getContext("2d");
    
        context.clearRect(0, 0, canvas.width, canvas.height);
        
        if (items.length > 4) return;

        // draw item image for each item in list (image is 50x50 pixels)
        for (let i = 0; i < items.length; i++) {
            const item = items[i];
            
            const image = new Image;
            image.src = `./images/${item}.png`;

            context.drawImage(image, i*50, 0);
        }
    
    }

    // Draw the text given as parameter to the narrative box. Use 14px Helvetica as font.
    // Use wrapText method to split it to lines, then draw each line with fillText method. Method returns arrays within an array.
    // Loop through the items in the outer array. In each item (inner array), slots are text, x coordinate and y coordinate in that order.
    drawNarrative(text) {
        let canvas = document.getElementById(config.narrativeCanvas);
        let context = canvas.getContext("2d");
        
        context.clearRect(0, 0, canvas.width, canvas.height);

        context.font = "14px Arial";

        const textLines = this.wrapText(text);
                
        textLines.forEach(textLine => {
            context.fillText(textLine[0],textLine[1],textLine[2]);
        })
    }

    // Text wrapper for narrative. DO NOT TOUCH!
    // Modified from https://fjolt.com/article/html-canvas-how-to-wrap-text
    wrapText(text) {
        let canvas = document.getElementById(config.narrativeCanvas);
        let context = canvas.getContext('2d');
        // First, start by splitting all of our text into words, but splitting it into an array split by spaces
        let words = text.split(' ');
        let line = ''; // This will store the text of the current line
        let testLine = ''; // This will store the text when we add a word, to test if it's too long
        let lineArray = []; // This is an array of lines, which the function will return

        let x = 5;
        let y = 15;
    
        // Lets iterate over each word
        for(var n = 0; n < words.length; n++) {
            // Create a test line, and measure it..
            testLine += `${words[n]} `;
            let metrics = context.measureText(testLine);
            let testWidth = metrics.width;
            // If the width of this test line is more than the max width
            if (testWidth > (config.narrativeWidth - 10) && n > 0) {
                // Then the line is finished, push the current line into "lineArray"
                lineArray.push([line, x, y]);
                // Increase the line height, so a new line is started
                y += config.lineHeight;
                // Update line and test line to use this word as the first word on the next line
                line = `${words[n]} `;
                testLine = `${words[n]} `;
            }
            else {
                // If the test line is still less than the max width, then add the word to the current line
                line += `${words[n]} `;
            }
            // If we never reach the full max width, then there is only one line.. so push it into the lineArray so we return something
            if(n === words.length - 1) {
                lineArray.push([line, x, y]);
            }
        }
        // Return the line array
        return lineArray;
    }

    // enable arrow key buttons
    enableArrowKeys() {
        let nodes = document.getElementById("controls").getElementsByTagName('button');

        for(let i = 0; i < nodes.length; i++){
            nodes[i].disabled = false;
        }
    }

    // disable arrow key buttons (called by map unit when dialogue activates and only OK / Cancel are allowed)
    disableArrowKeys() {
        let nodes = document.getElementById("controls").getElementsByTagName('button');

        for(let i = 0; i < nodes.length; i++){
            if (nodes[i].id === "ok" || nodes[i].id === "cancel") {
                continue;
            }

            nodes[i].disabled = true;
        }
    }

    // Returns an "array" (object) with names and corresponding image objects. DO NOT TOUCH!
    // Use images with arrayName.imageName, e.g. if your array is in variable gameImages, then gameImages.player for player character image.
    preloadImages() {
        // List all image to preload here - image filename should be name + .png
        const imageNames = ['player', 'town', 'enemy', 'tree', 'potion'];

        let loadCount = 0;
        let loadTotal = imageNames.length;
    
        var loadedImages = {};

        // Create all image objects and store them in the loadedImages object.
        for (let i = 0; i < imageNames.length; i++) {
            let image = new Image();

            // in order for onload handler to have access to the AdventureGame object, it has to be a property of the image (stupid principle)
            image.game = this.game;
            image.src = 'images/' + imageNames[i] + '.png';

            loadedImages[(imageNames[i])] = image;
    
            // Add onload event handler.
            image.onload = function () {
                loadCount++;
                if (loadCount == loadTotal) {
                    // Done loading, can initialize game
                    this.game.init();
                }
            };
        }
    
        // Return an array of images
        return loadedImages;
    }
}