/*
  I am incredibly sorry to anyone who is reading this spaghetti code,
  I tried to comment to the best of my ability </3 

  Feel free to suggest ways to make this more efficient 
*/

var advancedSettings = false;

import Pixel from '/pixel.js';

import Dumpy from '/sus.js';
let gifmaker;

let numberOfImpostors = document.getElementById("numberOfImpostors");
let numberOfImpostors_advanced = document.getElementById("numberOfImpostors_advanced");
let gifSpeed = document.getElementById("gifSpeed");
let gifSpeed_advanced = document.getElementById("gifSpeed_advanced");
let enlargeOutput = document.getElementById("enlargeOutput");
let enlargeOutput_advanced = document.getElementById("enlargeOutput_advanced");
let imageInput = document.getElementById("imageInput");
let btnGenerate = document.getElementById("btnGenerate");
let status = document.getElementById("status");
let loader = document.getElementById("loader");
let outputImage = document.getElementById("outputImage");
let btnDownload = document.getElementById("btnDownload");

let choreographed = document.getElementById("choreographed");
let basicC = document.getElementById("basicC");
let rippleC = document.getElementById("rippleC");
let waveC = document.getElementById("waveC");

let colorCollapse = document.getElementById("colorCollapse");
let compressionPercent = document.getElementById("compressionPercent");
let compressionPercent_advanced = document.getElementById("compressionPercent_advanced");
let compressionPercentDisplay = document.getElementById("compressionPercentDisplay");
let compressionPercentContainer = document.getElementById("compressionPercentContainer");

// advanced settings for nerds:

let toggleAdvancedSettings = document.getElementById("toggleAdvancedSettings");

let numWorkers = document.getElementById("numWorkers");
let numWorkersDisplay = document.getElementById("numWorkersDisplay");
let gifQuality = document.getElementById("gifQuality");
let gifQualityDisplay = document.getElementById("gifQualityDisplay");

var startTime;
var endTime;

//when the generate button is clicked...
btnGenerate.addEventListener("click", async function(e) {
  e.preventDefault(); //prevent page from reloading

  gifmaker = new Dumpy(parseInt(numWorkers.value), parseInt(gifQuality.value));
  
  if (imageInput.files.length === 0) {
    alert("Please upload an image to sussify!");
    return;
  }

  startTime = new Date().getTime();

  btnGenerate.disabled = true;
  btnGenerate.textContent = "Generating...";
  
  status.style.display = "block";
  status.textContent = "Loading Image...";

  loader.style.display = "block";

  output.style.display = "none";

  let reader = new FileReader();
  
  reader.onload = async function(event) {
    let img = new Image();
    status.textContent = "Coloring impostors...";
    img.onload = async function() {
      gifmaker.setInterval(advancedSettings ? parseFloat(gifSpeed_advanced.value) : gifSpeed.value);
      gifmaker.setMultiplier(advancedSettings ? parseFloat(enlargeOutput_advanced.value) : enlargeOutput.value / 100);
      gifmaker.setOriginalDimensions(img.height, img.width);
      gifmaker.setChoreographySettings(getChoreographyData());
      gifmaker.setColorCollapseSettings(getColorCollapseData());

      //this will determine the number of impostors we have in the output gif
      let resizedDimensions = resizeImage(img.height, img.width, advancedSettings ? parseInt(numberOfImpostors_advanced.value) : numberOfImpostors.value);
      gifmaker.setDimensions(resizedDimensions.height, resizedDimensions.width);
      
      let tempCanvas = document.createElement("canvas");
      let tempCtx = tempCanvas.getContext("2d");
      tempCanvas.width = resizedDimensions.width;
      tempCanvas.height = resizedDimensions.height;
      tempCtx.drawImage(img, 0, 0, resizedDimensions.width, resizedDimensions.height);
      
      var imgData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
      
      let pixels = [];
      
      for (let i = 0; i < imgData.data.length; i += 4) {
        let pixel = new Pixel(imgData.data[i], imgData.data[i+1], imgData.data[i+2], imgData.data[i+3]);
        pixels.push(pixel);
      }

      if (colorCollapse.checked) {
        for (let i = 0; i < pixels.length; i++) {
          for (let j = i + 1; j < pixels.length; j++) {
            if (Pixel.distance(pixels[i], pixels[j]) < 7) {
              pixels[j].replace(pixels[i]);
            }
          }
        } 
      }
      
      await gifmaker.setPixels(pixels);
      
      status.textContent = "Rendering gif...";
      let gifBlob = await gifmaker.generateGif();

      gifBlob = URL.createObjectURL(gifBlob);
      
      outputImage.width = img.width*(advancedSettings ? parseFloat(enlargeOutput_advanced.value) : enlargeOutput.value / 100);
      outputImage.height = img.height*(advancedSettings ? parseFloat(enlargeOutput_advanced.value) : enlargeOutput.value / 100);
      outputImage.src = gifBlob;
      
      status.style.display = "none";
      loader.style.display = "none";
      document.getElementById("output").style.display = "block"; //make the output container visible
      btnGenerate.disabled = false;
      btnGenerate.textContent = "Generate";

      endTime = new Date().getTime();

      console.log(`Elapsed Time: ${(endTime-startTime)/1000}s`);
    }
    
    img.src = event.target.result;
  }
  reader.readAsDataURL(imageInput.files[0]);
});

btnDownload.addEventListener("click", function() {
  let link = document.createElement("a");
  link.href = outputImage.src;
  link.download = `${imageInput.files[0].name.replace(/\.[^/.]+$/, "")}_${advancedSettings ? parseInt(numberOfImpostors_advanced.value) : numberOfImpostors.value}lsb.gif`;
  
  link.click();
});

//original height and width have to be in pixels
function resizeImage(originalHeight, originalWidth, numberOfImpostors) {
  let newHeight = parseInt(numberOfImpostors);
  /*
    keeping ratios constant:
    newHeight/originalHeight = newWidth/originalWidth
    originalWidth*newHeight/originalHeight = newWidth
  */
  let newWidth = Math.round((originalWidth*newHeight)/(originalHeight));

  return {
    height: newHeight,
    width: newWidth
  }
}



// ---------------------------------------- inputs/displays -------------------------------------

function attachInputEvent(el, callback) {
  for (let event of ["mouseup", "mousedown", "mousemove", "change"]) {
    el.addEventListener(event, callback);
  }
}

//update the number of impostors label when the input value changes
attachInputEvent(numberOfImpostors, () => {
  document.getElementById("numberOfImpostorsDisplay").innerHTML = `Lines of Sussy Bakas: <strong>${numberOfImpostors.value}</strong><br>(higher value = better "resolution" but will take more time to generate)`;
});

attachInputEvent(numberOfImpostors_advanced, () => {
  document.getElementById("numberOfImpostorsDisplay").innerHTML = `Lines of Sussy Bakas: <strong>${numberOfImpostors_advanced.value}</strong><br>(higher value = better "resolution" but will take more time to generate)`;
});

//update the gif speed label when the input value changes
attachInputEvent(gifSpeed, () => {
  document.getElementById("gifSpeedDisplay").textContent = "Gif speed... 😏: " + gifSpeed.value + " ms";
});

attachInputEvent(gifSpeed_advanced, () => {
  document.getElementById("gifSpeedDisplay").textContent = "Gif speed... 😏: " + gifSpeed_advanced.value + " ms";
});

//update the enlarge output label when the input value changes (actual value = input value/100)
attachInputEvent(enlargeOutput, () => {
  if (enlargeOutput.value == 100) {
    document.getElementById("enlargeOutputDisplay").textContent = "Enlarge Output: 1x (Default, no enlargement)";
  } else {
    document.getElementById("enlargeOutputDisplay").textContent = "Enlarge Output: " + (enlargeOutput.value/100).toFixed(2) + "x";
  }
});

attachInputEvent(enlargeOutput_advanced, () => {
  if (enlargeOutput_advanced.value == 1) {
    document.getElementById("enlargeOutputDisplay").textContent = "Enlarge Output: 1x (Default, no enlargement)";
  } else {
    document.getElementById("enlargeOutputDisplay").textContent = "Enlarge Output: " + parseFloat(enlargeOutput_advanced.value).toFixed(2) + "x";
  }
});

// update the numWorkersDisplay label when the input value changes
attachInputEvent(numWorkers, () => {
  numWorkersDisplay.textContent = `Number of workers: ${numWorkers.value}`;
});

// update the gifQualityDisplay label when the input value changes
attachInputEvent(gifQuality, () => {
  gifQualityDisplay.textContent = `Pixel Sample Interval: ${gifQuality.value} (lower = higher quality GIF but will take longer to produce, and vice versa)`
});

// display additional settings when choreographed is checked

choreographed.addEventListener("change", function() {
  if (choreographed.checked) {
    document.getElementById("choreographyTypeContainer").style.display = "block";
  } else {
    document.getElementById("choreographyTypeContainer").style.display = "none";
  }
});

basicC.addEventListener("change", function() {
  if (basicC.checked) {
    document.getElementById("waveDirectionsContainer").style.display = "none";
  }
});

rippleC.addEventListener("change", function() {
  if (rippleC.checked) {
    document.getElementById("waveDirectionsContainer").style.display = "none";
  }
});

waveC.addEventListener("change", function() {
  if (waveC.checked) {
    document.getElementById("waveDirectionsContainer").style.display = "block";
  }
});

// display additional settings when Color Collapse is checked

colorCollapse.addEventListener("change", function() {
  if (colorCollapse.checked) {
    compressionPercentContainer.style.display = "block";
  } else {
    compressionPercentContainer.style.display = "none";
  }
});

//update the compression percent label when the input value changes (actual value = input value/100)
attachInputEvent(compressionPercent, () => {
  compressionPercentDisplay.textContent = `Compression: ${compressionPercent.value}% (higher = less contrast, but faster generation time)`;
});

attachInputEvent(compressionPercent_advanced, () => {
  compressionPercentDisplay.textContent = `Compression: ${compressionPercent_advanced.value}% (higher = less contrast, but faster generation time)`;
});

toggleAdvancedSettings.addEventListener("click", function() {
  advancedSettings = !advancedSettings;
  
  let advancedSettingsElements = Array.from(document.getElementsByClassName("advancedSettings"));
  let basicSettings = Array.from(document.getElementsByClassName("basicSettings"));

  if (advancedSettings) { // if they're being enabled
    numberOfImpostors_advanced.value = numberOfImpostors.value;
    gifSpeed_advanced.value = gifSpeed.value;
    enlargeOutput_advanced.value = enlargeOutput.value/100;
    compressionPercent_advanced.value = compressionPercent.value;
  } else { // if they're being disabled
    numberOfImpostors.value = numberOfImpostors_advanced.value;
    gifSpeed.value = gifSpeed_advanced.value;
    enlargeOutput.value = enlargeOutput_advanced.value*100;
    compressionPercent.value = compressionPercent_advanced.value;
  }

  advancedSettingsElements.forEach(el => {
    if (advancedSettings) {
      el.style.display = "block";
    } else {
      el.style.display = "none";
    }
  });

  basicSettings.forEach(el => {
    if (advancedSettings) {
      el.style.display = "none";
    } else {
      el.style.display = "";
    }
  });

  if (advancedSettings) {
    toggleAdvancedSettings.scrollIntoView({
      behavior: 'instant'
    });
  }
});

// helper function to quickly get choreography data:

function getChoreographyData() {
  let obj = {};

  if (choreographed.checked) {
    obj.choreography = true;
  } else {
    obj.choreography = false;
    return obj;
  }

  if (basicC.checked) {
    obj.type = "basic";    
  } else if (waveC.checked) {
    obj.type = "wave";
    
    if (document.getElementById("horizontal").checked) {
      obj.direction = "horizontal";
    } else if (document.getElementById("vertical").checked) {
      obj.direction = "vertical";
    }
  } else if (rippleC.checked) {
    obj.type = "ripple";
  }

  return obj;
}


// helper function to quickly get color collapse data

function getColorCollapseData() {
  return {
    collapse: colorCollapse.checked,
    compressionPercent: advancedSettings ? compressionPercent_advanced.value : compressionPercent.value
  }
}