import Vec3 from '../math/vec3';
import Box from "../math/box";
export class Octree {
    constructor(dimensions, maxObjects = 10, maxLevels = 10) {
        this.dimensions = dimensions;
        this.maxObjects = maxObjects;
        this.maxLevels = maxLevels;
        this.root = new OcTreeNode(new Box(new Vec3([
            0,
            0,
            0
        ]), this.dimensions), maxLevels, maxObjects, 0);
    }
    insert(obj) {
        this.root.insert(obj);
    }
    remove(obj) {
        this.root.remove(obj);
    }
    find(area) {
        return this.root.find(area);
    }
}
export class OcTreeNode {
    constructor(boundingBox, maxLevels, maxObjects, currentLevel = 0) {
        this.boundingBox = boundingBox;
        this.maxLevels = maxLevels;
        this.maxObjects = maxObjects;
        this.currentLevel = currentLevel;
        this.objects = [];
        this.nodes = [];
    }
    insert(obj) {
        if (!obj.boundingBox.overlaps(this.boundingBox))
            return;
        if (this.nodes.length > 1) {
            for (let i = 0; i < this.nodes.length - 1; i++) {
                this.nodes[i].insert(obj);
            }
        }
        else {
            this.objects.push(obj);
            if (this.objects.length > this.maxObjects) {
                this.split();
            }
        }
    }
    remove(obj) {
        if (obj.boundingBox.overlaps(this.boundingBox)) {
            if (this.nodes.length > 1) {
                for (let i = 0; i < this.nodes.length - 1; i++) {
                    this.nodes[i].remove(obj);
                }
            }
        }
        if (this.objects.includes(obj)) {
            this.objects = this.objects.filter((item) => item !== obj);
        }
    }
    find(area) {
        let result = [];
        if (area.overlaps(this.boundingBox)) {
            if (this.nodes.length > 1) {
                for (let i = 0; i < this.nodes.length; i++) {
                    let res = this.nodes[i].find(area);
                    if (res.length > 0)
                        res.forEach((item) => result.push(item));
                }
            }
            if (this.objects.length > 0)
                this.objects.forEach((item) => result.push(item));
        }
        return result;
    }
    split() {
        if (this.currentLevel == this.maxLevels)
            return;
        const halfWidth = this.boundingBox.size.x / 2;
        const halfHeight = this.boundingBox.size.y / 2;
        const halfDepth = this.boundingBox.size.z / 2;
        this.nodes[Direction.AboveUpperLeft] = new OcTreeNode(new Box(new Vec3([
            this.boundingBox.position.x,
            this.boundingBox.position.y,
            this.boundingBox.position.z + halfDepth
        ]), new Vec3([
            halfWidth,
            halfHeight,
            halfDepth
        ])), this.maxLevels, this.maxObjects, this.currentLevel + 1);
        this.nodes[Direction.AboveUpperRight] = new OcTreeNode(new Box(new Vec3([
            this.boundingBox.position.x + halfWidth,
            this.boundingBox.position.y,
            this.boundingBox.position.z + halfDepth
        ]), new Vec3([
            halfWidth,
            halfHeight,
            halfDepth
        ])), this.maxLevels, this.maxObjects, this.currentLevel + 1);
        this.nodes[Direction.AboveLowerRight] = new OcTreeNode(new Box(new Vec3([
            this.boundingBox.position.x + halfWidth,
            this.boundingBox.position.y + halfHeight,
            this.boundingBox.position.z + halfDepth
        ]), new Vec3([
            halfWidth,
            halfHeight,
            halfDepth
        ])), this.maxLevels, this.maxObjects, this.currentLevel + 1);
        this.nodes[Direction.AboveLowerLeft] = new OcTreeNode(new Box(new Vec3([
            this.boundingBox.position.x,
            this.boundingBox.position.y + halfHeight,
            this.boundingBox.position.z + halfDepth
        ]), new Vec3([
            halfWidth,
            halfHeight,
            halfDepth
        ])), this.maxLevels, this.maxObjects, this.currentLevel + 1);
        this.nodes[Direction.BelowUpperLeft] = new OcTreeNode(new Box(new Vec3([
            this.boundingBox.position.x,
            this.boundingBox.position.y,
            this.boundingBox.position.z
        ]), new Vec3([
            halfWidth,
            halfHeight,
            halfDepth
        ])), this.maxLevels, this.maxObjects, this.currentLevel + 1);
        this.nodes[Direction.BelowUpperRight] = new OcTreeNode(new Box(new Vec3([
            this.boundingBox.position.x + halfWidth,
            this.boundingBox.position.y,
            this.boundingBox.position.z
        ]), new Vec3([
            halfWidth,
            halfHeight,
            halfDepth
        ])), this.maxLevels, this.maxObjects, this.currentLevel + 1);
        this.nodes[Direction.BelowLowerRight] = new OcTreeNode(new Box(new Vec3([
            this.boundingBox.position.x + halfWidth,
            this.boundingBox.position.y + halfHeight,
            this.boundingBox.position.z
        ]), new Vec3([
            halfWidth,
            halfHeight,
            halfDepth
        ])), this.maxLevels, this.maxObjects, this.currentLevel + 1);
        this.nodes[Direction.BelowLowerLeft] = new OcTreeNode(new Box(new Vec3([
            this.boundingBox.position.x,
            this.boundingBox.position.y + halfHeight,
            this.boundingBox.position.z
        ]), new Vec3([
            halfWidth,
            halfHeight,
            halfDepth
        ])), this.maxLevels, this.maxObjects, this.currentLevel + 1);
        this.distributeObjectsToNodes();
    }
    distributeObjectsToNodes() {
        if (this.nodes.length < 8) {
            this.split();
            return;
        }
        for (let i = 0; i < this.objects.length; i++) {
            for (let j = 0; j < this.nodes.length - 1; j++) {
                this.nodes[j].insert(this.objects[i]);
            }
        }
        this.objects = [];
    }
}
var Direction;
(function (Direction) {
    Direction[Direction["AboveUpperLeft"] = 0] = "AboveUpperLeft";
    Direction[Direction["AboveUpperRight"] = 1] = "AboveUpperRight";
    Direction[Direction["AboveLowerRight"] = 2] = "AboveLowerRight";
    Direction[Direction["AboveLowerLeft"] = 3] = "AboveLowerLeft";
    Direction[Direction["BelowUpperLeft"] = 4] = "BelowUpperLeft";
    Direction[Direction["BelowUpperRight"] = 5] = "BelowUpperRight";
    Direction[Direction["BelowLowerRight"] = 6] = "BelowLowerRight";
    Direction[Direction["BelowLowerLeft"] = 7] = "BelowLowerLeft";
    Direction[Direction["Here"] = 8] = "Here";
})(Direction || (Direction = {}));
