const imageExtensions = ['gif','jpg','jpeg','png', 'webp', 'svg', 'ico', 'bmp'];
const videoExtensions =['mpg', 'mp2', 'mpeg', 'mpe', 'mpv', 'mp4'];
const audioExtensions =['mp3', 'ogg', 'wav', 'flac'];

const invalidFileNameCharsRe  =/[\\/\|:\*\?"<>]/;   // dont allow \/|:*?<>
const invalidFileNameChars = "\\/|:*?<>";

const mappings = new Map(JSON.parse(localStorage.getItem('mappings')) || []);
let statusLine = document.getElementById('status');
// store for when done with sorting TODO display
let splash = document.getElementById('current-file').innerHTML;
let noFiles = document.getElementById('file-list').innerHTML;

let history = [];

// 
// SERVER COMMUNICATION
//

/**
 * Only show the server response text if its a 500 internal
 */
function showServerError(fname, status, text) {
    if (status == 500) {
        message = `in ${fname}: server returned ${status}: '${text}'`;
    } else {
        message = `in ${fname}: server returned ${status}`;
    }
    statusLine.textContent = message;
    console.error(message);
}


/**
 * get the path in which the images are located and can be loaded from, by appending to them to the stagingPath
 */
let stagingPath = "";
async function setStagingPath() {
    const response = await fetch("imgsort.php?action=getStagingPath");
    text = await response.text();
    if (response.ok) {
        stagingPath = text;
    }
    else {
        showServerError("setStagingPath", response.status, text);
        throw new Error("Can not get staging path");
    }
}

/**
 * list of files to be processed
 */
let fileList = [];
async function setFileList() {
    const response = await fetch("imgsort.php?action=getFileList");
    const text = await response.text();
    // console.log(text);
    if (response.ok) {
        fileList = JSON.parse(text);
        // fileList.sort();
    }
    else {
        showServerError("setFileList", response.status, text);
        throw new Error("Can not get file list");
    }
}



// 
// RENDERING
// 

/*
 * Create button for each mapping
 */
function renderMoveButtons() {
    const buttons = document.getElementById('move-buttons');
    buttons.innerHTML = '';
    mappings.forEach((directory, key) => {
        const moveButton = document.createElement('button');
        moveButton.innerHTML = `<span class="key">${key}</span> <span class="directory">${directory}</span>`;
        moveButton.onclick = function() {
            moveFile(directory);
        };
        moveButton.className = "move-button";
        buttons.appendChild(moveButton);
    });
}


function renderFileList() {
    const fileListDiv = document.getElementById('file-list');
    if (fileList.length == 0) {
        fileListDiv.innerHTML = noFiles;
        return;
    }
    fileListDiv.innerHTML = '';
    fileList.forEach(file => {
        const p = document.createElement('p');
        classes = "file-list-item path";
        if (file === currentFile) {
            classes += " selected-file-list-item";
        }
        p.setAttribute("class", classes)
        p.textContent = file;
        fileListDiv.appendChild(p);
    });
}


let currentFile = "";
let currentFileIdx = null;

function createMediaFileElement(file, autoplay=false) {
    const fileExt = file.split('.').pop().toLowerCase();
    if (imageExtensions.includes(fileExt)) {  // if image
        let img = document.createElement("img");
        img.setAttribute('src', stagingPath + file)
        return img;
    } else if (videoExtensions.includes(fileExt)) {  // if video
        let vid = document.createElement("video");
        if (autoplay) vid.setAttribute('autoplay', true);
        vid.setAttribute('controls', true);
        src = document.createElement("source");
        src.setAttribute("src", stagingPath + file);
        src.setAttribute("type", `video/${fileExt}`);
        vid.appendChild(src);
        return vid;
    } else if (audioExtensions.includes(fileExt)) {  // if audio
        let aud = document.createElement("audio");
        if (autoplay) aud.setAttribute('autoplay', true);
        aud.setAttribute('controls', true);
        src = document.createElement("source");
        src.setAttribute("src", stagingPath + file);
        src.setAttribute("type", `audio/${fileExt}`);
        aud.appendChild(src);
        return aud;
    } else {  // if none of the above
        let p = document.createElement("p");
        p.innerHTML = 'No preview available';
        return p;
    }
}


function renderCurrentFile() {
    // const currentFileNameDiv = document.getElementById('current-file-name');
    // if (!currentFile) {
    //     currentFileNameDiv.innerHTML = "No files in staging";
    // }
    // else {
    //     currentFileNameDiv.innerHTML = currentFile;
    // }
    const currentFileDiv = document.getElementById('current-file');
    currentFileDiv.innerHTML = "";
    if (currentFile) {
        let element = createMediaFileElement(currentFile, true);
        currentFileDiv.appendChild(element);
    } else {
        currentFileDiv.innerHTML = splash;
    }
}


// load media tag of the next file in hidden div, without autoplay
function preloadNextFile() {
    const nextFile = fileList[currentFileIdx+1];
    if (!nextFile) {
        return
    }
    const preloadFileDiv = document.getElementById('preload-file');
    preloadFileDiv.innerHTML = "";
    let element = createMediaFileElement(nextFile, false);
    preloadFileDiv.appendChild(element);
    console.log("Preloaded " + nextFile);
}


// 
// LOGIC
// 

// set the currentFile variable according to currentFileIdx
function setCurrentFile() {
    // check configuration
    if (mappings.size == 0) {
        statusLine.textContent = "No mappings configured - Click 'Configure' first";
        return;
    }
    // if none set and there are files
    if (currentFileIdx == null && fileList.length > 0) {
        currentFileIdx = 0;
    }
    // else if (currentFileIdx < fileList.length) {
    // }
    else if (currentFileIdx >= fileList.length) {
        currentFileIdx = fileList.length - 1;
    }
    // if index is valid
    if (typeof(currentFileIdx) == 'number' && currentFileIdx >= 0 && currentFileIdx < fileList.length) {
        currentFile = fileList[currentFileIdx];
    }
    else {
        currentFileIdx = null;
        currentFile = "";
        statusLine.textContent = "No more files to sort";
    }
    
    // set filename in input field
    const fileNameNoExt = currentFile.replace(/\.[^/.]+$/, "");
    document.getElementById('filename').value = fileNameNoExt;

    renderCurrentFile();
    renderFileList();
}


function setCurrentFileFromName(filename) {
    let idx = fileList.indexOf(filename);
    if (idx < 0) {
        message = `Can not set file from name '${filename}': Not found in list`;
        statusLine.textContent = message;
        console.error(message);
        return;
    }
    currentFileIdx = idx;
    setCurrentFile();
}

// Event delegation
document.getElementById('file-list').addEventListener('click', function(event) {
    if (event.target && event.target.tagName === 'P') {
        // Call the function with the content of the clicked paragraph
        if (event.target.textContent != "No files") {
            setCurrentFileFromName(event.target.textContent);
        }
    }
});



async function moveFile(directory) {
    if (!currentFile) {
        statusLine.textContent = "No file to process!";
        return;
    }
    const userSetFilename = document.getElementById('filename').value;
    if (!userSetFilename) {
        statusLine.textContent = "Filename must not be empty";
        return;
    }
    // file names invalid on windows not handled
    if (invalidFileNameCharsRe.test(userSetFilename)) {
        statusLine.textContent = `Filename must not contain ${invalidFileNameChars}`;
        return;
    }

    // re-add the extension
    const fileExt = currentFile.split('.').pop();
    let filename = userSetFilename + '.' + fileExt;

    let newFilePath = directory + '/' + filename;
    const response = await fetch(`imgsort.php?action=moveFile&file=${escape(currentFile)}&dest=${escape(newFilePath)}`);
    const text = await response.text();
    // console.log(text);
    if (response.ok) {
        if (filename != currentFile) {
            statusLine.textContent = `Moved '${currentFile}' to '${directory}'`;
        } else {
            statusLine.textContent = `Moved '${currentFile}' to '${directory}' as '${filename}'`;
        }
        history.push([currentFile, newFilePath]);
        // remove file from list
        fileList.splice(currentFileIdx, 1);
        // set next file
        setCurrentFile();
        preloadNextFile();
    }
    else {
        showServerError("moveFile", response.status, text);
    }
}


async function undo() {
    if (!history.length > 0) {
        statusLine.textContent = "Nothing to undo"
        return;
    }
    const [file, newFilePath] = history.pop();
    const response = await fetch(`imgsort.php?action=undoFile&file=${escape(file)}&dest=${escape(newFilePath)}`);
    const text = await response.text();
    // console.log(text);
    if (response.ok) {
        statusLine.textContent = `Undo: move '${file}' to '${newFilePath}'`;
        await setFileList()
        setCurrentFile();
    }
    else {
        showServerError("undo", response.status, text);
    }
}


function skip() {
    currentFileIdx++;
    setCurrentFile();
}



async function initialize() {
    // remove the value on reload
    document.getElementById('filename').value = "";
    await setStagingPath();
    await setFileList();
    renderMoveButtons();
    renderFileList();
    // setCurrentFile();
    preloadNextFile();
    statusLine.textContent = "Ready";
}

// load existing mappings on page load
window.onload = function() {
    initialize();
};

// handle keyboard
document.onkeypress = function(e) {
    // dont handle keys while user is typing in filename box
    if (document.querySelector('input') === document.activeElement) {
        return;
    }

    e = e || window.event;
    let keydebug = document.getElementById("keydebug");
    if (keydebug) {
        keydebug.innerHTML = e.key;
    }
    if (e.key == "u") {
        undo();
    }
    else if (e.key == "s") {
        skip();
    }
    else if (mappings.has(e.key)) {
        moveFile(mappings.get(e.key));
    }
}
