import * as THREE from 'three';
import { KeyCode } from './KeyCode.js';
import { Bee } from './Bee.js';
import { Hive } from './Hive.js';
import { Flower } from './Flower.js';
import { Waypoint } from './Waypoint.js';

// scene size
var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;

// camera
var VIEW_ANGLE = 45;
var ASPECT = WIDTH / HEIGHT;
var NEAR = 1;
var FAR = 10000;

var camera, scene, renderer, raycaster;
var clock = new THREE.Clock();
var intersects;

export var keys = [];
var mouse, dragStart, dragOffset;

var selected;
var ground;
var draggingOnGround;

function init() {
    for (let i = 0; i < 256; i++) {
        keys[i] = false;
    }

    raycaster = new THREE.Raycaster();
    mouse = new THREE.Vector2(-1, -1);
    dragStart = new THREE.Vector3(0, 0, 0);
    dragOffset = new THREE.Vector3(0, 0, 0);
    intersects = [];

    // renderer
    renderer = new THREE.WebGLRenderer({
        antialias: true,
    });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(WIDTH, HEIGHT);
    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    renderer.outputColorSpace = THREE.SRGBColorSpace;
    renderer.alias

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

    // camera
    camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
    camera.focus = new THREE.Vector3(0, 0, 0);
    camera.focusTarget = new THREE.Vector3(0, 0, 0);
    camera.offset = new THREE.Vector3(160, 220, 160);
    camera.position.set(camera.focus.x + camera.offset.x, camera.focus.y + camera.offset.y, camera.focus.z + camera.offset.z);
    camera.follow = false;
    camera.lookAt(camera.focus);
    draggingOnGround = false;

    var container = document.getElementById('container');
    container.appendChild(renderer.domElement);
}

function fillScene() {

    //sky
    let cubeloader = new THREE.CubeTextureLoader();
    let texture = cubeloader.load([
        './cubemapsimple/posx.png',
        './cubemapsimple/negx.png',
        './cubemapsimple/posy.png',
        './cubemapsimple/negy.png',
        './cubemapsimple/posz.png',
        './cubemapsimple/negz.png',
    ]);
    scene.background = texture;

    //ground
    let textureLoader = new THREE.TextureLoader();
    let lawnTex = textureLoader.load("textures/lawn.jpg");
    var planeGeo = new THREE.PlaneGeometry(1000, 1000, 32, 32);
    let floorMat = new THREE.MeshPhongMaterial({ map: lawnTex });
    floorMat.flatShading = true;
    floorMat.shininess = 0;
    lawnTex.wrapS = lawnTex.wrapT = THREE.RepeatWrapping;
    lawnTex.repeat.set(5, 5);
    ground = new THREE.Mesh(planeGeo, floorMat);
    ground.rotateX(- Math.PI / 2);
    ground.receiveShadow = true;
    scene.add(ground);

    // lights

    var sunlight = new THREE.DirectionalLight(0xffffff, 2.8, 0, 2);
    sunlight.position.x = 400;
    sunlight.position.y = 400;
    sunlight.position.z = 100;
    const shadowSize = 1000
    sunlight.castShadow = true
    sunlight.shadow.camera.left = -shadowSize
    sunlight.shadow.camera.right = shadowSize
    sunlight.shadow.camera.top = -shadowSize
    sunlight.shadow.camera.bottom = shadowSize
    sunlight.shadow.camera.far = 800
    sunlight.shadow.mapSize.width = Math.min(renderer.capabilities.maxTextureSize, 2048)
    sunlight.shadow.mapSize.height = Math.min(renderer.capabilities.maxTextureSize, 2048)
    sunlight.shadow.bias = -0.005
    sunlight.shadow.radius = 6
    //sunlight.shadow.camera.far = 1600;
    scene.add(sunlight);

    var mainLight = new THREE.HemisphereLight(0xB1E1FF, 0xB97A20, 0.6);
    mainLight.position.x = 1;
    mainLight.position.y = 1;
    mainLight.position.z = 1;
    mainLight.lookAt(0, 0, 0);
    scene.add(mainLight);

    // var light = new THREE.AmbientLight(0xcccccc, 1);
    // scene.add(light);

    //set pieces
    Hive.collection = [];
    Flower.collection = [];
    Bee.collection = [];

    Promise.all([Hive.loadModel(), Flower.loadModel(), Bee.loadModel()]).then(async result => {

        for (let i = 0; i < 3; i++) {
            let hive = new Hive(scene);
            await hive.load(result[0]);
            hive.init(i);
            Hive.collection.push(hive);
        }

        Hive.collection.forEach(hive => hive.addToScene(scene));

        for (let i = 0; i < 24; i++) {
            let flower = new Flower();
            await flower.load(result[1]);
            flower.position.x = ((i % 6) / 6 * 400) - 200;
            flower.position.z = (Math.floor(i / 6) / 6 * 300) + 150;
            flower.rotation.y = (2 * Math.PI) * Math.random();

            Flower.collection.push(flower);
        }

        Flower.collection.forEach(flower => flower.addToScene(scene));

        Bee.baseModel = result[2];
        Hive.collection[0].setSwarm(1);
        Hive.collection[0].honeyReserve = 20;
        Hive.collection[0].toggleFlower(Flower.collection[0]);
        Hive.collection[0].spawnBee();

        Hive.collection[2].setSwarm(2);
        Hive.collection[2].honeyReserve = 20;
        Hive.collection[2].toggleFlower(Flower.collection[5]);
        Hive.collection[2].spawnBee();


        for (let i = 0; i < Waypoint.collection.length; i++) {
            let waypoint = Waypoint.collection[i];
            waypoint.rebuildLines();
        }
        update();
    });
}

function setFocus(focus) {
    selected = focus;
    camera.focusTarget.set(selected.position.x, selected.position.y, selected.position.z);
    camera.follow = true;
}

function render() {
    renderer.render(scene, camera);
}

function update() {

    window.requestAnimationFrame(update);

    var delta = clock.getDelta();
    var timer = Date.now();

    let origin = new THREE.Vector3(0, camera.offset.y, 0);
    let distance = camera.offset.distanceTo(origin);
    var delta = camera.offset.clone().sub(origin);
    var theta = Math.atan2(delta.z, delta.x);
    let cameraSpeed = 3;

    if (keys[KeyCode.Left] || keys[KeyCode.A]) { //left
        let vector = new THREE.Vector3();
        camera.getWorldDirection(vector);
        vector.multiply(new THREE.Vector3(cameraSpeed, 0, -cameraSpeed));
        camera.focusTarget.add(vector);
    }
    if (keys[KeyCode.Right] || keys[KeyCode.D]) { //right
        let vector = new THREE.Vector3();
        camera.getWorldDirection(vector);
        vector.multiply(new THREE.Vector3(-cameraSpeed, 0, cameraSpeed));
        camera.focusTarget.add(vector);
    }

    if (keys[KeyCode.Up] || keys[KeyCode.W]) { //up
        let vector = new THREE.Vector3();
        camera.getWorldDirection(vector);
        vector.multiply(new THREE.Vector3(cameraSpeed, 0, cameraSpeed));
        camera.focusTarget.add(vector);
    }

    if (keys[KeyCode.Down] || keys[KeyCode.S]) { //down
        let vector = new THREE.Vector3();
        camera.getWorldDirection(vector);
        vector.multiply(new THREE.Vector3(-cameraSpeed, 0, -cameraSpeed));
        camera.focusTarget.add(vector);
    }

    
    var rotation = (6/180) * Math.PI

    let cameraOffset = new THREE.Vector3(camera.offset.x, camera.offset.y, camera.offset.z);
    if (keys[KeyCode.Q]) {
        cameraOffset.x = distance * Math.cos(theta + rotation);
        cameraOffset.z = distance * Math.sin(theta + rotation);
    }

    if (keys[KeyCode.E]) {
        cameraOffset.x = distance * Math.cos(theta - rotation);
        cameraOffset.z = distance * Math.sin(theta - rotation);
    }

    if (camera.follow) {
        camera.focusTarget = selected.position.clone();
    }

    camera.offset.lerp(cameraOffset, 0.1);
    camera.focus.lerp(camera.focusTarget, 0.1);
    camera.position.set(camera.focus.x + camera.offset.x, camera.focus.y + camera.offset.y, camera.focus.z + camera.offset.z);
    camera.lookAt(camera.focus);

    raycaster.setFromCamera(mouse, camera);
    intersects = raycaster.intersectObjects(scene.children, true);

    Bee.collection.forEach(bee => bee.update(delta, timer));
    Hive.collection.forEach(hive => hive.update(delta, timer));
    Flower.collection.forEach(flower => flower.update(delta, timer));

    render();
}

document.addEventListener("DOMContentLoaded", () => {
    init();
    fillScene();
});

window.addEventListener("keydown", e => keys[e.keyCode] = true);
window.addEventListener("keyup", e => keys[e.keyCode] = false);

window.addEventListener("mousemove", event => {
    mouse.x = normalize(event.clientX / window.innerWidth);
    mouse.y = -normalize(event.clientY / window.innerHeight);
    event.target.style.cursor = "default";
    let hiveIntersects = getIntersectFromCollection(Hive.collection, intersects);
    if (hiveIntersects.length > 0) {
        event.target.style.cursor = "pointer";
    }

    let flowerIntersects = getIntersectFromCollection(Flower.collection, intersects);
    if (flowerIntersects.length > 0) {
        event.target.style.cursor = "pointer";
    }

    let beeIntersects = getIntersectFromCollection(Bee.collection, intersects);
    if (beeIntersects.length > 0) {
        event.target.style.cursor = "pointer";
    }

    let groundIntersect = intersects.filter(i => i.object == ground);
    if (groundIntersect.length > 0) {
        if (draggingOnGround) {
            event.target.style.cursor = "move";
            dragOffset = camera.focus.clone().sub(groundIntersect[0].point);
            camera.focusTarget = dragStart.clone().add(dragOffset);
        }
    }
});

window.addEventListener("mousedown", event => {
    camera.follow = false;
    mouse.x = normalize(event.clientX / window.innerWidth);
    mouse.y = -normalize(event.clientY / window.innerHeight);
    let hiveIntersects = getIntersectFromCollection(Hive.collection, intersects);
    if (hiveIntersects.length > 0) {
        return;
    }

    let flowerIntersects = getIntersectFromCollection(Flower.collection, intersects);
    if (flowerIntersects.length > 0) {
        return;
    }

    let beeIntersects = getIntersectFromCollection(Bee.collection, intersects);
    if (beeIntersects.length > 0) {
        setFocus(beeIntersects[0]);
        return;
    }

    let groundIntersect = intersects.filter(i => i.object == ground);
    if (groundIntersect.length > 0) {
        draggingOnGround = true;
        dragStart = groundIntersect[0].point.clone();
        dragOffset = camera.focus.clone().sub(groundIntersect[0].point);
        camera.focusTarget = dragStart.clone().add(dragOffset);
    }
});

window.addEventListener("contextmenu", event => {
    event.preventDefault();
});

window.addEventListener("mouseup", event => {
    if (draggingOnGround) {
        draggingOnGround = false;
        return;
    }
    draggingOnGround = false;
    if (event.button == 2) {
        if (selected instanceof Hive) {
            let flowerIntersects = getIntersectFromCollection(Flower.collection, intersects);
            if (flowerIntersects.length > 0) {
                selected.toggleFlower(flowerIntersects[0]);
                addLines(selected);
                return;
            }
            return;
        }
    }
    let hiveIntersects = getIntersectFromCollection(Hive.collection, intersects);
    if (hiveIntersects.length > 0) {
        if (selected == hiveIntersects[0]) {
            setFocus(hiveIntersects[0]);
        }
        selected = hiveIntersects[0];
        addLines(selected);
        return;
    }

    let flowerIntersects = getIntersectFromCollection(Flower.collection, intersects);
    if (flowerIntersects.length > 0) {
        if (selected == flowerIntersects[0]) {
            setFocus(flowerIntersects[0]);
        }
        selected = flowerIntersects[0];
        addLines(selected);
        return;
    }

    let beeIntersects = getIntersectFromCollection(Bee.collection, intersects);
    if (beeIntersects.length > 0) {
        setFocus(beeIntersects[0]);
        addLines(selected);
        return;
    }
});

window.addEventListener("wheel", event => {
    let vector = new THREE.Vector3();
    camera.getWorldDirection(vector);
    vector.multiplyScalar(-event.deltaY / 4);
    camera.offset.add(vector);
});

let addLines = (focus) => {
    for (let i = 0; i < Waypoint.collection.length; i++) {
        let waypoint = Waypoint.collection[i];
        for (let j = 0; j < waypoint.lines.length; j++) {
            let line = waypoint.lines[j];
            scene.remove(line);
        }
    }

    if (focus.hasOwnProperty("entranceWaypoint")) {
        for (let i = 0; i < focus.entranceWaypoint.lines.length; i++) {
            scene.add(focus.entranceWaypoint.lines[i]);
        }
    }

    for (let i = 0; i < Hive.collection.length; i++) {
        let hive = Hive.collection[i];
        scene.remove(hive.billboard);
        if (hive == focus) {
            scene.add(hive.billboard);
        }
    }
}

let getIntersectFromCollection = (collection, intersectCollection) => {
    return collection.filter(target => intersectCollection.filter(i => crawlParent(target.model, i.object)).length > 0);
}

let crawlParent = (lookingFor, questionObj) => {
    if (questionObj == lookingFor) {
        return true;
    }
    if (questionObj.parent == null) {
        return false;
    }
    if (questionObj.parent == lookingFor) {
        return true;
    }

    return crawlParent(lookingFor, questionObj.parent);
}

window.addEventListener("touchmove", event => {
    mouse.x = normalize(event.touches[0].clientX / window.innerWidth);
    mouse.y = -normalize(event.touches[0].clientY / window.innerHeight);
});

function normalize(value) {
    return (2 * value) - 1;
}
