setInterval(getState, 4000);
setInterval(updateProgress, 500); // Update progress every second

/* Global single-flight queue for XMLHttpRequest
   - Serializes all XHR to 1 at a time
   - Adds timeouts (GET ~3.5s, POST ~6s, /upload 10min)
   - Deduplicates idempotent GETs to same URL (drops duplicates)
   This reduces concurrent load on the ESP32 web server and SD card. */
(function(){
  var origOpen = XMLHttpRequest.prototype.open;
  var origSend = XMLHttpRequest.prototype.send;

  var queue = [];
  var active = null;
  var inflightKeys = new Set();

  function keyOf(xhr){ return (xhr.__method || 'GET') + ' ' + (xhr.__url || ''); }

  function startNext(){
    if (active || queue.length === 0) return;
    var item = queue.shift();
    active = item;
    inflightKeys.add(item.key);

    var xhr = item.xhr;
    var timeoutMs = item.timeoutMs;
    var timer = null;

    function cleanup() {
      active = null;
      inflightKeys.delete(item.key);
      if (timer) { clearTimeout(timer); timer = null; }
      setTimeout(startNext, 0);
    }

    xhr.addEventListener('loadend', cleanup);

    if (timeoutMs > 0 && !xhr.__skipTimeout) {
      timer = setTimeout(function(){
        try { xhr.abort(); } catch(e){}
      }, timeoutMs);
    }

    item.origSend.call(xhr, item.body);
  }

  XMLHttpRequest.prototype.open = function(method, url, async){
    this.__method = (method || 'GET').toUpperCase();
    this.__url = url || '';
    return origOpen.apply(this, arguments);
  };

  XMLHttpRequest.prototype.send = function(body){
    var key = keyOf(this);
    var isIdempotentGET = (this.__method === 'GET');

    var timeoutMs;
    if ((this.__url || '').indexOf('/upload') !== -1) {
      timeoutMs = 600000; // 10 minutes for uploads
    } else if ((this.__url || '').indexOf('/directory') !== -1) {
      timeoutMs = 30000; // 30 seconds for directory listing (can be large)
    } else if (this.__method === 'GET') {
      timeoutMs = 6000;
    } else {
      timeoutMs = 8000;
    }

    if (isIdempotentGET && inflightKeys.has(key)) {
      // Drop duplicate GET to same resource
      return;
    }
    if (isIdempotentGET) {
      for (var i = 0; i < queue.length; i++) {
        if (queue[i].key === key) {
          // Already queued; keep most recent body if any
          queue[i].body = body;
          return;
        }
      }
    }

    var item = { xhr: this, body: body, key: key, timeoutMs: timeoutMs, origSend: origSend };
    queue.push(item);
    startNext();
  };
})();

/* Dynamic content loaders for playlist and mapping (avoid heavy template processing on server) */
function bindPlaylistClicks() {
  var container = document.getElementById('playlistContainer');
  if (!container) return;
  container.onclick = function(e) {
    var li = e.target.closest('li');
    if (!li || !container.contains(li)) return;
    var id = li.dataset && li.dataset.id;
    if (id) playSongById(id);
  };
}

function loadDirectory() {
  var container = document.getElementById('playlistContainer');
  if (!container) return;
  var xhr = new XMLHttpRequest();
  xhr.open('GET', '/directory', true);
  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
      if (xhr.status >= 200 && xhr.status < 300) {
        container.innerHTML = xhr.responseText || '';
        bindPlaylistClicks();
      } else {
        container.innerHTML = '<div class="hint">Failed to load playlist.</div>';
      }
    }
  };
  xhr.send();
}

function resetWifi() {
  if (!confirm('Are you sure you want to reset WiFi settings? The device will restart and create an access point.')) {
    return;
  }
  var xhr = new XMLHttpRequest();
  xhr.open('POST', '/reset_wifi', true);
  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
      if (xhr.status >= 200 && xhr.status < 300) {
        alert('WiFi settings reset. Device is restarting...');
      } else {
        alert('Reset failed: ' + (xhr.responseText || 'Unknown error'));
      }
    }
  };
  xhr.send();
}

function loadMapping() {
  var el = document.getElementById('mappingList');
  if (!el) return;
  var xhr = new XMLHttpRequest();
  xhr.open('GET', '/mapping', true);
  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
      if (xhr.status >= 200 && xhr.status < 300) {
        el.innerHTML = xhr.responseText || '';
      } else {
        el.innerHTML = '<div class="hint">Failed to load mapping.</div>';
      }
    }
  };
  xhr.send();
}

/* Kick off dynamic loads on DOM ready */
document.addEventListener('DOMContentLoaded', function() {
  loadDirectory();
  loadMapping();
  getState();
});

var lastChange = 0;

var lastStateUpdateTime = Date.now();
var songStartTime = 0;
var currentSongLength = 0;
var isPlaying = false;
var userIsInteracting = false; // Flag to track user interaction with the slider

// Format seconds into mm:ss or hh:mm:ss when needed
function formatTime(totalSec) {
  totalSec = Number.isFinite(totalSec) ? Math.max(0, Math.floor(totalSec)) : 0;
  var h = Math.floor(totalSec / 3600);
  var m = Math.floor((totalSec % 3600) / 60);
  var s = totalSec % 60;
  function pad(n) { return n < 10 ? '0' + n : '' + n; }
  if (h > 0) return h + ':' + pad(m) + ':' + pad(s);
  return m + ':' + pad(s);
}

// Add click event listener to each <li> element
/* Clicks are handled via event delegation in bindPlaylistClicks() */

function simpleGetCall(endpoint) {
  var xhr = new XMLHttpRequest();    
  xhr.open("GET", "/" + endpoint, true);

  xhr.onreadystatechange = function() {
   if (xhr.readyState === 4) {
     getState(); // Fetch the latest state right after the button action
   }
 };

  xhr.send();
}

function postValue(endpoint,value) {
  var xhr = new XMLHttpRequest();     
  xhr.open("POST", "/" + endpoint, true);
  xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");    
  xhr.send("value="+encodeURIComponent(value));

  xhr.onreadystatechange = function() {
   if (xhr.readyState === 4) {
     getState(); // Fetch the latest state right after the button action
   }
  };     
}

function getState() {
 var xhr = new XMLHttpRequest();
 xhr.onreadystatechange = function() {
   if (xhr.readyState === 4) {
     if (xhr.status >= 200 && xhr.status < 300) {
       try {
         var state = JSON.parse(xhr.responseText || xhr.response || '{}');
         isPlaying = !!state['playing'];
         if (isPlaying) {
           songStartTime = Date.now() - ((state['time'] || 0) * 1000);
           currentSongLength = ((state['length'] || 0) * 1000);
         }
         lastStateUpdateTime = Date.now();
         displayState(state);
       } catch (e) {
         // Ignore parse errors; will retry on next poll
       }
     }
   }
 };
 xhr.open("GET","/state", true);
 xhr.send();
}

function updateProgress() {
 if (isPlaying && !userIsInteracting) { // Check if user is not interacting
   var elapsedTime = Date.now() - songStartTime;
   if (elapsedTime >= currentSongLength) {
     elapsedTime = currentSongLength;
     isPlaying = false; // Stop updating if the song has ended
   }
   var progressElement = document.getElementById('progressSlider');
   progressElement.value = elapsedTime / 1000; // Convert to seconds
   var seconds = Math.floor(elapsedTime / 1000);
   document.getElementById("progressLabel").innerHTML = formatTime(seconds);
 }
}

function displayState(state) {
 var title = state['title'] || '—';
 var titleEl = document.getElementById("state");
 if (titleEl) titleEl.innerHTML = title;
 var bigTitleEl = document.getElementById("stateTitle");
 if (bigTitleEl) bigTitleEl.innerText = title;

 var progressLabel = document.getElementById("progressLabel");
 if (progressLabel) progressLabel.innerHTML = formatTime(Math.floor(state['time'] || 0));

 var progressMax = document.getElementById("progressMax");
 if (progressMax) progressMax.innerHTML = formatTime(Math.floor(state['length'] || 0));

 var voltageEl = document.getElementById("voltage");
 if (voltageEl) voltageEl.innerHTML = (state['voltage'] || '') + ' mV';

 // Update header battery indicator
 var headerBattery = document.getElementById("batteryStatus");
 if (headerBattery) {
   var mv = state['voltage'] || 0;
   if (mv > 0) {
     // Estimate percentage for single cell LiPo (approx 3.3V - 4.2V)
     var pct = Math.round((mv - 3300) / (4200 - 3300) * 100);
     if (pct < 0) pct = 0;
     if (pct > 100) pct = 100;
     
     headerBattery.innerHTML = 
       '<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" style="opacity:0.7"><path d="M16 4h-1V2a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v2H8a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2z"/></svg>' +
       '<span>' + pct + '%</span>';
     headerBattery.title = mv + ' mV';
   } else {
     headerBattery.innerHTML = '';
   }
 }

 var heapEl = document.getElementById("heap");
 if (heapEl) heapEl.innerHTML = (state['heap'] || '') + ' bytes free heap';

 var uidEl = document.getElementById("uid");
 if (uidEl) uidEl.innerHTML = 'Last NFC ID: ' + (state['uid'] || '');

 /* Autofill convenience fields */
 var fm = document.getElementById('fileManager');
 if (state['filepath'] && fm && fm.style.display == 'none') {
   var moveFrom = document.getElementById('moveFrom');
   var deleteFileName = document.getElementById('deleteFileName');
   var song = document.getElementById('song');
   if (moveFrom) moveFrom.value = state['filepath'];
   if (deleteFileName) deleteFileName.value = state['filepath'];
   if (song) song.value = state['filepath'];
 }
 if (state['uid']) {
   var rfidEl = document.getElementById('rfid');
   if (rfidEl) rfidEl.value = state['uid'];
 }

 var btn = document.querySelector('.play-button');
 if (btn) {
   if (state['playing']) {
     btn.classList.add('paused');
   } else {
     btn.classList.remove('paused');
   }
 }
 
 if (Date.now()-lastChange>1200) {
   var progress = document.getElementById('progressSlider');
   if (progress) {
     progress.value = state['time'];
     progress.max = state['length'];
   }

   var volume = document.getElementById('volumeSlider');
   if (volume) volume.value = state['volume'];
 }
 updateProgress();
}

function playSongById(id) {
 var url = "/playbyid";
 var params = "id="+id;
 var http = new XMLHttpRequest();

 http.open("GET", url+"?"+params, true);
 http.onreadystatechange = function()
 {
     if(http.readyState == 4 && http.status == 200) {
       getState();       
     }
 }
 http.send(null);
}

function playNamedSong(song) {
 var xhr = new XMLHttpRequest();
 xhr.open("POST", "/playnamed");
 xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
 var body = song;
 xhr.send("title="+encodeURIComponent(body));
}

function editMapping() {
     var rfid = document.getElementById('rfid').value;
     var song = document.getElementById('song').value;
     var modeEl = document.getElementById('mode');
     var mode = modeEl ? modeEl.value : 's';
     var xhr = new XMLHttpRequest();
     xhr.open("POST", "/edit_mapping", true);
     xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
     xhr.send("rfid=" + encodeURIComponent(rfid) + "&song=" + encodeURIComponent(song) + "&mode=" + encodeURIComponent(mode));
     xhr.onreadystatechange = function() {
       if (xhr.readyState === 4 && xhr.status === 200) {
         alert("Mapping updated successfully!");
       } else if (xhr.readyState === 4) {
         alert("Failed to update mapping: " + (xhr.responseText || xhr.status));
       }
     };
   }

// Validate file before upload
function validateFile(file) {
  var maxSize = 50 * 1024 * 1024; // 50MB limit
  var allowedExtensions = ['.mp3'];
  
  if (file.size > maxSize) {
    return 'File too large. Maximum size is 50MB.';
  }
  
  var fileName = file.name.toLowerCase();
  var hasValidExtension = allowedExtensions.some(ext => fileName.endsWith(ext));
  
  if (!hasValidExtension) {
    return 'Invalid file type';
  }
  
  return null; // No error
}

// Handle form submission with AJAX to show upload status
document.getElementById('uploadForm').addEventListener('submit', function(event) {
  event.preventDefault(); // Prevent the default form submit

  var fileInput = document.getElementById('uploadFile');
  var file = fileInput.files[0];
  
  if (!file) {
    alert('Please select a file to upload.');
    return;
  }
  
  var validationError = validateFile(file);
  if (validationError) {
    alert(validationError);
    return;
  }

  var form = event.target;
  var formData = new FormData(form);
  var uploadButton = document.getElementById('uploadButton');
  var uploadStatus = document.getElementById('uploadStatus');
  var uploadProgress = document.getElementById('uploadProgress');
  var progressFill = document.getElementById('progressFill');
  var progressText = document.getElementById('progressText');

  // Disable upload button and show progress
  uploadButton.disabled = true;
  uploadButton.value = 'Uploading...';
  uploadProgress.style.display = 'block';
  uploadStatus.innerHTML = 'Preparing upload...';

  var xhr = new XMLHttpRequest();
  xhr.open('POST', '/upload', true);

  xhr.upload.onloadstart = function() {
    uploadStatus.innerHTML = 'Upload started...';
  };

  xhr.upload.onprogress = function(event) {
    if (event.lengthComputable) {
      var percentComplete = Math.round((event.loaded / event.total) * 100);
      progressFill.style.width = percentComplete + '%';
      progressText.innerHTML = percentComplete + '%';
      uploadStatus.innerHTML = 'Uploading: ' + percentComplete + '% (' + 
        Math.round(event.loaded / 1024) + 'KB / ' + 
        Math.round(event.total / 1024) + 'KB)';
    }
  };

  xhr.upload.onerror = function() {
    uploadStatus.innerHTML = 'Upload failed due to network error.';
    resetUploadForm();
  };

  xhr.upload.onabort = function() {
    uploadStatus.innerHTML = 'Upload was cancelled.';
    resetUploadForm();
  };

  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) { // Request is done
      if (xhr.status >= 200 && xhr.status < 300) { // Success status code range
        uploadStatus.innerHTML = 'Upload completed successfully!';
        progressFill.style.width = '100%';
        progressText.innerHTML = '100%';
        
        setTimeout(function() {
          alert('File uploaded successfully!');
          location.reload(); // Reload to get updated playlist
        }, 1000);
      } else {
        var errorMsg = xhr.responseText || 'Unknown error occurred';
        uploadStatus.innerHTML = 'Upload failed: ' + errorMsg;
        alert('Upload failed: ' + errorMsg);
        resetUploadForm();
      }
    }
  };

  xhr.send(formData); // Send the form data using XMLHttpRequest
});

function resetUploadForm() {
  var uploadButton = document.getElementById('uploadButton');
  var uploadProgress = document.getElementById('uploadProgress');
  var progressFill = document.getElementById('progressFill');
  var progressText = document.getElementById('progressText');
  
  uploadButton.disabled = false;
  uploadButton.value = 'Upload';
  uploadProgress.style.display = 'none';
  progressFill.style.width = '0%';
  progressText.innerHTML = '0%';
  
  // Clear file input
  document.getElementById('uploadFile').value = '';
}

/* File Manager Functions */

function toggleFileManager() {
  var fm = document.getElementById('fileManager');
  if (fm.style.display === 'none' || fm.style.display === '') {
    fm.style.display = 'block';
  } else {
    fm.style.display = 'none';
  }
}

function moveFile() {
  var from = document.getElementById('moveFrom').value.trim();
  var to = document.getElementById('moveTo').value.trim();
  if (!from || !to) {
    alert('Please provide both source and destination paths.');
    return;
  }
  var xhr = new XMLHttpRequest();
  xhr.open('GET', '/move_file?from=' + encodeURIComponent(from) + '&to=' + encodeURIComponent(to), true);
  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
      if (xhr.status >= 200 && xhr.status < 300) {
        alert('File moved successfully.');
        location.reload();
      } else {
        alert('Move failed: ' + (xhr.responseText || 'Unknown error'));
      }
    }
  };
  xhr.send();
}

function deleteFileOnServer() {
  var filename = document.getElementById('deleteFileName').value.trim();
  if (!filename) {
    alert('Please provide filename to delete.');
    return;
  }
  if (!confirm('Are you sure you want to delete ' + filename + '?')) {
    return;
  }
  var xhr = new XMLHttpRequest();
  xhr.open('GET', '/delete_file?filename=' + encodeURIComponent(filename), true);
  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
      if (xhr.status >= 200 && xhr.status < 300) {
        alert('File deleted successfully.');
        location.reload();
      } else {
        alert('Delete failed: ' + (xhr.responseText || 'Unknown error'));
      }
    }
  };
  xhr.send();
}
