
/**
 * QUERY SECTION
 * 
 *               */

// SPARQL Query text -- 'prefixes' and 'thisUrlParams' defined in people.js; 
let queryNetwork = prefixes + " SELECT COUNT(?event1) AS ?count1 COUNT(?event2) AS ?count2 ?uri2 SAMPLE(?label2) AS ?text \
WHERE { \
{?event1 rdf:type crm:EL1_Exchange_Letters . \
?event_to rdfs:subClassOf ?event1; \
rdf:type crm:EL2_Send_Letter ; \
crm:P01_has_domain ?pc_to . \
?pc_to crm:P02_has_range ?uri . \
?uri rdfs:label ?label . \
?event_from rdfs:subClassOf ?event1; \
rdf:type crm:EL3_Receive_Letter; \
crm:P01_has_domain ?pc_from . \
?pc_from crm:P02_has_range ?uri2 . \
?uri2 rdfs:label ?label2 . \
FILTER (?uri = <" + thisUrlParams.link + ">) \
} UNION { \
?event2 rdf:type crm:EL1_Exchange_Letters . \
?event_to rdfs:subClassOf ?event2; \
rdf:type crm:EL3_Receive_Letter ; \
crm:P01_has_domain ?pc_from . \
?pc_from crm:P02_has_range ?uri . \
?uri rdfs:label ?label . \
?event_from rdfs:subClassOf ?event2; \
rdf:type crm:EL2_Send_Letter; \
crm:P01_has_domain ?pc_to . \
?pc_to crm:P02_has_range ?uri2 . \
?uri2 rdfs:label ?label2 . \
FILTER (?uri = <" + thisUrlParams.link + ">) \
} \
}"

// 'prepareQueryURL' defined in people.js
let queryNet = prepareQueryURL(queryNetwork);

// do the query, call data processor on successful output
$.ajax({
    url: queryNet,
    dataType: "json",
    success: (data) => handle_word_network(data)
});



/**
 * COMPONENT CREATION SECTION
 * 
 *               */

// Master function, takes data output from the query and creates the HTML for the Word Cloud component of the page
function handle_word_network(json){
    // Pre-process data
    let words = processQuery(json);
    // Create page elements
    doListPersonNetwork(words);
    doWordCloud(words);
}


// Return structured object from raw query data for further processing
function processQuery(json) {
  
    const tempArray = [];
    
    $.each(
        json['results']['bindings'],
        (index, value) => {
          let text = value['text']['value'];
          let link = value['uri2']['value'];
          let sent = parseInt(value['count1']['value']);
          let received = parseInt(value['count2']['value']);
          let count = sent + received;
          tempArray.push({'text': text, 'sent': sent, 'received': received, 'count': count, 'hlink': link});
        }
    );

    tempArray.sort((a, b) => b.count - a.count);

    return tempArray;

}


// Create formatted list of people in the network
function doListPersonNetwork(tempArray){

    let ArrayNames = "";

    tempArray.forEach(element => {
        ArrayNames +=
        "<div class='item-place-person'>\
            <div class='item-place-person-label'>" +
                element['text'] +
                "<br /><span class='num_occ'>[Lettere inviate: " + element['sent'] + "]</span>\
                <br /><span class='num_occ'>[Lettere ricevute: " + element['received'] + "]</span>\
                <br /><span class='num_occ'>[Totale corrispondenza: " + element['count'] + "]</span>\
            </div>\
            <div class='item-place-person-action'>\
                <div class='persona' id='" + element['hlink'] +"'>\
                    <i class='fa fa-user' style='cursor:pointer'></i>\
                </div>\
            </div>\
        </div>";            
    });

    document.getElementById("list_person_network").innerHTML = ArrayNames;

}


// Create the word cloud
function doWordCloud(words){
    
    // Clear word cloud div
    $('#myWordCloud').empty();


    // OVERALL GRAPHIC SETTINGS for the cloud container -- id, dimensions, position

    let width = 0.5*window.outerWidth;
    let height = 0.6*width;
  
    // append the svg object to the body of the page
    let svg = d3.select("#myWordCloud")
        .append("svg")
        .attr("id", "wordcloudNetwork")
        .attr("preserveAspectRatio", "xMinYMin meet")
        .attr("viewBox", "0 0 " + width + " " + height)
        .classed("svg-content", true)
        .attr("width", width)
        .attr("height", height)
        .attr("style", "border:1px solid black")

    // In case of empty cloud, draw special page and exit
    if (words.length == 0){
        drawEmptyWordCloud();
        return;
    }
    
    // THE ACTUAL CLOUD
    svg = document.getElementById('wordcloudNetwork');
    console.log(svg);

    let zoom = window.devicePixelRatio;
    console.log('zoom', zoom);

    wcParameters = {
        svgWidth: svg.getBoundingClientRect().width,
        svgHeight: svg.getBoundingClientRect().height,
        maxFontSize: 30/((1+zoom)/2),
        minFontSize: 10/((1+zoom)/2)
    }
    let svgWords = hackWords(words, wcParameters);
    svg.innerHTML = svgWords;

    let wordCloudText = createWordCloud(words, wcParameters);
    svg.innerHTML = wordCloudText;
}

// Helper function -- draw special empty word cloud if there are no occurrences
function drawEmptyWordCloud(){

    let wordIcon = "<div id='users_icon' class='no_info_icon'> \
    <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 512'> \
      <!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d='M319.9 320c57.41 0 103.1-46.56 103.1-104c0-57.44-46.54-104-103.1-104c-57.41 0-103.1 46.56-103.1 104C215.9 273.4 262.5 320 319.9 320zM369.9 352H270.1C191.6 352 128 411.7 128 485.3C128 500.1 140.7 512 156.4 512h327.2C499.3 512 512 500.1 512 485.3C512 411.7 448.4 352 369.9 352zM512 160c44.18 0 80-35.82 80-80S556.2 0 512 0c-44.18 0-80 35.82-80 80S467.8 160 512 160zM183.9 216c0-5.449 .9824-10.63 1.609-15.91C174.6 194.1 162.6 192 149.9 192H88.08C39.44 192 0 233.8 0 285.3C0 295.6 7.887 304 17.62 304h199.5C196.7 280.2 183.9 249.7 183.9 216zM128 160c44.18 0 80-35.82 80-80S172.2 0 128 0C83.82 0 48 35.82 48 80S83.82 160 128 160zM551.9 192h-61.84c-12.8 0-24.88 3.037-35.86 8.24C454.8 205.5 455.8 210.6 455.8 216c0 33.71-12.78 64.21-33.16 88h199.7C632.1 304 640 295.6 640 285.3C640 233.8 600.6 192 551.9 192z'/> \
    </svg> \
    <p>Nessuna persona trovata</p> \
    </div>";

    $("#wordcloudNetwork").css("display", "none");
    $("#references_network").css("display", "none");

    document.getElementById("myWordCloud").innerHTML = wordIcon;

}


/*****************************
* Customized word cloud code *
*****************************/

function hackWords(words, parameters){

    let weights = words.map(word => word.count);
    let minWeight = Math.min(...weights);
    let maxFontSize = parameters.maxFontSize;
    let minFontSize = parameters.minFontSize;
    console.log('weights', weights);
    console.log('maxFontSize', maxFontSize);
    console.log('minFontSize',minFontSize);
    let stringOfWords = "";

    for(let ind=0; ind<words.length; ind++){

        let word = words[ind];
        word.fontSize = getFontSize(words[ind].count, minWeight, maxFontSize, minFontSize);
        stringOfWords = stringOfWords+
        '<text x="100" y ="' + (maxFontSize + maxFontSize*ind).toString() + '" + font-family="Verdana" font-size="'+(word.fontSize)+'" id="word' + ind + '">'+words[ind].text+'</text>\n';
    }
    return stringOfWords;
}


// Takes a list of words, returns the innerHTML for a Word Cloud as a string containing multiple <g> svg elements
function createWordCloud(words, parameters){

    let actualUsefulWidth = parameters.svgWidth;
    let actualUsefulHeight = parameters.svgHeight;
    console.log('Actual SVG dimensions', actualUsefulWidth, actualUsefulHeight);

    maxFontSize = parameters.maxFontSize;
    minFontSize = parameters.minFontSize;

    const rectObjs = []
    let allBooms = 0
    let attempts = 0;
    let outOfBorder = 0;
    for(let ind=0;ind<words.length;ind++){
    //for(let ind=0;ind<3;ind++){

        // Control vars
        if(allBooms>=20*(ind+1)) break;
        attempts++;
        let boom = false;

        // word + bounding box parameters definition
        let word = words[ind];
        let id0 = "word"+ind;
        let wordBB = document.getElementById(id0).getBoundingClientRect();
        let fontSize = word.fontSize;
        let width = wordBB.width;
        let height = wordBB.height;
        let multiplier = 0.1 + 0.4*Math.max((maxFontSize-fontSize)/(maxFontSize-minFontSize), allBooms/(20*(ind+1)));
        let xR = Math.floor(actualUsefulWidth/2-width/2 + randomRange(-multiplier*actualUsefulWidth, multiplier*actualUsefulWidth));
        let yR = Math.floor(actualUsefulHeight/2 + randomRange(-multiplier*actualUsefulHeight, multiplier*actualUsefulHeight));
        //console.log('x', 'y', xR, yR)
        let angle;
        if(word.received/word.sent>3) angle = -3;
        else if(word.received/word.sent<0.3) angle = 3;
        let text = word.text;
        //console.log('Rect:', width, height, xR, yR, angle, text);

        // 
        let newRect = new Rect(width, height, xR, yR, angle, text, fontSize);
        boom = checkBorder(newRect, 0, actualUsefulWidth, 0, actualUsefulHeight)
        if(boom){
            outOfBorder++;
            if(outOfBorder<30){
                ind--;
                continue;
            } else{
                boom = false;
            }
        }
        for(let rect of rectObjs){
            boom = checkColl(newRect, rect);
            if(boom) break;
        }
        if(boom){
            allBooms++;
            ind--;
            continue;
        }
        allBooms = 0;
        rectObjs.push(newRect);
    }
    console.log('Attempted:', attempts);
    console.log('Placed:', rectObjs.length);
    console.log('booms!', allBooms);

    const rectTexts = [];
    rectObjs.forEach(rect => rectTexts.push(rect.printCoords(false, maxFontSize, minFontSize)));
    let rects = rectTexts.join('\n');

    return rects;
}

function getFontSize(weight, minWeight, maxFontSize, minFontSize){
    let r1 = weight/10*minWeight - 1/10; // between 0 and a big num
    let res = minFontSize + (maxFontSize-minFontSize)*r1/Math.sqrt(1+r1**2);
    return res;
}

function randomRange(a, b=0){
    return (a-b)*Math.random()+b;
}
function randomInt(a){
    return Math.floor(a*Math.random());
}
function randomRangeInt(a, b=0){
    let ap = Math.floor(a);
    let bp = Math.floor(b);
    return Math.floor((ap-bp)*Math.random())+bp;
}

// Check border collision
function checkBorder(rect, minX, maxX, minY, maxY){
    if(rect.minX<minX) return true;
    if(rect.maxX>maxX) return true;
    if(rect.minY<minY) return true;
    if(rect.maxY>maxY) return true;
    return false;
}