import { EntityComponentTypes, system, world } from "@minecraft/server";
import { sendDebugMessage } from "./JM_Utils";
import { couplingData, dimensions } from "./trainsAddOn";

// returns up to <count> number of items from <inventory>
function getItems(inventory,count) {
    const items = []
    for (let slot = 0; slot < inventory.size; slot++) {
        const thisItem = inventory.getItem(slot)
        if (!thisItem) continue
        // no matter what, we're taking this item
        items.push(inventory.getItem(slot))
        if (thisItem.amount >= count) { // this slot has at least as many as we need
            items[items.length - 1].amount = count
            if (thisItem.amount === count) { // this slot has exactly as many as we need; remove the item from the slot
                inventory.setItem(slot)
            } else { // this slot has more than we need, remove only what we're taking
                thisItem.amount -= count
                inventory.setItem(slot,thisItem)
            }
            break
        } else {
            count -= thisItem.amount
            inventory.setItem(slot)
        }
        if (count <= 0) break
    }
    return items
}

// function called by hopper types.
// looks at the car being pulled for inventory space to transfer items to
function transferItem(car) {
    const data = couplingData(car)
    const pullingId = data.getId()
    if (!pullingId) return
    const pulledCar = world.getEntity(pullingId)
    if (!pulledCar) return
    if (!pulledCar.hasComponent(EntityComponentTypes.Inventory)) return
    const ourInventory = car.getComponent("inventory")?.container,
        theirInventory = pulledCar.getComponent("inventory")?.container
    if (ourInventory.emptySlotsCount === ourInventory.size) return
    // because of our earlier empty inventory check, this should never return an empty array... <.<
    const transferItems = getItems(ourInventory,Math.ceil(Math.random() * 3) + 3)
    // anything in failures[] will get returned to ourInventory{} after the transfer
    const failures = []
    transferItems.forEach(item => {
        const result = theirInventory.addItem(item)
        if (result) {
            failures.push(result)
        }
    })
    if (failures.length > 0) {
        failures.forEach(item => {
            const result = ourInventory.addItem(item)
            if (result) sendDebugMessage(`§4Uhhh... This should be impossible.  Unable to return ${JSON.stringify(item)} to its original inventory`)
        })
    }
    
}

// pulls in item entities
function magnetEffect(entity,height = 2) {
    const origin = {x:entity.location.x,y:entity.location.y + height,z:entity.location.z}
    const itemEntities = entity.dimension.getEntities({location:origin,minDistance:0.7,maxDistance:13,type:"item"})

    itemEntities.forEach(item => {
        const xDist = origin.x - item.location.x,
            yDist = origin.y - item.location.y,
            zDist = origin.z - item.location.z,
            dist = Math.hypot(xDist,yDist,zDist),
            strength = .1/(dist * dist)
        if (dist > 1) {
            item.applyImpulse({
                x: Math.min(xDist * strength,0.5),
                y: Math.min(yDist * strength * (dist > 3 ? 10 : 1),0.5),
                z: Math.min(zDist * strength,0.5)
            })
        } else {
            item.teleport(origin)
        }
    });
}

// adds nearby items to the given car's inventory, if possible
function pickUpItems(car,height = 2) {
    const itemEntities = car.dimension.getEntities({location:{x:car.location.x,y:car.location.y + height,z:car.location.z},maxDistance:0.1,type:"item"}),
        inventory = car.getComponent(EntityComponentTypes.Inventory)?.container


    itemEntities.forEach(item => {
        const itemComponent = item.getComponent(EntityComponentTypes.Item)
        const itemStack = itemComponent.itemStack
        const remaining = inventory.addItem(itemStack)
        if (remaining) {
            if (remaining.amount === itemStack.amount) {
                // nothing has changed, don't bother with spawning a whole new ItemStack
            } else {
                itemStack.amount = remaining.amount
                item.dimension.spawnItem(itemStack,item.location)
                item.remove()
            }        

        } else item.remove()
    })
}

system.runInterval(() => {
    dimensions.forEach(element => {
        const dimension = world.getDimension(element)
        dimension.getEntities({type:"lifeboat_ta:hopper_wagon"}).forEach(car => {
            magnetEffect(car,1.3)
            if (system.currentTick % 8 === 0) transferItem(car)
            pickUpItems(car,1.3)
        })
        dimension.getEntities({type:"lifeboat_ta:hopper_wagon_2"}).forEach(car => {
            magnetEffect(car,2)
            if (system.currentTick % 8 === 0) transferItem(car)
            pickUpItems(car,2)
        })
    })
})