Source: homepage/homepage.js

const DISPLAY_TASK_COUNT = 2;
const RATING_FILES_NAMES = ["1angry.png", "2upset.png", "3neutral.png", "4happy.png", "5overjoyed.png"];
const PRODUCTIVITY_FILES_NAMES = ["1-icon.svg", "2-icon.svg", "3-icon.svg", "4-icon.svg", "5-icon.svg"];

window.addEventListener('DOMContentLoaded', init);

// Get current date global
var currDate = new Date();

// Defines confetti
const confetti = window.confetti;

/**
 * Initializes all necessary components
 */
function init() {
    dateQuery();
    displayDate(formatDate(currDate));
    displayWeek();
    initButtons();
    taskListViewHandler();

    // Configure going to today's homepage on refresh
    window.history.replaceState("stateObj", 
    "new page", "../homepage/homepage.html");

}

/**
 * Initializes functionality of buttons
 */
function initButtons() {
    const nextBtn = document.querySelector(".next-date-btn");
    nextBtn.addEventListener("click", nextDate);
    const prevBtn = document.querySelector(".prev-date-btn");
    prevBtn.addEventListener("click", prevDate);

    const addTaskBtn = document.querySelector(".add-task-btn");
    addTaskBtn.addEventListener("click", () => {
        addTask();
    });

    const ratingSelBtn = document.querySelectorAll(".rating-select-btn");
    ratingSelBtn.forEach(btn => {
        btn.addEventListener("click", () => {
            var id = btn.getAttribute("id");
            selectWidget(id.substring(3,5));
        });
    });

    // Add keyboard left, rigth arrow for switching dates
    window.addEventListener('keydown', function(event) {
        if ((event.target.tagName.toLowerCase() === "textarea") ||
           (event.target.tagName.toLowerCase() === "div")) {
            return;
        }
        if (event.key === "ArrowLeft") {
            prevDate();
        } else if (event.key === "ArrowRight") {
            nextDate();
        }
    });
    // Save user entry to local storage on any changes
    journal.addEventListener("blur", saveJournal);
    tasks.addEventListener("blur", saveTasks);
    tasks.addEventListener("change", saveTasks);
    tasks.addEventListener("blur", saveCompleted);
    tasks.addEventListener("change", saveCompleted);
    completedTasks.addEventListener("blur", saveCompleted);
    completedTasks.addEventListener("change", saveCompleted);
    completedTasks.addEventListener("blur", saveTasks);
    completedTasks.addEventListener("change", saveTasks);
}

/**
 * Updates interface with date
 * 
 * @param {string} date - date in string format
 */
function displayDate(date) {
    const dateContainer = document.getElementById('current-date');
    dateContainer.textContent = date;
}

/**
 * Updates the global currDate to the next date and updates interface
 */
function nextDate() {
    saveJournal();
    let today = new Date();
    today.setHours(0, 0, 0, 0);
    if (currDate <= today) {
        currDate.setDate(currDate.getDate() + 1);
        displayDate(formatDate(currDate));  
    }
    unselectAllWidgets();
    unselectAllCompleted();
    loadAll();
}

/**
 * Updates global currDate to the previous date and updates interface
 */
function prevDate() {
    saveJournal();
    currDate.setDate(currDate.getDate() - 1);
    displayDate(formatDate(currDate));
    unselectAllWidgets();
    unselectAllCompleted();
    loadAll();
}

/**
 * Formats the currDate global variable into proper string display
 * @returns {string} - properly formatted string representing the date as "Weekday, Month Day, Year"
 */
function formatDate() {
    // Formats date as "Weekday, Month Date, Year"
    const formattedDate = currDate.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
    return formattedDate;
}

/**
 * Shows that a given button has been selected by adding the active property to its classname
 * 
 * @param {int} buttonIndex - the index of the button selected.
 */
function selectWidget(buttonIndex) {
    if (buttonIndex > 5) {
        // Clear active class from all buttons in row
        const buttons = document.querySelectorAll('.productiveness img');
        buttons.forEach(button => {
            button.classList.remove('active');
        });
        // Add active class to selected button
        const selection = document.querySelector(`.rating-widget .productiveness button:nth-child(${buttonIndex - 5}) img`);
        selection.classList.add('active');
        saveWidgets(buttonIndex);
    }
    else {
        const buttons = document.querySelectorAll('.feelings img');
        buttons.forEach(button => {
            button.classList.remove('active');
        });
        const selection = document.querySelector(`.rating-widget .feelings button:nth-child(${buttonIndex}) img`);
        selection.classList.add('active');
        saveWidgets(buttonIndex);
    }
}

/**
 * Adds task to task list upon "Add Task" button click. Initializes buttons within each task
 * @param {boolean} [loadTask=false] 
 */
 function addTask(loadTask = false) {
    const taskList = document.querySelector(".task-container");
    const task = document.createElement("li");
    task.setAttribute("class", "task");
    task.insertAdjacentHTML("beforeend", `
        <div class="check-input-wrap">
            <button id="task1" class="task-checkbox" aria-label="Task Checkbox"></button>
            <div contenteditable="true" class="task-input" placeholder="Add a task..." onkeypress="return this.innerText.length <= 180;"></div>
        </div>
        <div class="color-buttons">
            <button id="purple" class="color-button" aria-label="Purle"></button>
            <button id="green" class="color-button" aria-label="Green"></button>
            <button id="blue" class="color-button" aria-label="Blue"></button>
            <button id="pink" class="color-button" aria-label="Pink"></button>
            <button id="grey" class="color-button" aria-label="Grey"></button>
        </div>
        <img class="trash-icon" src="../icons/trash-icon.svg" alt="Remove">
    `);
    task.querySelector(".task-input").addEventListener("input", saveCompleted)
    taskList.append(task);

    // Listener to stop editing when user presses enter
    const task_name = task.querySelector(".task-input");
    task_name.addEventListener('keydown', function (event) {
        // Shift+Enter pressed, insert a line break
        if (event.key == 'Enter') {
            // Enter pressed, end editing
            if (!event.shiftKey) {
                // Prevent default behavior of Enter key
                event.preventDefault();

                // Remove focus from the element
                task_name.blur(); 
            }
        }
    });

    // Auto click into the task name text box
    if (loadTask == false){
        setTimeout(() => {
            task_name.focus();
            const selection = document.getSelection();
            if (selection.rangeCount > 0) {
                selection.collapseToEnd();
            }
        }, 0);
    }   

    taskButtonsFunctionality(task);
    if (loadTask == false){
        saveTasks();
    }

    return task;
}

/**
 * Adds button functionality to task upon creation
 * 
 * @param {HTMLElement} task - the task to have functionality
 */
function taskButtonsFunctionality(task) {
    // Implement color changing functionality
    const colorBtns = task.querySelectorAll(".color-button");
    colorBtns.forEach(btn => {
        btn.addEventListener('click', function() {
            let color;
            switch (btn.id) {
                case "purple":
                    color = "#C380CC";
                    break;
                case "green":
                    color = "#91DC79";
                    break;
                case "blue":
                    color = "#6BB1D9";
                    break;
                case "pink":
                    color = "#EEBAE9";
                    break;
                default:
                    color = "var(--main-color)";
            }
            task.style['background-color'] = color;
            saveCompleted();
            saveTasks();
        });
    });

    // Trash icon delete functionality
    const deleteIcon = task.querySelector(".trash-icon");
    deleteIcon.addEventListener("click", () => {
        task.remove();
        saveCompleted();
        saveTasks();
    });

    // Checkbox move to Completed Tasks functionality
    const checkbox = task.querySelector(".task-checkbox");
    checkbox.addEventListener('click', function() {
        if (task.className.includes('complete')) {
            task.classList.remove('complete');
            const taskContainer = document.querySelector('.task-container');
            taskContainer.appendChild(task);
            task.addEventListener("blur", saveTasks);
            saveCompleted();
            saveTasks();
        }
        else {
            task.classList.add('complete');
            const completedTaskContainer = document.querySelector('.completed-task-container');
            completedTaskContainer.appendChild(task);
            saveCompleted();
            saveTasks();

            confetti({
                particleCount: 100,
                spread: 70,
                origin: { y: 0.6 }
            });
        }
    });
}

/**
 * Unselects all widgets by removing the active property from their classnames
 */
 function unselectAllWidgets() {
    const buttons = document.querySelectorAll('.feelings img');
    buttons.forEach(button => {
        button.classList.remove('active');
    });

    const buttons2 = document.querySelectorAll('.productiveness img');
    buttons2.forEach(button => {
        button.classList.remove('active');
    });
}



/**
 * Updates interface with Past Week view
 */
function displayWeek() {
    let allDays = ["Sun", "Mon", "Tue", "Wed","Thu", "Fri", "Sat"];

    // Get and clear the table
    let table = document.getElementById("week-calendar");
    table.innerHTML = "";

    let currWeekDay = new Date();
    currWeekDay.setDate(currWeekDay.getDate() + 1);

    let row = document.createElement("tr");

    // Initialize  each past day
    for (let i = 0; i < 7; i++) {
        let cellData = document.createElement("td");

        if (i === 0){
            currWeekDay.setDate(currWeekDay.getDate() + (i-8));
        }
        else {
            currWeekDay.setDate(currWeekDay.getDate() + 1);
        }

        let cellNum = document.createElement('span'); 
        cellNum.textContent = allDays[currWeekDay.getDay()] + " " + (currWeekDay.getMonth()+1) + "/" + currWeekDay.getDate();
        cellNum.className = "cell-date";
        cellData.appendChild(cellNum);
        loadCellData(cellData, currWeekDay);
        row.appendChild(cellData);
    }
    table.appendChild(row);
}

/* ******** Storage and Population ********** */

// Get the all relevent elements from page
const journal = document.getElementById("textarea");
const date = document.getElementById("current-date");
const tasks = document.querySelector(".task-container");
const completedTasks = document.querySelector(".completed-task-container");

// Load journal entry and tasks from local storage on page load
window.onload = function () {
    loadAll();
    loadTasks();
}

// Save journal entry and tasks to local storage on page unload
window.onbeforeunload = function () {
    saveJournal()
    saveTasks()
    saveCompleted()
}

/**
 * Load all data from local storage
 */
function loadAll() {
    loadJournal();
    loadWidgets();
    loadCompleted();
}

/**
 * Format journal input to be stored
 * 
 * @param {string} data - journal entry text in parsed json format
 * @param {string} dateText - date of the journal entry in locale date string format
 * @param {string} key - key to store the value under
 * @param {string} value - value to store
 * 
 */
export function saveToStorage(data, dateText, key, value) {
    if (!(dateText in data)) {
        data[dateText] = {}
    }
    data[dateText][key] = value;
}

/**
 * Load journal entry from local storage
 * 
 * @param {string} data - journal entry text in parsed json format
 * @param {string} dateText - date of the journal entry in locale date string format
 * @param {string} key - key to get the value from
 */
export function loadFromStorage(data, dateText, key) {
    if (!(dateText in data)) {
        return null;
    }
    return data[dateText][key];
}

/**
 * Save journal entry to local storage
 */
function saveJournal() {
    let data = getJournal()
    let dateText = new Date(date.textContent).toLocaleDateString();
    saveToStorage(data, dateText, "contents", journal.value)
    localStorage.setItem("journals", JSON.stringify(data))
}

/**
 * Get journal entry from local storage
 * 
 * @returns {string} journal entry text in parsed json format
 */
export function getJournal() {
    let data = JSON.parse(localStorage.getItem("journals"))
    if (data == null) {
        data = {}
    }
    return data
}

/**
 * Load journal entry from local storage
 */
function loadJournal() {
    let data = getJournal()
    let dateText = new Date(date.textContent).toLocaleDateString();
    journal.value = loadFromStorage(data, dateText, "contents") || "";
}

/**
 * Save tasks to local storage
 */
function saveTasks() {
    let tasks = [];
    document.querySelectorAll('.task-container li').forEach(task => {
        let taskName = task.querySelector('.task-input').textContent;
        let taskColor = task.style['background-color']
        tasks.push({
            text: taskName,
            color: taskColor,
        });
    });

    localStorage.setItem("tasks", JSON.stringify(tasks));

    displayWeek();
}

/**
 * Get tasks from local storage
 *
 * @returns {string} tasks in parsed json format or empty array if no tasks
 */
function getTasks() {
    let storedTasks = localStorage.getItem("tasks");
    return storedTasks ? JSON.parse(storedTasks) : [];
}

/**
 * Load tasks from local storage
 */
function loadTasks() {
    let tasks = getTasks();

    if (tasks.length > 0) {
        tasks.forEach(task => {
            let curLi = addTask(true);
            curLi.querySelector(".task-input").textContent = task['text']
            curLi.style['background-color'] = task['color']
        });
    }

}

/**
 * Saves the completed tasks and updates Past Week view
 */
function saveCompleted() {
    let data = getJournal();
    let completedTask = [];
    let dateText = new Date(date.textContent).toLocaleDateString();

    document.querySelectorAll('.completed-task-container li').forEach(completedTaskElement => {
        let taskName = completedTaskElement.querySelector('.task-input').textContent;
        let taskColor = completedTaskElement.style['background-color']
        completedTask.push({
            text: taskName,
            color: taskColor,
        });
    });

    saveToStorage(data, dateText, "completedTasks", completedTask);
    localStorage.setItem("journals", JSON.stringify(data));

    displayWeek();
}

/**
 * Fetch completed tasks from storage in proper format
 * 
 * @returns {string} tasks data in proper format
 */
function getCompleted() {
    let data = getJournal();
    let dateText = new Date(date.textContent).toLocaleDateString();
    let storedTasks = loadFromStorage(data, dateText, "completedTasks");
    return storedTasks ? storedTasks : [];
}

/**
 * Load and populate completed tasks from local storage
 */
function loadCompleted() {
    let tasks2 = getCompleted();
    if (tasks2.length > 0) {
        tasks2.forEach(task => {
            let curLi = addTask(true);
            completedTasks.appendChild(curLi);
            curLi.querySelector(".task-input").textContent = task['text']
            curLi.style['background-color'] = task['color']
            curLi.classList.add('complete')
        });
    }
}

/**
 * Remove completed tasks from interface upon changing dates
 */
function unselectAllCompleted() {
    document.querySelectorAll('.completed-task-container li').forEach(task => {
        task.remove();
    });
}

/**
 * Save widgets to local storage and updates Past Week view
 * 
 * @param {int} value - ID value of the widget selected
 */
function saveWidgets(value) {
    let data = getJournal();
    let dateText = new Date(date.textContent).toLocaleDateString();
    if (value < 6) {
        saveToStorage(data, dateText, "rating", value);
    }
    else {
        saveToStorage(data, dateText, "productivity", value);
    }

    localStorage.setItem("journals", JSON.stringify(data));

    displayWeek();
}

/**
 * Load widget ratings from local storage and update interface
 */
function loadWidgets() {
    let data = getJournal();
    let dateText = new Date(date.textContent).toLocaleDateString();
    let rating = loadFromStorage(data, dateText, "rating");
    let productivity = loadFromStorage(data, dateText, "productivity");

    if (rating != null) {
        selectWidget(rating);
    }
    if (productivity != null) {
        selectWidget(productivity);
    }
}

/**
 * Fetches data from local storage and populates Past Week view
 * @param {HTMLElement} cellData - Data for specified day
 * @param {Date} currWeekDay - Date to populate data within
 */
function loadCellData(cellData, currWeekDay) {
    let journals = getJournal();
    let dateText = currWeekDay.toLocaleDateString();

    let rating = loadFromStorage(journals, dateText, "rating");
    let productivity = loadFromStorage(journals, dateText, "productivity");
    let tasks = loadFromStorage(journals, dateText, "completedTasks");

    if (rating != null) {

        // Add sentiment icon
        let sentimentIcon = document.createElement("img");
        sentimentIcon.src = `../icons/${RATING_FILES_NAMES[rating - 1]}`;
        sentimentIcon.alt = "sentiment icon";
        sentimentIcon.className = "sentiment-icon";

        // Append sentiment icon to new cell
        cellData.appendChild(sentimentIcon);
    }

    if (productivity != null) {

        // Add productivity icon
        let productivityIcon = document.createElement("img");
        productivityIcon.src = `../icons/${PRODUCTIVITY_FILES_NAMES[productivity - 1 - 5]}`;
        productivityIcon.alt = "productivity icon";
        productivityIcon.className = "productivity-icon";

        // Append sentiment icon to new cell
        cellData.appendChild(productivityIcon);
    }

    // Add tasklist in calendar cell
    let taskDiv = document.createElement("div");
    taskDiv.className = "task-div";
    let taskList = document.createElement("ul");
    taskList.className = "task-ul";

    // Format task
    if (tasks != null) {
        for (let i = 0; i < tasks.length && i < DISPLAY_TASK_COUNT; i++) {
            let taskItem = document.createElement("li");
            taskItem.textContent = tasks[i]["text"];
            taskItem.className = "task-item";
            taskItem.style.setProperty('--task-color', tasks[i]["color"]);
            taskList.appendChild(taskItem);
        }

        // Extra tasks are indicated but not displayed
        if (tasks.length > DISPLAY_TASK_COUNT) {
            let taskExtra = document.createElement("li");
            taskExtra.textContent = `${tasks.length - DISPLAY_TASK_COUNT} more tasks`;
            taskExtra.className = "task-indicator";
            taskList.appendChild(taskExtra);
        }
    }

    // Create buttons that link to speciic homepage and extract selected date
    let aLink = document.createElement("a");
    let dayLink = currWeekDay.getDate();
    let monthLink = currWeekDay.getMonth();
    let yearLink = currWeekDay.getFullYear()

    // Query is in format ?date=month-day-year
    aLink.href = `../homepage/homepage.html?date=${monthLink}-${dayLink}-${yearLink}`;
    aLink.className = "a-link";
    aLink.setAttribute("aria-label", `Link to details for ${monthLink + 1}/${dayLink}/${yearLink}`);
    cellData.appendChild(aLink);

    // Append taskList to task div;
    taskDiv.appendChild(taskList);

    // Append tasklist div to new cell
    cellData.appendChild(taskDiv);
}

/**
 * Links calendar cell to homepage on corresponding date
 */
function dateQuery() {
    // Extract query from the page
    let params = new URLSearchParams(window.location.search);
    let date = params.get("date");

    if (date) {
        let components = date.split('-');
        currDate = new Date(components[2], components[0], components[1]);
    }
}

/**
 * Expands task list from collapsed state
 */
function taskListViewHandler() {
    const taskList = document.querySelector('.task-list');
    const taskWrap = document.querySelector('.task-wrapper');
    const outSide = document.querySelector('.main-wrap');

    taskList.addEventListener('click', function(event) {
        if (event.target === taskList) {
            if (window.innerWidth <= 800) { 
                taskList.classList.toggle('active');
                taskWrap.classList.toggle('active');
            }
        }
    });

    outSide.addEventListener('click', function(){
        if (window.innerWidth <= 800) { 
            taskList.classList.remove('active');
            taskWrap.classList.remove('active');
        }
    });
}