package me.eater.test.threedom.dom

import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.collections.shouldContainExactly
import io.kotest.matchers.collections.shouldHaveSingleElement
import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.shouldBe
import me.eater.threedom.dom.Document
import me.eater.threedom.dom.IDocument
import me.eater.threedom.dom.PlainNode
import me.eater.threedom.dom.createNode
import me.eater.threedom.utils.joml.Vector3d
import me.eater.threedom.utils.joml.translation
import me.eater.threedom.utils.joml.setTranslation

class PositionTest : StringSpec({
    "ensure positioning works" {
        val doc: IDocument = Document()
        val node = doc.createNode<PlainNode>()
        doc.addNode(node)
        node.shouldIndexLocation = true
        node.model { setTranslation(10, 0, 10) }
        node.absolute.translation shouldBe Vector3d(10, 0, 10)
        val nodeTwo = doc.createNode<PlainNode>()
        nodeTwo.shouldIndexLocation = true
        node.addNode(nodeTwo)
        nodeTwo.model { setTranslation(-10, 20, 0) }
        nodeTwo.absolute.translation shouldBe Vector3d(0, 20, 10)
        doc.addNode(nodeTwo)
        nodeTwo.absolute.translation shouldBe Vector3d(-10, 20, 0)
    }

    "ensure position search tree works" {
        val doc: IDocument = Document()
        val node = doc.createNode<PlainNode>()
        val nodeTwo = doc.createNode<PlainNode>()
        val nodeThree = doc.createNode<PlainNode>()
        doc.addNode(node)
        node.addNode(nodeTwo)
        nodeTwo.addNode(nodeThree)
        node.model { setTranslation(10, 0, 10) }
        nodeTwo.model { setTranslation(-10, 20, 0) }
        nodeThree.model { setTranslation(0, 20, 0) }

        listOf(node, nodeTwo, nodeThree).forEach { it.shouldIndexLocation = true }

        doc.findAt(10, 0, 10) shouldHaveSingleElement node
        doc.findAt(0, 20, 10) shouldHaveSingleElement nodeTwo
        doc.findAt(0, 40, 10) shouldHaveSingleElement nodeThree
        doc.rebalance()
        doc.findAt(10, 0, 10) shouldHaveSingleElement node
        doc.findAt(0, 20, 10) shouldHaveSingleElement nodeTwo
        doc.findAt(0, 40, 10) shouldHaveSingleElement nodeThree
        doc.addNode(nodeTwo)
        doc.findAt(-10, 20, 0) shouldHaveSingleElement nodeTwo
        doc.findAt(-10, 40, 0) shouldHaveSingleElement nodeThree
    }

    "ensure in region works" {
        val doc: IDocument = Document()
        val node = doc.createNode<PlainNode>()
        val nodeTwo = doc.createNode<PlainNode>()
        val nodeThree = doc.createNode<PlainNode>()
        doc.addNode(node)
        node.addNode(nodeTwo)
        nodeTwo.addNode(nodeThree)
        node.model { setTranslation(10, 0, 10) }
        nodeTwo.model { setTranslation(-10, 20, 0) }
        nodeThree.model { setTranslation(0, 20, 0) }

        listOf(node, nodeTwo, nodeThree).forEach { it.shouldIndexLocation = true }

        val result = doc.findInRegion(Vector3d(0, 0, 0), Vector3d(0, 20, 20)).toList()
        result shouldHaveSize 1
        result shouldHaveSingleElement nodeTwo

        val resultTwo = doc.findInRegion(Vector3d(0, 0, 0), Vector3d(0, 40, 20)).toList()
        resultTwo shouldHaveSize 2
        resultTwo shouldContainExactly listOf(nodeTwo, nodeThree)
    }

    "ensure in range works" {
        val doc: IDocument = Document()
        val node = doc.createNode<PlainNode>()
        val nodeTwo = doc.createNode<PlainNode>()
        val nodeThree = doc.createNode<PlainNode>()

        listOf(node, nodeTwo, nodeThree).forEach { it.shouldIndexLocation = true }

        doc.addNode(node)
        node.addNode(nodeTwo)
        nodeTwo.addNode(nodeThree)
        node.model { setTranslation(10, 0, 10) }
        nodeTwo.model { setTranslation(-10, 20, 0) }
        nodeThree.model { setTranslation(0, 20, 0) }

        val result = doc.findInRange(Vector3d(0, 0, 0), 20).toList()
        result shouldHaveSize 1
        result shouldContainExactly listOf(node)

        val resultTwo = doc.findInRange(Vector3d(0, 20, 10), 20).toList()
        resultTwo shouldHaveSize 2
        resultTwo shouldContainExactly listOf(nodeTwo, nodeThree)
    }
})
