// ------VARIABLE DECLARATIONS------
const settings = document.querySelectorAll(".axis-setting")
const variable = document.querySelector(".x-axis-value")
const scope = document.querySelector(".scope")
const exportButton = document.querySelector(".export")
// Get the graph data
const data = graphData["chart_data"]

// Width/height needed for saving image to dashboard
const width = document.getElementById('graph').clientWidth;
const height = document.getElementById('graph').clientHeight;

// Get the iso code for all the countries and pair them with country names for later access
let iso = {}
const countries = Datamap.prototype.worldTopo.objects.world.geometries;
for (let i = 0, j = countries.length; i < j; i++) {
  iso[countries[i].properties.name] = countries[i].id
}

// Set the projections for various parts of the world
const africa = function(element) {
    let projection = d3.geo.equirectangular()
      .center([19, 0])
      .rotate([4.4, 0])
      .scale(400)
      .translate([element.offsetWidth / 2, element.offsetHeight / 2]);
    let path = d3.geo.path()
      .projection(projection);
    return {path: path, projection: projection};
}

const europe = function(element) {
    let projection = d3.geo.mercator()
      .center([20, 56])
      .rotate([0, 0])
      .scale(490)
      .translate([element.offsetWidth / 2, element.offsetHeight / 2]);
    let path = d3.geo.path()
      .projection(projection);
    return {path: path, projection: projection};
}

const southAmerica = function(element) {
    let projection = d3.geo.mercator()
      .center([-65, -24])
      .rotate([0, 0])
      .scale(350)
      .translate([element.offsetWidth / 2, element.offsetHeight / 2]);
    let path = d3.geo.path()
      .projection(projection);
    return {path: path, projection: projection};
}

const northAmerica = function(element) {
    let projection = d3.geo.mercator()
      .center([-90, 45])
      .rotate([0, 0])
      .scale(340)
      .translate([element.offsetWidth / 2, element.offsetHeight / 2]);
    let path = d3.geo.path()
      .projection(projection);
    return {path: path, projection: projection};
}

const asia = function(element) {
    let projection = d3.geo.mercator()
      .center([100, 35])
      .rotate([0, 0])
      .scale(340)
      .translate([element.offsetWidth / 2, element.offsetHeight / 2]);
    let path = d3.geo.path()
      .projection(projection);
    return {path: path, projection: projection};
}

const oceania = function(element) {
    let projection = d3.geo.mercator()
      .center([130, -20])
      .rotate([0, 0])
      .scale(400)
      .translate([element.offsetWidth / 2, element.offsetHeight / 2]);
    let path = d3.geo.path()
      .projection(projection);
    return {path: path, projection: projection};
}

// ------END OF VARIABLE DECLARATIONS------
// ------SET EVENT LISTENERS------
// Add event listener for any axis changes
settings.forEach(setting => {
  setting.onchange = function(){
    render(variable.options[variable.selectedIndex].value);
  }
})

// Runs if variables already set (i.e. if user is choosing to edit rather than create)
if(variable.options[variable.selectedIndex].value != ''){
  render(variable.options[variable.selectedIndex].value)
}

// Export button that allows user to export and download the SVG as a PNG image
exportButton.addEventListener("click", () => {
  let title = document.querySelector(".title").value
  let exportTitle = title == "" ? "plot.png": `${title}.png`
  saveSvgAsPng(document.getElementsByTagName("svg")[0], exportTitle, {scale: 2, backgroundColor: "#FFFFFF"});
})

// ------FUNCTIONS FOR DRAWING THE GRAPH------
function render(chosenVariable){
  // Needed to delete the old graph (if present)
  $("#graph").parent().append( "<div id='graph' class='h-100 position-relative'/>");
  $("#graph").remove();
  // Get the grouped data
  let groupedData = group(chosenVariable);
  // Get the most common country value so that we can base our colour scale of that
  let max = getMax(groupedData)

  // set the legend variables, based on the maximum value. We have 5 in total,
  // so each bin comprises a 5th of the maximum value
  let veryHigh = `- Between ${(max/5)*4} and ${max}`
  let high = `- Between ${(max/5)*3} and ${(max/5)*4 - 1}`
  let medium = `- Between ${(max/5)*2} and ${(max/5)*3 - 1}`
  let low = `- Between ${max/5} and ${(max/5)*2 - 1}`
  let veryLow = `- Less than ${max/5}`

  // Get our colour scale and link it to the values in our legend
  let colourScale = getColourScale(max, veryHigh, high, medium, veryLow, low)

  let result = {};
  // Loop through our data, setting the colour for each
  groupedData.forEach(country => {
    let colour = 'defaultFill'
    if(country.values < max) colour = veryHigh
    if(country.values < (max / 5)*4) colour = high
    if(country.values < (max / 5)*3) colour = medium
    if(country.values < (max / 5)*2) colour = low
    if(country.values < max / 5) colour = veryLow
    let fill = {
      "fillKey": colour,
      "value": country.values
    }
    // Convert the country to 3 letter ISO code (if needed)
    let countryCode = iso[country.key] == undefined ? country.key : iso[country.key]
    result[countryCode] = fill
  })
  // Get the projection and scope of the graph
  // Scope relates to whether it is focused on states of America or countries of the World
  let chosenProjection = getProjection()
  let usa_world = getScope()
  // Draw our map with our colourscale and data
  let map = new Datamap({
      element: document.getElementById('graph'),
      scope: usa_world,
      geographyConfig: {
                // Set the border colour to same colour as the default fill
                highlightBorderColor: '#FC8D59',
                // Customise popup to also display the values/counts if they exist
                popupTemplate: function(geography, data) {
                  if(data == null){
                    return '<div class="hoverinfo"><strong>' + geography.properties.name + '</strong></div>';
                  }
                  return '<div class="hoverinfo"><strong>' + geography.properties.name + ':</strong> ' + data.value +  '</div>';
                }
            },
      projection: 'mercator',
      setProjection: chosenProjection,
      fills: colourScale,
      data: result
  });
  // Add legend to the map
  map.legend();
}

// Groups the data on the chosenVariable/column
function group(chosenVariable){
  // We can create a 'nested' D3 object, with the key as the chosen x-axis variable
  let nestedData = d3.nest().key(function(d) { return d[chosenVariable]; })
  return nestedData
    .rollup(function(v) { return v.length; })
    .entries(data)
}

// Gets the maximum value and then increases it so it is divisible by 5 (for the 5 bins)
function getMax(groupedData){
  let max = 0
  for(let i = 0; i < groupedData.length; i++){
    if(groupedData[i].values > max){
      max = groupedData[i].values
    }
  }
  // we now increase the maximum until it is directly divisble by 5 (as we have 5 bins)
  max++;
  while(max % 5 != 0){
    max++;
  }
  return max
}

// Sets the colour scale of the map
function getColourScale(max, veryHigh, high, medium, veryLow, low){
  // Save as string so that we can insert in javascript variables
  let colourScale = `{
      "${veryLow}": "#DEEDCF",
      "${low}": "#74C67A",
      "${medium}": "#1D9A6C",
      "${high}": "#137177",
      "${veryHigh}": "#0A2F51",
      "defaultFill": "#dae4eb"
    }`
  // Convert to JSON object to be used in the map
  return JSON.parse(colourScale)
}

// Gets the projection of the map, depending on the chosen user input
function getProjection(){
  if(scope.options[scope.selectedIndex].value == "Europe"){
    return europe
  }else if(scope.options[scope.selectedIndex].value == "Africa"){
    return africa
  }else if(scope.options[scope.selectedIndex].value == "Asia"){
    return asia
  }else if(scope.options[scope.selectedIndex].value == "South America"){
    return southAmerica
  }else if(scope.options[scope.selectedIndex].value == "Australia/Oceania"){
    return oceania
  }else if(scope.options[scope.selectedIndex].value == "North America"){
    return northAmerica
  }else{
    return null
  }
}

// Gets the scope (either countries of the world or states of america)
function getScope(){
  if(scope.options[scope.selectedIndex].value == "United States of America"){
    return "usa"
  }else{
    return "world"
  }
}

// Ajax call used to post, as we are also sending the image of the graph (not in form)
$('form').submit(function (e) {
  // prevent default form submission
  e.preventDefault();
  // call function to post form (separate js file)
  postgraph(width, height)
});
