import * as THREE from 'three';
import { Font as FontLoader } from 'three/examples/jsm/loaders/FontLoader';
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry';
import { ComponentProps } from 'preact';
import helvetiker_regular from './fonts/helvetiker_regular.typeface.json'; //taken from threejs's website

function getTextGeometry(text: string): TextGeometry {
    //get font
    const Font = new FontLoader(helvetiker_regular);

    const geometry = new TextGeometry(text, {
        font: Font,
        size: 20,
        height: 1,
        curveSegments: 4,
        bevelEnabled: true,
        bevelThickness: 2,
        bevelSize: 1,
        bevelOffset: 0,
        bevelSegments: 2
    });
    geometry.center();
    return geometry;
}

interface runThreeArgs {
    canvas: HTMLCanvasElement;
    text: string;
    zoom?: number;
    scale?: number;
    width?: number;
    height?: number;
}

function runThree({ canvas, text, zoom = 1, scale = 1, width, height }: runThreeArgs) {

    if (!width && !height) {
        return;
    }

    //create scene
    const scene = new THREE.Scene();

    //create text
    const geometry = getTextGeometry(text);
    
    //create mesh from text
    const material = new THREE.MeshNormalMaterial();
    // const material = new THREE.ShaderMaterial({
    //     uniforms: {
    //         'tDiffuse': {
    //             value: null
    //         },
    //         'resolution': {
    //             value: null
    //         },
    //         'pixelSize': {
    //             value: 100
    //         }
    //     },
    //     vertexShader:
    //         /* glsl */
    //         `
    //         varying highp vec2 vUv;
    //         void main() {
    //             vUv = uv;
    //             gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
    //         }`,
    //     fragmentShader:
    //         /* glsl */
    //         `
    //         uniform sampler2D tDiffuse;
    //         uniform float pixelSize;
    //         uniform vec2 resolution;
    //         varying highp vec2 vUv;
    //         void main(){
    //             vec2 dxy = pixelSize / resolution;
    //             vec2 coord = dxy * floor( vUv / dxy );
    //             gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
    //         }`
    // });
    const mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);

    //set canvas size
    {
        //get size of text
        geometry.computeBoundingBox();
        let textW = Math.abs(geometry.boundingBox!.min.x) + Math.abs(geometry.boundingBox!.max.x),
            textH = Math.abs(geometry.boundingBox!.min.y) + Math.abs(geometry.boundingBox!.max.y),
            aspect = textW / textH;

        //calculate width and height
        if (width && !height) {
            height = width / aspect;
        } else if (!width && height) {
            width = height * aspect;
        }

        console.log(width! * scale, height! * scale)

        canvas.width = width! * scale;
        canvas.height = height! * scale;

        canvas.style.width = `${width!}px`;
        canvas.style.height = `${height!}px`;
        
    }

    //create camera
    const Bounds = canvas.getBoundingClientRect();
    const camera = new THREE.PerspectiveCamera(70, Bounds.width / Bounds.height, 0.01, 1000);
    camera.position.set(0, 0, 100 / zoom);

    //set render settings
    const renderer = new THREE.WebGLRenderer({
        antialias: false,
        precision: 'lowp',
        canvas: canvas,
        alpha: true
    });

    renderer.render(scene, camera);
    
    //animate
    renderer.setAnimationLoop(animation);

    function animation(time: number) {

        mesh.rotation.x = time / 500;

        renderer.render(scene, camera);

    }
}

interface Text3DArgs extends ComponentProps<'canvas'> {
    children: string;
    /**
     * zoom < 100
     */
    zoom?: number;
    scale?: number;

    width?: number;
    height?: number;
}

export default function Text3D({ children: text, zoom, scale, width, height, ...props }: Text3DArgs) {

    if (!width && !height) {
        return (null);
    }

    function runRef(canvas: HTMLCanvasElement | null) {

        if (!canvas)
            return;

        runThree({
            canvas, text, zoom, scale, width, height
        });
    }

    return (
        <canvas width={width} height={height} {...props} ref={runRef} />
    )
}
