import { Backdrop, CircularProgress, Button, Box } from "@mui/material";
import HelpIcon from '@mui/icons-material/Help';
import Tooltip, { tooltipClasses } from '@mui/material/Tooltip';
import Typography from '@mui/material/Typography';
import RepeatIcon from '@mui/icons-material/Repeat';
import { styled } from '@mui/material/styles';

import * as d3 from "d3";
import * as d3Contour from "d3-contour";
import React, { useContext, useEffect, useRef, useState } from "react";

import TextContainer, { topicsSizeFraction } from "./TextContainer";
import { TopicsContext } from "./UploadFileContext";
import QueryView from "./QueryView";

const bunkaDocs = "bunka_docs.json";
const bunkaTopics = "bunka_topics.json";
const { REACT_APP_API_ENDPOINT } = "local";

/**
 * Generic tooltip
 */
export const HtmlTooltip = styled(({ className, ...props }) => (
  <Tooltip {...props} classes={{ popper: className }} />
))(({ theme }) => ({
  [`& .${tooltipClasses.popper}`]: {
    backgroundColor: '#fff',
    color: 'rgba(0, 0, 0, 0.87)',
    maxWidth: 220,
    fontSize: theme.typography.pxToRem(12),
  },
}));

function MapView() {
  const [selectedDocument, setSelectedDocument] = useState(null);
  const [mapLoading, setMapLoading] = useState(false);
  const [topicsCentroids, setTopicsCentroids] = useState([])

  const { data: apiData, isLoading: isFileProcessing } = useContext(TopicsContext);


  const svgRef = useRef(null);
  const scatterPlotContainerRef = useRef(null);
  const createScatterPlot = (data) => {
    const margin = {
      top: 20,
      right: 20,
      bottom: 50,
      left: 50,
    };
    const plotWidth = window.innerWidth * 0.6;
    const plotHeight = window.innerHeight - document.getElementById("top-banner").clientHeight - 50; // Adjust the height as desired

    d3.select(svgRef.current).selectAll("*").remove();

    const svg = d3
      .select(svgRef.current)
      .attr("width", "100%")
      .attr("height", plotHeight);
    /**
    * SVG canvas group on which transforms apply.
    */
    const g = svg.append("g")
      .classed("canvas", true)
      .attr("transform", `translate(${margin.left}, ${margin.top})`);
    /**
    * TODO Zoom.
    */
    const zoom = d3.zoom()
      .scaleExtent([1, 3])
      .translateExtent([[0, 0], [1000, 1000]])
      .on("zoom", function ({ transform }) {
        g.attr(
          "transform",
          `translate(${transform.x ?? 0}, ${transform.y ?? 0}) scale(${transform.k ?? 1})`
        )
        //positionLabels()
        // props.setTransform?.({
        //   x: transform.x,
        //   y: transform.y,
        //   k: transform.k
        // })
      });
    svg.call(zoom);

    /**
     * Initial zoom.
     */
    // const defaultTransform = { k: 1 };
    // const initialTransform = defaultTransform?.k != null
    //   ? new ZoomTransform(
    //     defaultTransform.k ?? 1,
    //     defaultTransform.x ?? 0,
    //     defaultTransform.y ?? 0
    //   )
    //   : d3.zoomIdentity;
    // svg.call(zoom.transform, initialTransform);

    const xMin = d3.min(data, (d) => d.x);
    const xMax = d3.max(data, (d) => d.x);
    const yMin = d3.min(data, (d) => d.y);
    const yMax = d3.max(data, (d) => d.y);

    const xScale = d3
      .scaleLinear()
      .domain([xMin, xMax]) // Use the full range of your data
      .range([0, plotWidth]);

    const yScale = d3
      .scaleLinear()
      .domain([yMin, yMax]) // Use the full range of your data
      .range([plotHeight, 0]);

    // Add contours
    const contourData = d3Contour
      .contourDensity()
      .x((d) => xScale(d.x))
      .y((d) => yScale(d.y))
      .size([plotWidth, plotHeight])
      .bandwidth(30)(
        // Adjust the bandwidth as needed
        data,
      );

    // Define a custom color for the contour lines

    const contourLineColor = "rgb(94, 163, 252)";

    // Append the contour path to the SVG with a custom color
    g
      .selectAll("path.contour")
      .data(contourData)
      .enter()
      .append("path")
      .attr("class", "contour")
      .attr("d", d3.geoPath())
      .style("fill", "none")
      .style("stroke", contourLineColor) // Set the contour line color to the custom color
      .style("stroke-width", 1);

    /*
    const circles = svg.selectAll('circle')
    .data(data)
    .enter()
    .append('circle')
    .attr('cx', (d) => xScale(d.x))
    .attr('cy', (d) => yScale(d.y))
    .attr('r', 5)
    .style('fill', 'lightblue')
    .on('click', (event, d) => {
        // Show the content and topic name of the clicked point in the text container
        setSelectedDocument(d);
        // Change the color to pink on click
        circles.style('fill', (pointData) => (pointData === d) ? 'pink' : 'lightblue');
    });
    */

    const centroids = data.filter((d) => d.x_centroid && d.y_centroid);
    setTopicsCentroids(centroids);

    g
      .selectAll("circle.topic-centroid")
      .data(centroids)
      .enter()
      .append("circle")
      .attr("class", "topic-centroid")
      .attr("cx", (d) => xScale(d.x_centroid))
      .attr("cy", (d) => yScale(d.y_centroid))
      .attr("r", 8) // Adjust the radius as needed
      .style("fill", "red") // Adjust the fill color as needed
      .style("stroke", "black")
      .style("stroke-width", 2)
      .on("click", (event, d) => {
        // Show the content and topic name of the clicked topic centroid in the text container
        setSelectedDocument(d);
      });



    // Add text labels for topic names
    g
      .selectAll("rect.topic-label-background")
      .data(centroids)
      .enter()
      .append("rect")
      .attr("class", "topic-label-background")
      .attr("x", (d) => {
        // Calculate the width of the text
        const first10Words = d.name.split(' ').slice(0, 8).join(' ');
        const textLength = first10Words.length * 8; // Adjust the multiplier for width as needed

        // Calculate the x position to center the box
        return xScale(d.x_centroid) - textLength / 2;
      }) // Center the box horizontally
      .attr("y", (d) => yScale(d.y_centroid) - 20) // Adjust the y position
      .attr("width", (d) => {
        // Compute the width based on the text's length
        const first10Words = d.name.split(' ').slice(0, 8).join(' ');
        const textLength = first10Words.length * 8; // Adjust the multiplier for width as needed
        return textLength;
      })
      .attr("height", 30) // Set the height of the white box
      .style("fill", "white") // Set the white fill color
      .style("stroke", "grey") // Set the blue border color
      .style("stroke-width", 2); // Set the border width

    // Add text labels in black within the white boxes
    g
      .selectAll("text.topic-label-text")
      .data(centroids)
      .enter()
      .append("text")
      .attr("class", "topic-label-text")
      .attr("x", (d) => xScale(d.x_centroid))
      .attr("y", (d) => yScale(d.y_centroid) + 4) // Adjust the vertical position
      .text((d) => {
        const first10Words = d.name.split(' ').slice(0, 8).join(' ');
        return first10Words;
      }) // Use the first 10 words
      .style("text-anchor", "middle") // Center-align the text
      .style("fill", "black"); // Set the text color

    const convexHullData = data.filter((d) => d.convex_hull);

    for (const d of convexHullData) {
      const hull = d.convex_hull;
      const hullPoints = hull.x_coordinates.map((x, i) => [xScale(x), yScale(hull.y_coordinates[i])]);

      g
        .append("path")
        .datum(d3.polygonHull(hullPoints))
        .attr("class", "convex-hull-polygon")
        .attr("d", (d1) => `M${d1.join("L")}Z`)
        .style("fill", "none")
        .style("stroke", "rgba(255, 255, 255, 0.5)") // White with 50% transparency
        .style("stroke-width", 2);
    }

    // Add polygons for topics. Delete if no clicking on polygons
    const topicsPolygons = g
      .selectAll("polygon.topic-polygon")
      .data(centroids)
      .enter()
      .append("polygon")
      .attr("class", "topic-polygon")
      .attr("points", (d) => {
        const hull = d.convex_hull;
        const hullPoints = hull.x_coordinates.map((x, i) => [xScale(x), yScale(hull.y_coordinates[i])]);
        return hullPoints.map((point) => point.join(",")).join(" ");
      })
      .style("fill", "transparent")
      .style("stroke", "transparent")
      .style("stroke-width", 2); // Adjust the border width as needed

    let currentlyClickedPolygon = null;

    topicsPolygons.on("click", (event, d) => {
      // Reset the fill color of the previously clicked polygon to transparent light grey
      if (currentlyClickedPolygon !== null) {
        currentlyClickedPolygon.style("fill", "transparent");
        currentlyClickedPolygon.style("stroke", "transparent");
      }

      // Set the fill color of the clicked polygon to transparent light grey and add a red border
      const clickedPolygon = d3.select(event.target);
      clickedPolygon.style("fill", "rgba(200, 200, 200, 0.4)");
      clickedPolygon.style("stroke", "red");

      currentlyClickedPolygon = clickedPolygon;

      // Display the topic name and content from top_doc_content with a scroll system
      if (d.top_doc_content) {
        // Render the TextContainer component with topic details
        setSelectedDocument(d);
      }
    });
  };

  useEffect(() => {
    if (REACT_APP_API_ENDPOINT === "local" || apiData === undefined) {
      setMapLoading(true);
      // Fetch the JSON data locally
      fetch(`/${bunkaDocs}`)
        .then((response) => response.json())
        .then((localData) => {
          // Fetch the local topics data and merge it with the existing data
          fetch(`/${bunkaTopics}`)
            .then((response) => response.json())
            .then((topicsData) => {
              // Merge the topics data with the existing data
              const mergedData = localData.concat(topicsData);

              // Call the function to create the scatter plot after data is loaded
              createScatterPlot(mergedData);
            })
            .catch((error) => {
              console.error("Error fetching topics data:", error);
            })
            .finally(() => {
              setMapLoading(false);
            });
        })
        .catch((error) => {
          console.error("Error fetching JSON data:", error);
        })
        .finally(() => {
          setMapLoading(false);
        });
    } else {
      // Call the function to create the scatter plot with the data provided by TopicsContext
      createScatterPlot(apiData.docs.concat(apiData.topics));
    }

    // After the data is loaded, set the default topic
    if (apiData && apiData.topics && apiData.topics.length > 0) {
      // Set the default topic to the first topic in the list
      setSelectedDocument(apiData.topics[0]);
    }
  }, [apiData]);


  const mapDescription = "This map is created by embedding documents in a two-dimensional space. Two documents are close to each other if they share similar semantic features, such as vocabulary, expressions, and language. The documents are not directly represented on the map; instead, they are grouped into clusters. A cluster is a set of documents that share similarities. A cluster  is automatically described by a few words that best describes it.";

  return (
    <div className="json-display">
      {(isFileProcessing || mapLoading) ? (
        <Backdrop open={isFileProcessing || mapLoading} style={{ zIndex: 9999 }}>
          <CircularProgress color="primary" />
        </Backdrop>
      ) : (
        <div className="scatter-plot-and-text-container">
          <div className="scatter-plot-container" ref={scatterPlotContainerRef}>
            <HtmlTooltip
              title={
                <React.Fragment>
                  <Typography color="inherit">{mapDescription}</Typography>
                </React.Fragment>
              }
              followCursor
            >
              <HelpIcon style={{
                position: "relative",
                top: 10,
                left: 40,
                border: "none"
              }} />
            </HtmlTooltip>
            <svg ref={svgRef} />
          </div>
          <div className="text-container">
            {selectedDocument ? (
              <TextContainer
                topicName={selectedDocument.name}
                topicSizeFraction={topicsSizeFraction(topicsCentroids, selectedDocument.size)}
                content={selectedDocument.top_doc_content}
              />
            ) : (
              // Display a default view or null if no document is selected
              null
            )}
          </div>

        </div>
      )}
    </div>
  );
}

export default MapView;
