const tableLeftOffset = 1028;
const tableTopOffset = 128;
const baseElementSize = 80;
const initElementSize = 10;
const elementSpacing = 16;
const easeStrength = 4;
const mouseInfluenceDistance = 200;
const mouseInfluencePower = 0.5;
const influenceScale = 1.2;

var currentlySelectedElement = -1;

const elementPositions = [
  [0, 0],
  [17, 0],
  [0, 1],
  [1, 1],
  [12, 1],
  [13, 1],
  [14, 1],
  [15, 1],
  [16, 1],
  [17, 1],
  [0, 2],
  [1, 2],
  [12, 2],
  [13, 2],
  [14, 2],
  [15, 2],
  [16, 2],
  [17, 2],
  [0, 3],
  [1, 3],
  [2, 3],
  [3, 3],
  [4, 3],
  [5, 3],
  [6, 3],
  [7, 3],
  [8, 3],
  [9, 3],
  [10, 3],
  [11, 3],
  [12, 3],
  [13, 3],
  [14, 3],
  [15, 3],
  [16, 3],
  [17, 3],
  [0, 4],
  [1, 4],
  [2, 4],
  [3, 4],
  [4, 4],
  [5, 4],
  [6, 4],
  [7, 4],
  [8, 4],
  [9, 4],
  [10, 4],
  [11, 4],
  [12, 4],
  [13, 4],
  [14, 4],
  [15, 4],
  [16, 4],
  [17, 4],
  [0, 5],
  [1, 5],
  [3, 8],
  [4, 8],
  [5, 8],
  [6, 8],
  [7, 8],
  [8, 8],
  [9, 8],
  [10, 8],
  [11, 8],
  [12, 8],
  [13, 8],
  [14, 8],
  [15, 8],
  [16, 8],
  [17, 8],
  [3, 5],
  [4, 5],
  [5, 5],
  [6, 5],
  [7, 5],
  [8, 5],
  [9, 5],
  [10, 5],
  [11, 5],
  [12, 5],
  [13, 5],
  [14, 5],
  [15, 5],
  [16, 5],
  [17, 5],
  [0, 6],
  [1, 6],
  [3, 9],
  [4, 9],
  [5, 9],
  [6, 9],
  [7, 9],
  [8, 9],
  [9, 9],
  [10, 9],
  [11, 9],
  [12, 9],
  [13, 9],
  [14, 9],
  [15, 9],
  [16, 9],
  [17, 9],
  [3, 6],
  [4, 6],
  [5, 6],
  [6, 6],
  [7, 6],
  [8, 6],
  [9, 6],
  [10, 6],
  [11, 6],
  [12, 6],
  [13, 6],
  [14, 6],
  [15, 6],
  [16, 6],
  [17, 6],
];
var elementSizes = Array(elementPositions.length).fill(initElementSize);
var elementLeftOffsets = Array(elementPositions.length).fill(0);
var elementTopOffsets = Array(elementPositions.length).fill(0);

var mouseX = 0;
var mouseY = 0;

function generateElementPropertyHTMLCode(name, value, unit) {
  return `<div class="element-property-container">
    <div class="element-property-name">${name}</div>
    <div class="element-property-value">${value}${unit}</div>
</div>`;
}

function formatUnit(unitString) {
  if (unitString !== undefined) {
    if (unitString.kelvin !== undefined) {
      return "&nbsp;" + unitString.kelvin;
    }
    return "&nbsp;" + unitString;
  }
  return "";
}

function formatValue(valueString) {
  if (valueString !== undefined) {
    return valueString;
  }
  return "NaN";
}

function formattedCPKColor(hex_color) {
  colorString = "#" + hex_color;
  var r = parseInt(colorString.substr(1, 2), 16); // Grab the hex representation of red (chars 1-2) and convert to decimal (base 10).
  var g = parseInt(colorString.substr(3, 2), 16);
  var b = parseInt(colorString.substr(5, 2), 16);
  (r /= 255), (g /= 255), (b /= 255);
  var max = Math.max(r, g, b),
    min = Math.min(r, g, b);
  var h,
    s,
    l = (max + min) / 2;

  if (max == min) {
    h = s = 0; // achromatic
  } else {
    var d = max - min;
    s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
    switch (max) {
      case r:
        h = (g - b) / d + (g < b ? 6 : 0);
        break;
      case g:
        h = (b - r) / d + 2;
        break;
      case b:
        h = (r - g) / d + 4;
        break;
    }
    h /= 6;
  }

  return `hsl(${h * 360}deg ${s * 100}% 78%)`;
}

function displayElementProperties(elementIndex) {
  if (currentlySelectedElement != elementIndex) {
    currentlySelectedElement = elementIndex;
    properties = [
      "abundance.universe",
      "allotropes",
      "atomic_mass",
      "atomic_number",
      "block",
      "boiling-point",
      "classifications.dot_numbers",
      "conductivity.electric",
      "conductivity.thermal",
      "critical_pressure",
      "critical_temperature",
      "crystal_structure",
      "density.liquid",
      "density.stp",
      "discovered",
      "electron_configuration",
      "electronegativity_pauling",
      "gas_phase",
      "group",
      "hardness.mohs",
      "heat.fusion",
      "heat.molar",
      "heat.vaporization",
      "isotopes_known",
      "isotopes_stable",
      "isotopic_abundances",
      "melting_point",
      "molar_volume",
      "quantum_numbers",
      "radius.empirical",
      "refractive_index",
      "speed_of_sound",
    ];
    document.getElementById("element-data").innerHTML = "";
    document.getElementById("element-data").innerHTML = `
        <div id="element-name"></div>
        <div id="element-appearance"></div>
        <div id="element-properties">
    </div>`;
    document.body.style.backgroundColor = formattedCPKColor(
      pTable[elementIndex].cpk_hex
    );
    document.getElementById("element-name").innerHTML =
      pTable[elementIndex].name;
    document.getElementById("element-appearance").innerHTML =
      pTable[elementIndex].appearance;
    document.getElementById("element-properties").innerHTML = "";
    for (const [key, value] of Object.entries(properties)) {
      if (value.includes(".")) {
        propertySplit = value.split(".");
        propertyName = propertySplit[0] + `(${propertySplit[1]})`;
        try {
          propertyValue =
            pTable[elementIndex][propertySplit[0]][propertySplit[1]];
          propertyUnit = formatUnit(
            pTableUnits[propertySplit[0]][propertySplit[1]]
          );
        } catch {
          propertyValue = undefined;
          propertyUnit = "";
        }
        document.getElementById("element-properties").innerHTML +=
          generateElementPropertyHTMLCode(
            propertyName,
            formatValue(propertyValue),
            propertyUnit
          );
      } else {
        if (value == "discovered") {
          document.getElementById("element-properties").innerHTML +=
            generateElementPropertyHTMLCode(
              value,
              "by " +
                formatValue(pTable[elementIndex][value].by) +
                " in " +
                formatValue(pTable[elementIndex][value].year),
              ""
            );
        } else {
          document.getElementById("element-properties").innerHTML +=
            generateElementPropertyHTMLCode(
              value,
              formatValue(pTable[elementIndex][value]),
              formatUnit(pTableUnits[value])
            );
        }
      }
    }
  }
}

function drawElement(elementIndex) {
  elementLeftOffsets[elementIndex] =
    tableLeftOffset +
    Math.pow(
      elementPositions[elementIndex][0] * (baseElementSize + elementSpacing) +
        elementPositions[elementIndex][1] * 100,
      1.7
    ) /
      100;
  elementTopOffsets[elementIndex] =
    tableTopOffset +
    Math.pow(
      elementPositions[elementIndex][1] * (baseElementSize + elementSpacing),
      1.2
    ) /
      2;
  elementSizes[elementIndex] =
    4 *
    (elementPositions[elementIndex][0] * 2 + elementPositions[elementIndex][1]);
  document.body.innerHTML += `
    <div class="table-element" id="element-index-${elementIndex}" style="left: ${elementLeftOffsets[elementIndex]}px; top: ${elementTopOffsets[elementIndex]}px;">${pTable[elementIndex].symbol}<div class="element-name">${pTable[elementIndex].name}</div><div class="element-z-number">${pTable[elementIndex].atomic_number}</div></div>
    `;
}
var oldTime = new Date();
function updateElements() {
  var deltaTime = new Date() - oldTime;
  for (elementIndex in elementPositions) {
    refLeftOffset =
      tableLeftOffset +
      elementPositions[elementIndex][0] * (baseElementSize + elementSpacing);
    refTopOffset =
      tableTopOffset +
      elementPositions[elementIndex][1] * (baseElementSize + elementSpacing);
    elementLeftOffsets[elementIndex] =
      elementLeftOffsets[elementIndex] +
      (((refLeftOffset - elementLeftOffsets[elementIndex]) / easeStrength) *
        deltaTime) /
        100;
    elementTopOffsets[elementIndex] =
      elementTopOffsets[elementIndex] +
      (((refTopOffset - elementTopOffsets[elementIndex]) / easeStrength) *
        deltaTime) /
        100;
    distFromMouse = Math.sqrt(
      (refLeftOffset + baseElementSize / 2 - mouseX) ** 2 +
        (refTopOffset + baseElementSize / 2 - mouseY) ** 2
    );
    targetSize =
      baseElementSize +
      elementSpacing /
        (influenceScale +
          (distFromMouse / mouseInfluenceDistance) ** mouseInfluencePower);
    elementSizes[elementIndex] =
      elementSizes[elementIndex] +
      (((targetSize - elementSizes[elementIndex]) / easeStrength) * deltaTime) /
        100;
    document.getElementById(`element-index-${elementIndex}`).style.width =
      elementSizes[elementIndex] + "px";
    document.getElementById(`element-index-${elementIndex}`).style.height =
      elementSizes[elementIndex] + "px";
    document.getElementById(`element-index-${elementIndex}`).style.left =
      elementLeftOffsets[elementIndex] +
      (baseElementSize - elementSizes[elementIndex]) / 2 +
      "px";
    document.getElementById(`element-index-${elementIndex}`).style.top =
      elementTopOffsets[elementIndex] +
      (baseElementSize - elementSizes[elementIndex]) / 2 +
      "px";
  }
  oldTime = new Date();
  window.requestAnimationFrame(updateElements);
}

for (elementIndex in elementPositions) {
  drawElement(elementIndex);
}

document.body.addEventListener("mousemove", (event) => {
  mouseX = event.clientX;
  mouseY = event.clientY;
  el = document.elementFromPoint(mouseX, mouseY);
  if (el.parentNode.className == "table-element") {
    displayElementProperties(parseInt(el.parentNode.id.split("-index-")[1]));
  }
  if (el.className == "table-element") {
    displayElementProperties(parseInt(el.id.split("-index-")[1]));
  }
});

displayElementProperties(1);

window.requestAnimationFrame(updateElements);
