import * as THREE from 'three';
import { useMemo, useRef, useState } from 'react';
import * as Constants from '../../../../../types/world/worldConstants';

import insideMaterial from '../../../../../assets/textures/floor/wall-tex.png';
import outsideMaterial from '../../../../../assets/textures/floor/factory wall texture.jpg';
import useWallGeometry from './helpers/useWallGeometry';
import { useWallStore } from '../../../../../store/builder/useWallStore';
import { useWallClassification } from './helpers/useWallClassification';
import { useFrame, useThree } from '@react-three/fiber';
import { useWallVisibility } from '../../../../../store/builder/store';
import { Decal } from '@react-three/drei';
import { Base } from '@react-three/csg';

function Wall({ wall }: { readonly wall: Wall }) {
    const { walls } = useWallStore();
    const { getWallType } = useWallClassification(walls);
    const wallType = getWallType(wall);
    const [startPoint, endPoint] = wall.points;
    const [visible, setVisible] = useState(true);
    const { wallVisibility } = useWallVisibility();
    const meshRef = useRef<any>();
    const { camera } = useThree();

    const startX = startPoint.position[0];
    const startZ = startPoint.position[2];
    const endX = endPoint.position[0];
    const endZ = endPoint.position[2];

    const wallLength = Math.sqrt((endX - startX) ** 2 + (endZ - startZ) ** 2);
    const angle = Math.atan2(endZ - startZ, endX - startX);

    const centerX = (startX + endX) / 2;
    const centerZ = (startZ + endZ) / 2;
    const centerY = wall.wallHeight / 2;

    const textureLoader = new THREE.TextureLoader();

    const [insideWallTexture, outsideWallTexture] = useMemo(() => {
        const inside = textureLoader.load(insideMaterial);
        inside.wrapS = inside.wrapT = THREE.RepeatWrapping;
        inside.repeat.set(wallLength / 10, wall.wallHeight / 10);
        inside.colorSpace = THREE.SRGBColorSpace;

        const outside = textureLoader.load(outsideMaterial);
        outside.wrapS = outside.wrapT = THREE.RepeatWrapping;
        outside.repeat.set(wallLength / 10, wall.wallHeight / 10);
        outside.colorSpace = THREE.SRGBColorSpace;

        return [inside, outside];
    }, [wallLength, wall.wallHeight]);

    const materials = useMemo(() => {
        const frontMaterial = insideWallTexture;

        const backMaterial = outsideWallTexture;

        return [
            new THREE.MeshStandardMaterial({
                color: Constants.wallConfig.defaultColor,
                side: THREE.DoubleSide,
                map: frontMaterial
            }),
            new THREE.MeshStandardMaterial({
                color: Constants.wallConfig.defaultColor,
                side: THREE.DoubleSide,
                map: backMaterial
            }),
            new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide }),  // Left
            new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide }),  // Right
            new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide }),  // Top
            new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide })  // Bottom
        ];
    }, [insideWallTexture, outsideWallTexture, wall]);

    const geometry = useWallGeometry(wallLength, wall.wallHeight, wall.wallThickness);

    useFrame(() => {
        if (!meshRef.current) return;
        const v = new THREE.Vector3();
        const u = new THREE.Vector3();

        if (!wallVisibility && wallType.type === 'room') {
            meshRef.current.getWorldDirection(v);
            camera.getWorldDirection(u);
            setVisible((2 * v.dot(u)) <= 0.1);

        } else {
            setVisible(true);
        }
    })

    return (
        <group
            name={`Wall-${wall.wallUuid}`}
            userData={wall}
            position={[centerX, centerY, centerZ]}
            rotation={[0, -angle, 0]}
            visible={visible}
        >
            <Base ref={meshRef} geometry={geometry} visible>
                {materials.map((material, index) => (
                    <primitive key={index} object={material} attach={`material-${index}`} />
                ))}

                {wall.decals.map((decal) => {
                    return (
                        <Decal
                            // debug
                            position={[decal.decalPosition[0], decal.decalPosition[1], wall.wallThickness / 2]}
                            rotation={[0, 0, decal.decalRotation]}
                            scale={[decal.decalScale, decal.decalScale, 0.001]}
                        >
                            <meshBasicMaterial
                                map={outsideWallTexture}
                                side={THREE.DoubleSide}
                                polygonOffset
                                polygonOffsetFactor={-1}
                            />
                        </Decal>
                    )
                })}
            </Base>
        </group>
    );
}

export default Wall;