function main(){
  // declare map, query & style
  const map = L.map('map').setView([7, 20], 5); 
  const linesQuery = $('#lines-query').text(); 
  const linesStyle = $('#lines-style').text();
  const pointsQuery = $('#points-query').text(); 
  const pointsStyle = $('#points-style').text();
  const labelsQuery = $('#labels-query').text(); 
  const labelsStyle = $('#labels-style').text();

  // create season selector
  const seasonSelector = $("#season-selector");
	seasonSelector.slider({
	    range: true,
	    min: 1,
	    max: 7,
	    step: 1,
	    values: [ 1, 3 ],
	    classes: {
	      "ui-slider": "slider-bar",
				"ui-slider-handle": "slider-handle",
				"ui-slider-range": "slider-range"
	    },
	    labels: ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII'],
	  });
	  
	  const labels =  $('#season-selector').data().uiSlider.options.labels
	  for (var label of labels) {
	  	const el = $('<label>').addClass('season-label').text(label);
	  	$( "#season-labels" ).append(el);
	  }


  // add dark GoT basemap
  L.tileLayer('https://cartocdn-gusc-d.global.ssl.fastly.net/ramirocartodb/api/v1/map/named/tpl_d44e8b0f_a525_4d23_b93a_71aba54674bc/all/{z}/{x}/{y}.png', {
    maxZoom: 18,
    attribution: '<a href="https://carto.com/attribution">CARTO</a>'
  }).addTo(map);

  map.removeControl(map.zoomControl);

  // create CARTO client
  const client = new carto.Client({
    apiKey: 'default_public',
    username: 'ramirocartodb'
  });

  // create a source, style & lines layer
  const linesSource = new carto.source.SQL(linesQuery);
  const linesCartoCSS = new carto.style.CartoCSS(linesStyle);
  const linesLayer = new carto.layer.Layer(linesSource, linesCartoCSS, {
  	featureOverColumns: ['character', 'distance']
	});

  // create a source, style & labels layer
  const labelsSource = new carto.source.SQL(labelsQuery);
  const labelsCartoCSS = new carto.style.CartoCSS(labelsStyle);
  const labelsLayer = new carto.layer.Layer(labelsSource, labelsCartoCSS);

  // create a source, style & points layer
  // GoT emojis source: https://downloademoji.com/game-of-thrones/
  const pointsSource = new carto.source.SQL(pointsQuery);
  const pointsCartoCSS = new carto.style.CartoCSS(pointsStyle);
  const pointsLayer = new carto.layer.Layer(pointsSource, pointsCartoCSS);

  // add layers to the client
  client.addLayers([linesLayer, labelsLayer, pointsLayer]);

  // add the client layer to the map
  client.getLeafletLayer().addTo(map);

  // add popup
  const popup = L.popup({ closeButton: false });
	linesLayer.on('featureOver', function (featureEvent) {
		popup.setLatLng(featureEvent.latLng);
		const name = featureEvent.data.character;
		const dist = featureEvent.data.distance.toFixed(0);
		popup.setContent(`
			<h2 style="font-size: 14px; font-family: 'MedievalSharp', sans-serif;"> ${name} </h2>
            <p style="font-size: 10px; font-family: 'MedievalSharp', sans-serif;"> ${dist} Km</p>
          `);
		popup.openOn(map);
		console.log(featureEvent.data, featureEvent.distance);
	});

	linesLayer.on('featureOut', function (featureEvent) {
  		popup.removeFrom(map);
	});

  // add character data view
  const charCategory = new carto.dataview.Category(
    linesSource, 'character', {
      limit: 16,
      operation: carto.operation.SUM,
      operationColumn: 'distance'
    });

  const names = [], distances = [];

    charCategory.on('dataChanged', function (newData) {
        console.log('---Distance by character---');
        names.length = 0;
        distances.length = 0;
        for (category of newData.categories){
            names.push(category.name);
            distances.push(category.value);
            console.log(category.name, category.value);
        }
        const topBackgrounds = [];

        // assign color to character name
        for (const name of names) {
            if (name =="Theon") {
                topBackgrounds.push("#F2B701");
            }
            else if (name =="Eddard" || name =="Bran" || name =="Arya" || name =="Sansa" || name =="Jon" || name =="Robb") {
                topBackgrounds.push("#88CCEE");
            }
            else if (name =="Jaime" || name =="Cersei" || name =="Tyrion") {
                topBackgrounds.push("#882255");
            }
            else if (name == "Sam") {
                topBackgrounds.push("#661100");
            }
            else if (name == "Brienne") {
                topBackgrounds.push("#332288");
            }
            else if (name == "Davos") {
                topBackgrounds.push("#888888");
            }
            else if (name == "Daenerys") {
                topBackgrounds.push("#DDCC77");
            }
            else {topBackgrounds.push("#117733");
            }
        }

        // add bar chart widget
        const widget = document.getElementById("chart").getContext("2d");

        const chartOptions = {
            legend: {
                display:false,
            },
            title: {
                display: true,
                text: 'Distance in Kms',
                position: 'bottom',
            },
            scales: {
            yAxes: [{
              barPercentage: 0.2,
              gridLines: {
                    display: false,
              }
            }],
            xAxes: [{
              barPercentage: 0.2,
              gridLines: {
                    display: false,
              }   
            }]
            },
            elements: {
            rectangle: {
              borderSkipped: 'left',
            }
            }
        };

        Chart.defaults.global.defaultFontFamily = "'MedievalSharp', sans-serif";
		Chart.defaults.global.defaultFontSize = 14;

        const myChart = new Chart(widget, {
          type: 'horizontalBar',
          data: {
          labels: names,
          datasets: [{
              label: '',
              data: distances,
              backgroundColor: topBackgrounds,
              borderWidth: 0
            }]
        },
        options: chartOptions
      });

        console.log(names);

    });

  client.addDataview(charCategory);

  // add bbox filters
  const bboxFilter = new carto.filter.BoundingBoxLeaflet(map);
  charCategory.addFilter(bboxFilter);

  /* Slider on change */
  // filter query when selecting season range
  seasonSelector.on("slidechange", function( event, ui ) {
      let seasons = seasonSelector.slider( "values" );
      let rangeSeasons = Array.apply(null, {length: (seasons[1]-seasons[0]+1)}).map(function(value, index){
        return index + seasons[0];
      });
      filterSeason(rangeSeasons); 
      console.log(rangeSeasons);
  });

  const filterSeason = function (season) {
    let filterLinesQuery = linesQuery;
    let filterPointsQuery = pointsQuery;
    if (season) {
      filterLinesQuery = `      
          select 
              row_number() over() as cartodb_id, 
              array_agg(distinct season) as seasons,
              max(color) as color,
              st_makeline(the_geom order by cartodb_id asc) as the_geom, 
              st_length(st_makeline(the_geom order by cartodb_id asc)::geography)*0.138980150292748/1000 as distance,
              st_transform(st_makeline(the_geom order by cartodb_id asc), 3857) as the_geom_webmercator,
              max(house) as house,
              character
          from 
              episodes_locations 
          where season in (${season})
          group by 
              character`;
      filterPointsQuery = `      
          select 
              row_number() over() as cartodb_id, 
              array_agg(distinct season) as seasons,
              max(color) as color,
              st_endpoint(st_makeline(the_geom order by cartodb_id asc)) as the_geom, 
              st_transform(st_endpoint(st_makeline(the_geom order by cartodb_id asc)), 3857) as the_geom_webmercator,
              max(house) as house,
              character
          from 
              episodes_locations 
          where season in (${season})
          group by 
              character`;
    }
    linesSource.setQuery(filterLinesQuery);
    pointsSource.setQuery(filterPointsQuery);
  };

}
window.load = main();