import { ItemStack, system, world } from "@minecraft/server";
import { sendDebugMessage, toDegrees, Vector3 } from "./JM_Utils_for_book";

// note that this system assumes the book will rotate to face the player.  If it does not, the player can get "behind" the book,
// but the math will still think the book is facing the player - so the left and right page turns will appear reversed

////////////////////////////////
////////////////////////////////
////////////////////////////////
const bookId = "lb_as:book"
const maxPage = 9
const tabs = { //note: these show up in the order provided here, from closest-to-spine to furthest
    left: {},
    right: {
        animals: {
            page: 0
        },
        plants: {
            page: 4
        },
    }
}
////////////////////////////////
////////////////////////////////
////////////////////////////////

world.afterEvents.playerInteractWithEntity.subscribe(interactEvent => {
    const book = interactEvent.target
    if (book.typeId !== bookId || !book.isValid) return
    const player = interactEvent.player

    // x > 0 = facing left, else facing right
    const relativeAngle = entityAngleOffset(player, book)
    const direction = relativeAngle.x > 0 ? "left" : "right"
    if (relativeAngle.y < 10 && relativeAngle.y > -10) {
        // player has selected a tab?
        const data = Object.keys(tabs[direction])
        if (data.length > 0) {
            const activeTabs = []
            data.forEach(key => {
                const thisEntry = tabs[direction][key]
                if (typeof thisEntry === "object") activeTabs.push(thisEntry)
            })

            const maxPageWidth = toDegrees(2 * Math.atan(1.5/(2 * Vector3.distance(book.location, player.location))))
            const buttonWidth = maxPageWidth/activeTabs.length
            const h_angle = Math.abs(relativeAngle.x)
            let curAngle = 0
            for (let index = 0; index < activeTabs.length; index++) {
                if (h_angle > curAngle && h_angle < curAngle + buttonWidth) {
                    sendDebugMessage(`button ${index}`)
                    var pageOverride = activeTabs[index].page
                    break
                } else curAngle += buttonWidth
            }
        }
    }
    if (relativeAngle) turnPage(book, pageTurn[relativeAngle.x > 0 ? "left" : "right"], pageOverride)
})

function entityAngleOffset(source, target) {
    const locationDiff = Vector3.subtract(target.location, source.location)

    // HORIZONTAL ANGLE //
    let hAngleToTarget = -(toDegrees(Math.atan2(-locationDiff.z, locationDiff.x)) + 90)
    if (hAngleToTarget < -180) hAngleToTarget += 360
    const sourceAngle = source.getRotation()
    let h_diff = (hAngleToTarget - sourceAngle.y)
    if (h_diff > 180) h_diff -= 360
    if (h_diff < -180) h_diff += 360

    // VERTICAL ANGLE //
    const headLoc = source.getHeadLocation()
    const vLocDiff = target.location.y - headLoc.y
    const headDist = Math.max(Vector3.distance(headLoc, target.location), 0.0000001)
    const vAngleToTarget = -toDegrees(Math.asin(vLocDiff/headDist))
    const v_diff = vAngleToTarget - sourceAngle.x

    return { x: h_diff, y: v_diff }
}

const animationPrefix = "animation.lb_as_book."
const pageTurn = {
    left: "page_back",
    right: "page_forward"
}
const pageProperty = "lb_as:page"
const closedProperty = "lb_as:is_closed"

function turnPage(bookEntity, direction, targetPage) {
    if (targetPage > maxPage || targetPage < 0) {
        sendDebugMessage(`cannot turn to page ${targetPage}`)
        return
    }
    let curPage = bookEntity.getProperty(pageProperty)
    let isClosed = bookEntity.getProperty(closedProperty)
    let animation = ""
    let delay = 4
    if (targetPage == undefined) {
        // no tab, just turn 1 page left or right
        if (curPage === 0) {
            if (direction === pageTurn.left) {
                if (isClosed) {
                    targetPage = 0
                    isClosed = false
                } else {
                    isClosed = true
                }
            } else { // direction = right
                if (isClosed) {
                    targetPage = 0
                    isClosed = false
                } else {
                    targetPage = 1
                    animation = pageTurn.right
                }
            }
        } else if (curPage === maxPage) {
            if (direction === pageTurn.left) {
                if (isClosed) {
                    isClosed = false
                } else {
                    targetPage = maxPage - 1
                    animation = pageTurn.left
                }
            } else { // direction = right
                if (!isClosed) {
                    isClosed = true
                    targetPage = 0
                    delay += 4
                }
            }
        } else {
            targetPage = curPage + (direction === pageTurn.left ? -1 : 1)
            animation = direction
        }
    } else {
        // tab was selected
        if (isClosed) {
            delay = 0
            isClosed = false
        } else {
            if (targetPage < curPage) animation = pageTurn.left
            if (targetPage > curPage) animation = pageTurn.right
            if (targetPage === curPage) return
        }
    }
    if (!bookEntity.isValid) {
        sendDebugMessage(`invalid book, aborting`)
        return
    }
    // sendDebugMessage(`${curPage}, playing ${animation}, setting to ${targetPage}`)
    if (animation) bookEntity.playAnimation(animationPrefix + animation)
    bookEntity.dimension.playSound("item.book.page_turn", bookEntity.location)
    bookEntity.setProperty(closedProperty, isClosed)
    system.runTimeout(() => {
        if (targetPage !== undefined) bookEntity.setProperty(pageProperty, targetPage)
    }, delay)
}

world.afterEvents.entityHitEntity.subscribe(hitEvent => {
    const hurty = hitEvent.hitEntity
    if (hurty.typeId !== bookId) return
    const damager = hitEvent.damagingEntity
    try {
        if (damager && damager.typeId === "minecraft:player") {
            hurty.playAnimation("animation.lb_as_book.despawn"),
            system.runTimeout(() => {
                try {
                    hurty.dimension.spawnItem(new ItemStack(bookId, 1), hurty.location)
                    hurty.dimension.playSound("random.pop", hurty.location)
                    hurty.remove()
                } catch (error) {
                    // ignore
                }
            }, 12)
        }
    } catch (error) {
        // ignore
    }
})