
/* 
  This is the speedometer component.
  It contains the following components:
  - Speedometer
*/
import React, { useState, useRef, useEffect } from 'react'
import ReactSpeedometer from "react-d3-speedometer/slim"
import './Speedometer.scss'
import { SpeedometerRange } from '../../utils/commonFunctions'

interface SpeedometerProps {
    initialValue?: number; // The initial value of the speedometer
    unit?: string; // The unit of the speedometer
    onChange?: (value: number) => void; // The function to call when the value changes
}

/**
 * A speedometer component that displays a value on a circular gauge.
 * @param {SpeedometerProps} props - The props for the speedometer component.
 * @param {number} props.initialValue - The initial value of the speedometer.
 * @param {string} props.unit - The unit of the speedometer.
 * @param {function} props.onChange - The function to call when the value changes.
 */

const Speedometer: React.FC<SpeedometerProps> = ({ initialValue = 50, unit = 'Mbps', onChange }) => {
    const [currentValue, setCurrentValue] = useState(initialValue); // The current value of the speedometer
    const [isDragging, setIsDragging] = useState(false); // Whether the speedometer is being dragged
    const containerRef = useRef<HTMLDivElement>(null); // The ref for the container of the speedometer

    const maxValue = 100; // The maximum value of the speedometer


    /**
     * Calculates the value of the speedometer based on the mouse event.
     * @param {MouseEvent | React.MouseEvent} event - The mouse event.
     */

    const calculateValue = (event: MouseEvent | React.MouseEvent) => {
        if (!containerRef.current) return; // If the container is not found, return

        const rect = containerRef.current.getBoundingClientRect(); // Get the bounding rectangle of the container
        const centerX = rect.left + (rect.width / 2); // Calculate the center of the container
        const centerY = rect.top + (rect.height * 0.8); // Calculate the center of the container

        // Calculate relative position
        const x = event.clientX - centerX; // Calculate the x position of the mouse
        const y = centerY - event.clientY; // Calculate the y position of the mouse

        // Convert to polar coordinates
        let theta = Math.atan2(y, x); // Calculate the angle of the mouse

        // Convert to degrees (0-180)
        let degrees = (theta * (180 / Math.PI)); // Convert the angle to degrees

        // Normalize degrees to 0-180 range
        if (degrees < 0) {
            degrees += 360; // If the angle is less than 0, add 360 to it
        }

        // Clamp degrees between 0 and 180
        degrees = Math.max(0, Math.min(180, degrees)); // Clamp the angle to the range of 0 to 180

        // Map degrees to value (0-100)
        // Using Math.floor to ensure we can reach exactly 100
        let newValue = Math.floor(((180 - degrees) / 180) * maxValue); // Calculate the new value of the speedometer

        // Special case for exact 0 degrees to ensure we can reach 100
        if (degrees <= 0) {
            newValue = maxValue; // If the angle is 0, set the new value to the maximum value
        }

        // Special case for exact 180 degrees to ensure we can reach 0
        if (degrees >= 180) {
            newValue = 0; // If the angle is 180, set the new value to 0
        }

        if (newValue !== currentValue) {
            setCurrentValue(newValue); // Set the new value of the speedometer
            onChange?.(newValue); // Call the onChange function if it is provided
        }
    };

    /**
     * Handles the mouse down event.
     * @param {React.MouseEvent} event - The mouse event.
     */

    const handleMouseDown = (event: React.MouseEvent) => {
        event.preventDefault(); // Prevent the default behavior of the event
        setIsDragging(true); // Set the isDragging state to true
        calculateValue(event); // Calculate the value of the speedometer
    };

    /**
     * Handles the mouse move event.
     * @param {MouseEvent} event - The mouse event.
     */

    const handleMouseMove = (event: MouseEvent) => {
        if (isDragging) {
            event.preventDefault(); // Prevent the default behavior of the event
            calculateValue(event); // Calculate the value of the speedometer
        }
    };

    /**
     * Handles the mouse up event.
     */

    const handleMouseUp = () => {
        setIsDragging(false); // Set the isDragging state to false
    };

    /**
     * Handles the mouse move event.
     */

    useEffect(() => {
        if (isDragging) {
            window.addEventListener('mousemove', handleMouseMove); // Add the mouse move event listener
            window.addEventListener('mouseup', handleMouseUp); // Add the mouse up event listener
        }
        return () => {
            window.removeEventListener('mousemove', handleMouseMove); // Remove the mouse move event listener
            window.removeEventListener('mouseup', handleMouseUp); // Remove the mouse up event listener
        };
    }, [isDragging]);

    return (
        <div
            className="speedometer-container"
            ref={containerRef}
            onMouseDown={handleMouseDown}
        >
            <ReactSpeedometer
                minValue={0} // The minimum value of the speedometer
                maxValue={maxValue} // The maximum value of the speedometer
                value={currentValue} // The current value of the speedometer
                width={285} // The width of the speedometer
                height={160} // The height of the speedometer
                needleHeightRatio={0.7} // The height ratio of the needle
                segments={3} // The number of segments of the speedometer
                customSegmentStops={SpeedometerRange(maxValue, 7)} // The custom segment stops of the speedometer
                segmentColors={[
                    "#F04438", // The color of the first segment
                    "#F04438", // The color of the second segment
                    "#FDB022", // The color of the third segment
                    "#FDB022", // The color of the fourth segment
                    "#17B26A", // The color of the fifth segment
                    "#17B26A", // The color of the sixth segment
                ]}
                ringWidth={15} // The width of the ring
                needleTransitionDuration={isDragging ? 0 : 200} // The transition duration of the needle
                needleTransition="easeQuad" // The transition of the needle
                needleColor={'#101828'} // The color of the needle
                textColor={'#101828'} // The color of the text
                valueTextFontSize="0px" // The font size of the value text
                labelFontSize="14px" // The font size of the label
                startAngle={180} // The start angle of the speedometer
                endAngle={0} // The end angle of the speedometer
                currentValueText="" // The current value text of the speedometer
                maxSegmentLabels={3} // The maximum number of segment labels
                customLabels={SpeedometerRange(maxValue, 7).map(value => value.toString())} // The custom labels of the speedometer
                customLabelPosition="OUTSIDE" // The custom label position of the speedometer
            />
            <div className="value-display">
                {/* The value display of the speedometer */}
                <span className="value">{currentValue.toFixed(2)}</span>
                <span className="unit">{unit}</span>
            </div>
        </div>
    )
}

export default Speedometer