import * as THREE from 'three';

import {Sky} from 'three/addons/objects/Sky.js';
import {OrbitControls} from 'https://cdn.jsdelivr.net/npm/three@0.118/examples/jsm/controls/OrbitControls.js';
import {GUI} from 'https://cdn.jsdelivr.net/npm/dat.gui@0.7.9/build/dat.gui.module.js';
import {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';
import Stats from 'three/addons/libs/stats.module.js';

import ClickBall from './ClickBall.js';

let camera, scene, renderer;
let sky, sun;
let light, ambientLight;
let controls, stats;
let mesh;

init();
//render();

function initSky(){
    // Add Sky
    sky = new Sky();
    sky.scale.setScalar( 450000 );
    scene.add( sky );

    sun = new THREE.Vector3();

    /// GUI

    const effectController = {
        turbidity: 10,
        rayleigh: 3,
        mieCoefficient: 0.005,
        mieDirectionalG: 0.7,
        elevation: 2,
        azimuth: 180,
        exposure: renderer.toneMappingExposure
    };

    function guiChanged() {

        const uniforms = sky.material.uniforms;
        uniforms[ 'turbidity' ].value = effectController.turbidity;
        uniforms[ 'rayleigh' ].value = effectController.rayleigh;
        uniforms[ 'mieCoefficient' ].value = effectController.mieCoefficient;
        uniforms[ 'mieDirectionalG' ].value = effectController.mieDirectionalG;

        const phi = THREE.MathUtils.degToRad( 90 - effectController.elevation );
        const theta = THREE.MathUtils.degToRad( effectController.azimuth );

        sun.setFromSphericalCoords( 1, phi, theta );

        uniforms[ 'sunPosition' ].value.copy( sun );

        renderer.toneMappingExposure = effectController.exposure;
        renderer.render( scene, camera );

    }

    const gui = new GUI();

    const skyFolder = gui.addFolder('Sky');
    skyFolder.add( effectController, 'turbidity', 0.0, 20.0, 0.1 ).onChange( guiChanged );
    skyFolder.add( effectController, 'rayleigh', 0.0, 4, 0.001 ).onChange( guiChanged );
    skyFolder.add( effectController, 'mieCoefficient', 0.0, 0.1, 0.001 ).onChange( guiChanged );
    skyFolder.add( effectController, 'mieDirectionalG', 0.0, 1, 0.001 ).onChange( guiChanged );
    skyFolder.add( effectController, 'elevation', 0, 90, 0.1 ).onChange( guiChanged );
    skyFolder.add( effectController, 'azimuth', - 180, 180, 0.1 ).onChange( guiChanged );
    skyFolder.add( effectController, 'exposure', 0, 1, 0.0001 ).onChange( guiChanged );

    guiChanged();
}

function initLight(){
    light = new THREE.DirectionalLight(0xFFFFFF, 1.0);
    light.position.set(50, 100, 50);
    light.target.position.set(0, 0, 0);
    light.castShadow = true;
    light.shadow.bias = -0.001;
    light.shadow.mapSize.width = 2048;
    light.shadow.mapSize.height = 2048;

    light.shadow.camera.near = 0.5;
    light.shadow.camera.far = 500.0;
    light.shadow.camera.left = 50;
    light.shadow.camera.right = -50;
    light.shadow.camera.top = 50;
    light.shadow.camera.bottom = -50;

    light.shadow.radius = 5;
    light.shadow.blurSamples = 5;

    scene.add(light);

    //ambientLight = new THREE.AmbientLight(0x303e4c);
    ambientLight = new THREE.AmbientLight(0x475c72);
    scene.add(ambientLight);

    const hemiLight = new THREE.HemisphereLight( 0x5689af, 0xc4806d, 2 );
	//hemiLight.color.setHSL( 0.6, 1, 0.6 ); 5689af 8aa3b6
	//hemiLight.groundColor.setHSL( 0.095, 1, 0.75 );
	hemiLight.position.set( 0, 50, 0 );
	scene.add( hemiLight );
}

function addBoxes(){
    const plane = new THREE.Mesh(
        new THREE.PlaneGeometry(50, 50, 10, 10),
        new THREE.MeshStandardMaterial({
            color: 0xFFFFFF,
          }));
    plane.castShadow = false;
    plane.receiveShadow = true;
    plane.position.set(0, -3, 0);
    plane.rotation.x = -Math.PI / 2;
    scene.add(plane);

    // const box = new THREE.Mesh(
    //     new THREE.BoxGeometry(2, 2, 2),
    //     new THREE.MeshStandardMaterial({
    //         color: 0xFFFFFF,
    //     }));
    // box.position.set(0, 1, 0);
    // box.castShadow = true;
    // box.receiveShadow = true;
    // scene.add(box);

    const geometry = new THREE.BoxGeometry( 3, 3, 3 );
	const material = new THREE.MeshStandardMaterial( { roughness: 0 } );
    
    mesh = new THREE.Mesh( geometry, material );
    mesh.position.set(0, 0, 0);
    mesh.castShadow = true;
	scene.add( mesh );

    //const ball = new ClickBall(scene, camera, -3, 2, 0);
}

function addModel(){
    const loader = new GLTFLoader().setPath('./');
    loader.load('suzanne_subdivided.gltf', async function (gltf) {
        gltf.scene.traverse( node => {
            if(node.isMesh) node.castShadow = true;
        } );
        gltf.scene.traverse( child => {
            if ( child.material ) child.material.metalness = 0;
        } );
        const model = gltf.scene;
        await renderer.compileAsync(model, camera, scene);
        model.scale.setScalar(2);
        scene.add(model);
        render();
    });
}

function init(){
    camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1_000 );
    camera.position.set(0, 2, 10);

    scene = new THREE.Scene();

    renderer = new THREE.WebGLRenderer();
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    renderer.setAnimationLoop( animate );

    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFSoftShadowMap;

    renderer.toneMapping = THREE.ACESFilmicToneMapping;
    //renderer.toneMappingExposure = 0.5;

    document.body.appendChild( renderer.domElement );

    // controls = new OrbitControls(camera, renderer.domElement);
    // controls.addEventListener( 'change', render );
    // //controls.enableZoom = false;
	// controls.enablePan = false;

    initSky();
    initLight();
    addBoxes();
    addModel();

    stats = new Stats();
    document.body.appendChild(stats.dom);

    window.addEventListener( 'resize', onWindowResize );
}

function onWindowResize(){
    camera.aspect = window.innerWidth / window.innerHeight;
	camera.updateProjectionMatrix();

	renderer.setSize( window.innerWidth, window.innerHeight );
    render();
}

function animate(){
    stats.update();
    render();
}

function render(){
    const time = performance.now() * 0.001;
    const scale = 0.1;
	mesh.position.y = (Math.sin( time ) * 20 + 5 ) * scale;
	mesh.rotation.x = time * 0.5;
	mesh.rotation.z = time * 0.51;

    renderer.render(scene, camera);
}