<html>
<head>
    <meta charset=utf-8 />
    <meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">

    <title>Roundhound visualisation</title>

    <link href="<libs_relative_path>/libs/jquery-ui.min.css" rel="stylesheet">
    <link href="<libs_relative_path>/libs/style.css" rel="stylesheet">

    <script src="<libs_relative_path>/libs/cytoscape.min.js"></script>
    <script src="<libs_relative_path>/libs/jquery-3.6.0.min.js"></script>
    <script src="<libs_relative_path>/libs/jquery.layout-1.4.0.js"></script>
    <script src="<libs_relative_path>/libs/jquery-ui.min.js"></script>
    <script src="<libs_relative_path>/libs/jquery.blockUI.js"></script>

    <!-- cytoscape layouts -->
    <script src="<libs_relative_path>/libs/bluebird.js"></script>
    <script src="<libs_relative_path>/libs/fetch.min.js"></script>
    <script src="<libs_relative_path>/libs/cytoscape-euler.js"></script>

    <script src="<libs_relative_path>/libs/cytoscape-layers.js"></script>
    <script src="<libs_relative_path>/libs/cytoscape-bubblesets.js"></script>

    <script>
        cytoscape.warnings(true)

        var cy; // the graph

        var sample_selector_nodes = {};
        var sample_selector_edges = {};
        var bb = null;
        var sample_to_bb_paths = {};

        function show_sample_hits(sample, colour) {
            if (bb == null) {
                bb = cy.bubbleSets()
            }
            var checkbox = document.getElementById(sample);
            if (checkbox.checked == true) {
                console.log("Showing hits for sample ", sample, colour);
                const bbStyle = {
                    style: {
                        fill: colour,
                        stroke: colour,
                        fillOpacity: 0.5
                    },
                    zIndex: 4,
                    throttle: 10,
                    interactive: true,
                };
                sample_to_bb_paths[sample] = []
                all_nodes = cy.collection()
                sample_selector_nodes[sample].forEach((node_set) => {
                    edges = node_set.edgesWith(node_set)
                    sample_to_bb_paths[sample].push(bb.addPath(node_set, edges, null, bbStyle));
                    all_nodes = all_nodes.union(node_set)
                });
                cy.fit(all_nodes, 50);
            } else {
                console.log("Removing hits for sample ", sample);
                sample_to_bb_paths[sample].forEach((bb_path) => {
                    bb.removePath(bb_path);
                });
                cy.fit();
            }
        }

        function bfs(roots, max_dist) {
          close_nodes = new Set();
          cy.elements().bfs({
              roots: roots,
              visit: function(v, e, u, i, depth){
                if (depth > max_dist){
                    return false;
                }
                close_nodes.add(v)
              },
              directed: false
          });
          return close_nodes;
        }

        function get_layout() {
            layout = cy.layout({
                  name: 'euler',

                  // The ideal length of a spring
                  // - This acts as a hint for the edge length
                  // - The edge length can be longer or shorter if the forces are set to extreme values
                  springLength: edge => 40,

                  // Hooke's law coefficient
                  // - The value ranges on [0, 1]
                  // - Lower values give looser springs
                  // - Higher values give tighter springs
                  springCoeff: edge => 0.00008,

                  // The mass of the node in the physics simulation
                  // - The mass affects the gravity node repulsion/attraction
                  mass: node => 20,

                  // Coulomb's law coefficient
                  // - Makes the nodes repel each other for negative values
                  // - Makes the nodes attract each other for positive values
                  gravity: -1.2,

                  // A force that pulls nodes towards the origin (0, 0)
                  // Higher values keep the components less spread out
                  pull: 0.001,

                  // Theta coefficient from Barnes-Hut simulation
                  // - Value ranges on [0, 1]
                  // - Performance is better with smaller values
                  // - Very small values may not create enough force to give a good result
                  theta: 0.666,

                  // Friction / drag coefficient to make the system stabilise over time
                  dragCoeff: 0.02,

                  // When the total of the squared position deltas is less than this value, the simulation ends
                  movementThreshold: <movementThreshold>,

                  // The amount of time passed per tick
                  // - Larger values result in faster runtimes but might spread things out too far
                  // - Smaller values produce more accurate results
                  timeStep: 20,

                  // The number of ticks per frame for animate:true
                  // - A larger value reduces rendering cost but can be jerky
                  // - A smaller value increases rendering cost but is smoother
                  refresh: 10,

                  // Whether to animate the layout
                  // - true : Animate while the layout is running
                  // - false : Just show the end result
                  // - 'end' : Animate directly to the end result
                  animate: true,

                  // Animation duration used for animate:'end'
                  animationDuration: undefined,

                  // Easing for animate:'end'
                  animationEasing: undefined,

                  // Maximum iterations and time (in ms) before the layout will bail out
                  // - A large value may allow for a better result
                  // - A small value may make the layout end prematurely
                  // - The layout may stop before this if it has settled
                  maxIterations: 100000000,
                  maxSimulationTime: <maxSimulationTime>,

                  // Prevent the user grabbing nodes during the layout (usually with animate:true)
                  ungrabifyWhileSimulating: false,

                  // Whether to fit the viewport to the repositioned graph
                  // true : Fits at end of layout for animate:false or animate:'end'; fits on each frame for animate:true
                  fit: true,

                  // Padding in rendered co-ordinates around the layout
                  padding: 30,

                  // Constrain layout bounds with one of
                  // - { x1, y1, x2, y2 }
                  // - { x1, y1, w, h }
                  // - undefined / null : Unconstrained
                  boundingBox: undefined,

                  // Layout event callbacks; equivalent to `layout.one('layoutready', callback)` for example
                  ready: function(){}, // on layoutready
                  stop: function(){}, // on layoutstop

                  // Whether to randomize the initial positions of the nodes
                  // true : Use random positions within the bounding box
                  // false : Use the current node positions as the initial positions
                  randomize: false
            })
            return layout
        }

        function show_jaccard(cy) {
            old_edges = cy.remove(cy.edges());
            cy.add(new_edges);
            new_edges = old_edges;
        }

        function redraw() {
            $.blockUI({
              message: '<img width="25px" src="<libs_relative_path>/libs/resources/images/busy.gif" /> Loading resources and drawing the graph<br/>Please wait (max drawing time is <maxSimulationTime> milisseconds)...' ,
              css: {
                    border: 'none',
                    padding: '15px',
                    backgroundColor: '#000',
                    '-webkit-border-radius': '10px',
                    '-moz-border-radius': '10px',
                    opacity: .5,
                    color: '#fff'
                }
            });

            // creates graph
            cy = cytoscape({
                container: $('#cy'),
                elements: <elements_tag>,
                style: [ // the stylesheet for the graph
                    {
                        selector: 'node',
                        style: {
                            'background-color': 'data(color)',
                            'color': 'data(color)',
                            'label': 'data(name)',
                            'z-index': 10,
                            'width': 'data(size)',
                            'height': 'data(size)',
                            'shape': 'data(shape)',
                            'is_blackhole': 'data(is_blackhole)'
                        },
                    },
                    {
                        selector: 'edge',
                        style: {
                            'width': '2',
                            'z-index': 10,
                            'label': 'data(d_lbl)',
                            'font-size': 16,
                            'color': 'data(color)',
                        },
                    },
                ],
            });

            <samples_selectors>

            // hides blackholes
            hide_blackholes = $("#hide_blackholes").is(":checked")
            if (hide_blackholes) {
                blackholes = cy.elements(`node[is_blackhole > 0]`)
                console.log("Removing the following blackholes:")
                console.log(blackholes)
                cy.remove(blackholes)
            }

            // lays out the graph
            var layout = get_layout(cy)

            //event when finishing the layout
            layout.on('layoutstop', function(){
                //we are ready!
                $.unblockUI({
                    fadeOut: 0
                });
                cy.forceRender();
            })

            //run the layout
            layout.run();
        }

        $( document ).ready(function() {
            redraw();
        });
    </script>
</head>

<body>
<div id="cy" style="width: 80%; height: 100%;"></div>

<div id="config" class="config ui-layout-east">
    <div class="preamble">
        <div>
              <p>Legend</p>
              <p>Node colours:</p>
              <p>Each different colour represents a different plasmid community.</p>
              <p>Due to the lack of enough colours, different plasmid communities will sometimes have same colours.</p>
              <p>But hopefully they won't be connected, so we can infer they are still different communities.</p>
              <p>Node shapes:</p>
              <p>&#9679; Normal plasmids</p>
              <p>&#9733; Blackhole plasmids</p>
        </div>
        <div>
                <br/><br/>
                Filters: <br/>
                <filters_tag>
        </div>

        <br/><br/>
        <custom_buttons_tag>
    </div>
</div>

</body>

</html>