import { useMemo } from "react";
import { Color, DoubleSide, ShaderMaterial, Vector3, MeshStandardMaterial } from "three";
import useShaderReader from "../../../../utils/scene/useShaderReader";

import edgeVertex from "../../../../assets/shaders/edge/edge-fade.vert.glsl";
import edgeFragment from "../../../../assets/shaders/edge/edge-fade.frag.glsl";

type PolygonMaterialProps = {
    points: { position: [number, number, number] }[];
    color?: string;
    transparent?: boolean;
    depthWrite?: boolean;
    depthTest?: boolean;
    opacity?: number;
    polygonOffset?: boolean;
    polygonOffsetFactor?: number;
    polygonOffsetUnits?: number;
    fadeDistance?: number;
    variant?: "edge" | "none";
};

function PolygonMaterial({
    points,
    color = "white",
    transparent = false,
    depthWrite,
    depthTest,
    opacity = 1,
    polygonOffset = false,
    polygonOffsetFactor,
    polygonOffsetUnits,
    fadeDistance = 2.0,
    variant = "none",
}: Readonly<PolygonMaterialProps>) {
    const vertexShader = useShaderReader(edgeVertex);
    const fragmentShader = useShaderReader(edgeFragment);

    const edges = useMemo(() => {
        return points.map((p, i) => {
            const next = points[(i + 1) % points.length];
            return [new Vector3(...p.position), new Vector3(...next.position)];
        });
    }, [points]);

    const edgeMaterial = useMemo(() => {
        if (!vertexShader || !fragmentShader || variant !== "edge") return null;

        const edgeArray: number[] = [];
        edges.forEach(([a, b]) => {
            edgeArray.push(a.x, a.z, b.x, b.z);
        });

        return new ShaderMaterial({
            side: DoubleSide,
            vertexShader,
            fragmentShader,
            uniforms: {
                uColor: { value: new Color(color) },
                uFadeDistance: { value: fadeDistance },
                uEdges: { value: edgeArray },
                uEdgeCount: { value: edges.length },
            },
            transparent,
            depthWrite,
            depthTest,
            polygonOffset,
            polygonOffsetFactor,
            polygonOffsetUnits,
            opacity,
        });
    }, [color, fadeDistance, vertexShader, fragmentShader, edges]);

    const basicMaterial = useMemo(() => {
        if (variant !== "none") return null;
        return new MeshStandardMaterial({
            color: new Color(color),
            side: DoubleSide,
            transparent,
            depthWrite,
            depthTest,
            polygonOffset,
            polygonOffsetFactor,
            polygonOffsetUnits,
            opacity,
        });
    }, [color]);

    if (variant === "none" && basicMaterial) return <primitive object={basicMaterial} attach="material" />;
    if (variant === "edge" && edgeMaterial) return <primitive object={edgeMaterial} attach="material" />;

    return null;
}

export default PolygonMaterial;
