import * as d3 from 'd3'
import {Neo4JAdapter} from '../neo4j/neo'
import {Graph} from '../graph/graph'
import {GraphCreator} from '../graph/graphCreator'
import {v1 as neo4j} from 'neo4j-driver'
import {elements} from '../discussionElements'
import {getIssueTooltipOf, getStatementTooltipOf, getArgumentTooltipOf, getEdgeTooltipOf} from './tooltip/tooltip'

export class Network {
    async showNetwork(query) {
        const graph = await new GraphCreator().fill(new Graph(), query);

        var tooltip = d3.select("body")
            .append("div")
            .attr("class", "tooltip")
            .style("opacity", 0);

        const svg = d3.select('svg'),
            width = +svg.attr('width'),
            height = +svg.attr('height');

        const simulation = d3.forceSimulation()
            .nodes(graph.nodes)
            .force('link', d3.forceLink().id(d => d.id).distance(100))
            .force('charge', d3.forceManyBody())
            .force('center', d3.forceCenter(width / 2, height / 2))
            .velocityDecay(0.3)
            .alphaTarget(1)
            .on('tick', ticked);

        simulation.force('link')
            .links(graph.edges);

        const R = 10;
        let link = svg.selectAll('line')
            .data(graph.edges)
            .enter().append('line');

        link
            .attr('class', 'link')
            .attr('stroke', function (l) {
                if (l.type.includes(elements.REGARDING)) {
                    return "#A9A9A9";
                } else if (l.type.includes(elements.CONCLUDES)) {
                    return "#ce34ff";
                } else if (l.type.includes(elements.PREMISEOF)) {
                    return "#7b6fcc";
                }
            })
            .on('mouseover.tooltip', function (d) {
                tooltip.transition()
                    .duration(300)
                    .style("opacity", 0.8);
                tooltip.html(getEdgeTooltipOf(d))
                    .style("left", (d3.event.pageX) + "px")
                    .style("top", (d3.event.pageY + 10) + "px");
            })
            .on("mouseout.tooltip", function () {
                tooltip.transition()
                    .duration(100)
                    .style("opacity", 0);
            })
            .on('mouseout.fade', fade(1))
            .on("mousemove", function () {
                tooltip.style("left", (d3.event.pageX) + "px")
                    .style("top", (d3.event.pageY + 10) + "px");
            });
        ;
        let node = svg.selectAll('.node')
            .data(graph.nodes)
            .enter().append('g')
            .attr('class', 'node')
            .call(d3.drag()
                .on("start", dragstarted)
                .on("drag", dragged)
                .on("end", dragended));
        ;
        node.append('circle')
            .attr('r', R)
            .attr("fill", function (d) {
                if (d.labels.includes(elements.ATTACK)) {
                    return "#ff0000";
                } else if (d.labels.includes(elements.SUPPORT)) {
                    return "#00ff00";
                } else if (d.labels.includes(elements.STATEMENT)) {
                    return "#ffff00";
                } else if (d.labels.includes(elements.ISSUE)) {
                    return "#0000ff"
                }
            })
            .on('mouseover.tooltip', function (d) {
                tooltip.transition()
                    .duration(300)
                    .style("opacity", .8);
                let info = "";
                if (d.labels.includes("Issue")) {
                    info = getIssueTooltipOf(d);
                } else if (d.labels.includes(elements.STATEMENT)) {
                    info = getStatementTooltipOf(d);
                } else if (d.labels.includes(elements.ARGUMENT)) {
                    info = getArgumentTooltipOf(d);
                }
                tooltip.html(info)
                    .style("left", (d3.event.pageX) + "px")
                    .style("top", (d3.event.pageY + 10) + "px");
            })
            .on('mouseover.fade', fade(0.1))
            .on("mouseout.tooltip", function () {
                tooltip.transition()
                    .duration(100)
                    .style("opacity", 0);
            })
            .on('mouseout.fade', fade(1))
            .on("mousemove", function () {
                tooltip.style("left", (d3.event.pageX) + "px")
                    .style("top", (d3.event.pageY + 10) + "px");
            })
            .on('dblclick', releasenode);

        function ticked() {
            link
                .attr('x1', d => d.source.x)
                .attr('y1', d => d.source.y)
                .attr('x2', d => d.target.x)
                .attr('y2', d => d.target.y);

            node
                .attr('transform', d => `translate(${d.x},${d.y})`)
                .attr("cx", function (d) {
                    return d.x = Math.max(R, Math.min(width - R, d.x));
                })
                .attr("cy", function (d) {
                    return d.y = Math.max(R, Math.min(height - R, d.y));
                });
        }

        function dragstarted(d) {
            if (!d3.event.active) simulation.alphaTarget(0.3).restart();
            d.fx = d.x;
            d.fy = d.y;
        }

        function dragged(d) {
            d.fx = d3.event.x;
            d.fy = d3.event.y;
        }

        function dragended(d) {
            if (!d3.event.active) simulation.alphaTarget(0);
            d.fx = null;
            d.fy = null;

            graph.nodes[0].x = width / 2;
            graph.nodes[0].y = height / 2;

        }

        function releasenode(d) {
            d.fx = null;
            d.fy = null;
        }

        const linkedByIndex = {};
        graph.edges.forEach(d => {
            linkedByIndex[`${d.source.index},${d.target.index}`] = 1;
        });

        function isConnected(a, b) {
            return linkedByIndex[`${a.index},${b.index}`] || linkedByIndex[`${b.index},${a.index}`] || a.index === b.index;
        }

        function fade(opacity) {
            return d => {
                node.style('stroke-opacity', function (o) {
                    const thisOpacity = isConnected(d, o) ? 1 : opacity;
                    this.setAttribute('fill-opacity', thisOpacity);
                    return thisOpacity;
                });

                link.style('stroke-opacity', o => (o.source === d || o.target === d ? 1 : opacity));

            };
        }

        svg.append("g")
            .attr("class", "legendSequential")
            .attr("transform", "translate(" + (width - 100) + "," + (height - 300) + ")");
    }
}