import * as d3 from 'd3';
import { Chart } from '../chart-base/chart';
import { ChartConfiguration } from '../chart-base/chart-configuration';

interface PolarAreaChartDataType {
    key: string;
    value: number;
}

export class PolarAreaChart extends Chart {
    private pie: any;
    private arc: any;
    private outerArc: any;
    private color: any;
    private innerRadius: number;
    tooltip: d3.Selection<HTMLDivElement, unknown, HTMLElement, any>;
    outerRadius: number;

    constructor(_config: ChartConfiguration, _data?: PolarAreaChartDataType[]) {
        super(_config, _data);
        this.initVis();
    }

    protected getDefaultMargins() {
        return { top: 40, right: 130, bottom: 20, left: 130 };
    }

    protected getDefaultContainerSize() {
        return { width: 500, height: 500 };
    }

    protected initVis() {
        const vis = this;

        // Define the pie layout
        vis.pie = d3.pie<any>()
            .value(d => d.value)
            .sort(null);

        // Compute radiius
        vis.outerRadius = Math.min(vis.width, vis.height) / 2 + 45;
        vis.innerRadius = vis.outerRadius * 0.05;

        // Arc for the polar area chart slices
        vis.arc = d3.arc()
            .innerRadius(vis.innerRadius)
            .outerRadius((d: any) => {
                // Scale the outer radius based on the value
                return (d.data.value / d3.max(vis.data, (d: any) => d.value)) * vis.outerRadius;
            });

        vis.outerArc = d3.arc()
            .innerRadius(vis.outerRadius * 1.1)   // bigger than outerRadius
            .outerRadius(vis.outerRadius * 1.1); // to make labels sit outside

        // color scale
        vis.color = d3.scaleOrdinal(d3.schemeCategory10);

        // tooltip
        this.tooltip = d3.select('body').append('div')
            .attr('id', 'polar-area-chart-tooltip')
            .style('opacity', 0)
            .style('position', 'absolute')
            .style('display', 'none')
            .style('background', 'white')
            .style('border', '1px solid black')
            .style('padding', '5px')
            .style('pointer-events', 'none');
    }

    public updateVis() {
        this.renderVis();
    }

    protected renderVis() {
        const vis = this;

        const totalValues = d3.sum(vis.data, (d: any) => d.value);

        //helper function to find the midpoint angle
        function midAngle(d: any) {
            return d.startAngle + (d.endAngle - d.startAngle) / 2;
        }

        vis.chart.selectAll("*").remove();

        // Create group for the chart
        const g = vis.chart.append("g")
            .attr("transform", `translate(${vis.width / 2}, ${vis.height / 2})`);

        // Create pie data
        const pieData = vis.pie(vis.data);

        // Bind data to group elements
        const arcs = g.selectAll(".arc")
            .data(pieData, (d: any) => d.data.key);

        // ENTER
        const arcsEnter = arcs.enter().append("g")
            .attr("class", "arc");

        // The polar area chart slices
        arcsEnter.append("path")
            .attr("fill", (d: any) => vis.color(d.data.key))
            .attr("stroke", "#fff")
            .attr("stroke-width", 4)
            .attr("d", vis.arc)
            .attr("opacity", 0)
            .transition()
            .duration(500)
            .attr("opacity", 1);

        // Paths to connect slices to labels
        arcsEnter.append("path")
            .attr("class", "label-line")
            .attr("stroke", "#222")
            .attr("stroke-width", 2)
            .attr("fill", "none")
            .attr("d", (d: any) => {
                const pos = vis.outerArc.centroid(d);
                const mid = vis.arc.centroid(d);
                // change position based on quarter
                pos[0] = (d.data.value / d3.max(vis.data, (d: any) => d.value)) * vis.outerRadius * (midAngle(d) < Math.PI ? 1.2 : -1.2);
                return `M${mid[0]},${mid[1]} L${pos[0]},${pos[1]}`;
            })
            .transition()
            .duration(500)

        //labels outside the polar area chart
        arcsEnter.append("text")
            .attr("dy", "0.35em")
            .style("font-size", "12px")
            .style("font-weight", 'bold')
            .attr("text-anchor", (d: any) =>
                midAngle(d) < Math.PI ? "start" : "end"
            )
            .attr("transform", (d: any) => {
                const [x, y] = vis.arc.centroid(d);
                return `translate(${x},${y})`;
            })
            .transition()
            .duration(500)
            .attr("transform", (d: any) => {
                const pos = vis.outerArc.centroid(d);
                pos[0] = (d.data.value / d3.max(vis.data, (d: any) => d.value)) * vis.outerRadius * (midAngle(d) < Math.PI ? 1.2 : -1.2) + (midAngle(d) < Math.PI ? 3 : -3);
                return `translate(${pos})`;
            })
            .text((d: any) => d.data.key);

        // mouse events (hover) for arcs
        arcsEnter.select("path")
            .on("mouseover", function (event: any, d: any) {
                d3.select(event.currentTarget).attr("opacity", 1);
                arcsEnter.selectAll("path").filter((arcData: any) =>
                    arcData.data.key !== d.data.key
                ).attr("opacity", 0.4);

                const percentage = ((d.data.value / totalValues) * 100).toFixed(2);
                vis.tooltip.style('opacity', 0.9).style('display', 'block');
                vis.tooltip.html(`${d.data.key}<br>Total Medals: ${d.data.value}<br>Percentage: ${percentage}%`)
                    .style('left', (event.pageX - 20) + 'px')
                    .style('top', (event.pageY - 85) + 'px');
            })
            .on("mousemove", function (event: any) {
                vis.tooltip
                    .style('left', (event.pageX - 20) + 'px')
                    .style('top', (event.pageY - 85) + 'px');
            })
            .on("mouseout", function () {
                arcsEnter.selectAll("path").attr("opacity", 1);
                vis.tooltip.style('opacity', 0).style('display', 'none');
            });

        // To make hover effect on lines also
        arcsEnter.select("path.label-line")
            .on("mouseover", function (event: any, d: any) {
                d3.select(event.currentTarget).attr("opacity", 1);
                arcsEnter.selectAll("path").filter((arcData: any) =>
                    arcData.data.key !== d.data.key
                ).attr("opacity", 0.4);

                const percentage = ((d.data.value / totalValues) * 100).toFixed(2);
                vis.tooltip.style('opacity', 0.9).style('display', 'block');
                vis.tooltip.html(`${d.data.key}<br>Total Medals: ${d.data.value}<br>Percentage: ${percentage}%`)
                    .style('left', (event.pageX - 20) + 'px')
                    .style('top', (event.pageY - 85) + 'px');
            })
            .on("mousemove", function (event: any) {
                vis.tooltip
                    .style('left', (event.pageX - 20) + 'px')
                    .style('top', (event.pageY - 85) + 'px');
            })
            .on("mouseout", function () {
                arcsEnter.selectAll("path").attr("opacity", 1);
                vis.tooltip.style('opacity', 0).style('display', 'none');
            });

        // UPDATE
        arcs.select("path")
            .transition()
            .duration(500)
            .attr("d", vis.arc)
            .attr("fill", (d: any) => vis.color(d.data.key))
            .attr("stroke", "#fff")
            .attr("stroke-width", 1);

        arcs.select("path.label-line")
            .transition()
            .duration(500)
            .attr("d", (d: any) => {
                const pos = vis.outerArc.centroid(d);
                const mid = vis.arc.centroid(d);
                pos[0] = (d.data.value / d3.max(vis.data, (d: any) => d.value)) * vis.outerRadius * (midAngle(d) < Math.PI ? 1.2 : -1.2);
                return `M${mid[0]},${mid[1]} L${pos[0]},${pos[1]}`;
            });

        arcs.select("text")
            .transition()
            .duration(500)
            .attr("text-anchor", (d: any) =>
                midAngle(d) < Math.PI ? "start" : "end"
            )
            .attr("transform", (d: any) => {
                const pos = vis.outerArc.centroid(d);
                pos[0] = (d.data.value / d3.max(vis.data, (d: any) => d.value)) * vis.outerRadius * (midAngle(d) < Math.PI ? 1.2 : -1.2);
                return `translate(${pos})`;
            })
            .text((d: any) => d.data.key);

        // EXIT
        arcs.exit().remove();
    }
}