import * as THREE from 'three'
import gsap from 'gsap'
import { Howl, Howler } from 'howler'

import LevelLoader from './library/levelloader.js'
import { loadGltf } from './library/loadgltf.js'
import { loadRgbeBackground } from './library/loadbackground.js'
import { getForwardVector, getRightVector, drawDebugLine } from './library/mathhelpers.js'
import Ingredient from './library/ingredient.js'
import './library/arrayhelpers.js'

// Shaders
import firefliesVertex from './shaders/firefly/vertex.glsl'
import firefliesFragment from './shaders/firefly/fragment.glsl'
import cauldronVertex from './shaders/cauldron/vertex.glsl'
import cauldronFragment from './shaders/cauldron/fragment.glsl'

// UI
import { brewTutorialPrompt, closeMainMenuUI, mainMenuUI, openMainMenuUI } from './ui/mainmenuui.js'
import { brewUI, openBrewUI, closeBrewUI } from './ui/gameui.js'
import { closeShopUI, openShopUI, shopTutorialPrompt, shopUI } from './ui/shopui.js'
import { navigationUI, hideNavigationUI, nextRoom, previousRoom } from './ui/navigationui.js'

// Game Data
import * as levelData from './data/level.json'
import * as ingredientInfo from './data/ingredients.json'
import * as potionInfo from './data/potions.json'
import Shopper from './shopper.js'
import { buildCanvasText } from './library/canvastext.js'
import { hideGameStatusUI, updateGameStatusUI } from './ui/gamestatusui.js'

export const GAME_SAVE_KEY = "spookonomics-v1"
const raycast = new THREE.Raycaster()
let game, jackolantern, doorway, directionalLight, doorway2, lollipop, candle, candleLit, book

const ingredients = []
const soundEffects = {}
const stageData = {}

export const ROOM_SHOP = 1;
export const ROOM_BREW = 2;
export const ROOM_MARKET = 3;

const shelfSlots = [
    { "position": new THREE.Vector3(-3.5, 1.5, -3.5), "motionPath": [{ x: -3, y: 1.3, z: -3.5 }, { x: -1.5, y: 2, z: 0 }, { x: -0.5, y: 2, z: 0 }, { x: 0, y: 0.5, z: 0 }] },
    { "position": new THREE.Vector3(-4.5, 1.5, -3.5), "motionPath": [{ x: -3, y: 1.3, z: -3.5 }, { x: -1.5, y: 2, z: 0 }, { x: -0.5, y: 2, z: 0 }, { x: 0, y: 0.5, z: 0 }] },
    { "position": new THREE.Vector3(-4.5, 0, -3.0), "motionPath": [{ x: -3, y: 1.3, z: -3.5 }, { x: -1.5, y: 2, z: 0 }, { x: -0.5, y: 2, z: 0 }, { x: 0, y: 0.5, z: 0 }] },
    { "position": new THREE.Vector3(-3.3, 3, -3.5), "motionPath": [{ x: -3, y: 1.3, z: -3.5 }, { x: -1.5, y: 2, z: 0 }, { x: -0.5, y: 2, z: 0 }, { x: 0, y: 0.5, z: 0 }] }
]

const storeShelfSlots = [
    { "position": new THREE.Vector3(-12.5, 2.75, -3.25), "motionPath": [{ x: -11.5, y: 2.75, z: -3.25 }, { x: -12, y: 2, z: -2 }, { x: -13, y: 0.75, z: 2.5 }] },
    { "position": new THREE.Vector3(-11.5, 2.75, -3.25), "motionPath": [{ x: -11.5, y: 2.75, z: -3.25 }, { x: -12, y: 2, z: -2 }, { x: -13, y: 0.75, z: 2.5 }] },
    { "position": new THREE.Vector3(-8.5, 2.75, -3.25), "motionPath": [{ x: -11.5, y: 2.75, z: -3.25 }, { x: -12, y: 2, z: -2 }, { x: -13, y: 0.75, z: 2.5 }] },
    { "position": new THREE.Vector3(-7.5, 2.75, -3.25), "motionPath": [{ x: -11.5, y: 2.75, z: -3.25 }, { x: -12, y: 2, z: -2 }, { x: -13, y: 0.75, z: 2.5 }] },
    { "position": new THREE.Vector3(-8.5, 1.25, -3.25), "motionPath": [{ x: -11.5, y: 2.75, z: -3.25 }, { x: -12, y: 2, z: -2 }, { x: -13, y: 0.75, z: 2.5 }] },
    { "position": new THREE.Vector3(-7.5, 1.25, -3.25), "motionPath": [{ x: -11.5, y: 2.75, z: -3.25 }, { x: -12, y: 2, z: -2 }, { x: -13, y: 0.75, z: 2.5 }] },
]

const customerSlots = [
    { "position": new THREE.Vector3(-12.9, 0.1, 3) },
    { "position": new THREE.Vector3(-13.1, 0.1, 4) },
    { "position": new THREE.Vector3(-12.7, 0.1, 5) },
    { "position": new THREE.Vector3(-13.1, 0.1, 6) },
    { "position": new THREE.Vector3(-12.9, 0.1, 7) },
    { "position": new THREE.Vector3(-13, 0.1, 8) },
]

const bubblePositions = []
const bubbleVelocity = []
const bubbleLifespan = []
const bubbleScale = []
const bubbleCount = 15

async function beginBrew(game, stageData) {
    if (!stageData.potionToBrew) {
        stageData.soundEffects['audio/witch_cackle1.ogg'].play()
        return
    }

    stageData.cauldron.isBrewing = true

    closeBrewUI(game, stageData)

    stageData.brewWitch.stir = true
    stageData.brewWitch.bounce = true
    stageData.spoon.visible = true

    stageData.selectedIngredients.forEach(ingredientName => {
        ingredients.find((ingredient => ingredient.getName() == ingredientName)).beginBrew()
    })

    stageData.bottle1 = await loadGltf(game, stageData.potionToBrew.model)
    stageData.bottle1.position.y = -1.5
    game.scene.add(stageData.bottle1)

    stageData.selectedIngredients = []

    stageData.soundEffects['audio/click1.ogg'].play()

    const nextColorData = stageData.potionToBrew.color
    stageData.cauldronUniforms.uNextPotionColor.value = new THREE.Color(nextColorData.r, nextColorData.g, nextColorData.b)

    stageData.cauldronUniforms.uBlendTime.value = 0.0
    setTimeout(() => {
        stageData.soundEffects['audio/bubbling.mp3'].volume(0.5)
        stageData.soundEffects['audio/bubbling.mp3'].play()
        stageData.bubbleParticles.visible = true
        gsap.to(stageData.cauldronUniforms.uBlendTime, {
            value: 1, duration: 4.5, onComplete: () => {
                stageData.cauldronUniforms.uPotionColor.value = stageData.cauldronUniforms.uNextPotionColor.value
                stageData.cauldronUniforms.uBlendTime.value = 0.0
            }
        })
    }, 900)


    setTimeout(() => {
        addValueToSave(stageData.potionInventory, 'potion-inventory', stageData.potionToBrew.name)
        stageData.potionToBrew = null
        stageData.selectedIngredients = []

        stageData.bottle1.position.set(0, -1.5, 0)
        stageData.soundEffects['audio/bubbling.mp3'].fade(1, 0, 300)
        stageData.brewWitch.stir = false
        stageData.brewWitch.bounce = false
        stageData.spoon.visible = false
        stageData.bubbleParticles.visible = false

        gsap.to(stageData.bottle1.position, {
            duration: 3, y: 1.5, ease: "elastic", onComplete: () => {
                setTimeout(() => {
                    game.scene.remove(stageData.bottle1)
                    //TODO play potion particle effect
                    stageData.cauldron.isBrewing = false
                }, 1000)
            }
        })
    }, 5000)
}


async function customerBuilder(randomPotion, delay) {
    const shopperPosition = customerSlots[stageData.customers.length].position
    const randomIndex = Math.floor(Math.random() * 3)
    let shopper
    switch (randomIndex) {
        case 0:
            shopper = new Shopper(game, 'characters/assembled_character_1.gltf.glb', randomPotion, new THREE.Vector3(3.5, 3.5, 3.5), shopperPosition, Math.PI, function (elapsedTime) {
                this.object3d.position.y = 0.0625 * Math.sin(2 * elapsedTime) + 0.5
            })
            break;
        case 1:
            shopper = new Shopper(game, 'characters/character_skeleton_minion.gltf', randomPotion, new THREE.Vector3(1, 1, 1), shopperPosition, Math.PI, function (elapsedTime) {
                this.object3d.scale.x = 1 + (0.03125 * Math.sin(11.8 * elapsedTime))
                this.object3d.scale.y = 1 + (-0.03125 * Math.sin(11.8 * elapsedTime))
                this.object3d.scale.z = 1 + (0.03125 * Math.sin(11.8 * elapsedTime))
            })
            break;
        case 2:
            shopper = new Shopper(game, 'characters/ghost_1.gltf.glb', randomPotion, new THREE.Vector3(3.5, 3.5, 3.5), shopperPosition, Math.PI, function (elapsedTime) {
                this.object3d.position.y = 0.0625 * Math.sin(2.2 * elapsedTime) + 0.5
            })
            break;
    }
    await shopper.init(delay)
    return shopper
}

async function beginSell(game, stageData) {
    if (stageData.potionStocked.length < 1) {
        stageData.soundEffects['audio/witch_cackle1.ogg'].play()
        return
    }


    stageData.soundEffects['audio/click1.ogg'].play()

    stageData.isSellingPotions = true

    let randomPotions = []
    randomPotions.push(...stageData.potionStocked)
    randomPotions.push(...stageData.potionInventory)
    randomPotions.push(...stageData.potionInfo.map(info => info.name))

    randomPotions.shuffle()
    let numberOfCustomers = Math.min(randomPotions.length, Math.floor(Math.random() * 3) + 3)
    for (let i = 0; i < numberOfCustomers; i++) {
        const desiredPotion = randomPotions.shift()
        const potionInfo = stageData.potionInfo.find(info => info.name == desiredPotion)
        stageData.customers.push(await customerBuilder(potionInfo, i))
    }
    setTimeout(() => {
        stageData.soundEffects['audio/store-entrance-bell.ogg'].play()
    }, 500)

    stageData.customers[0].showDesire()
    closeShopUI(game, stageData)
}

export async function updatePotionShelfDisplay() {
    let storeShelfSpot = 0
    for (let i = 0; i < storeShelfSlots.length; i++) {

        if (stageData.displayedPotions[i]) {
            game.entities.remove(stageData.displayedPotions[i])
            game.scene.remove(stageData.displayedPotions[i])
            stageData.displayedPotions[i] = null
        }

        const currentPotionInfo = stageData.potionInfo.find(potion => stageData.potionStocked[i] == potion.name)
        if (!currentPotionInfo) {
            continue
        }
        const currentPotion = await loadGltf(game, currentPotionInfo.model)
        currentPotion.position.copy(storeShelfSlots[storeShelfSpot++].position)
        currentPotion.potionData = currentPotionInfo
        stageData.displayedPotions[i] = currentPotion
        game.entities.push(currentPotion)
        game.scene.add(currentPotion)
    }
}

export function addValueToSave(container, key, value) {
    let containerString = localStorage.getItem(`${GAME_SAVE_KEY}-${key}`)
    if (!containerString) {
        container = [value]
    } else {
        container.push(value)
    }

    localStorage.setItem(`${GAME_SAVE_KEY}-${key}`, JSON.stringify(container))
}

export function addAmountToSave(key, value) {
    let amount = parseInt(localStorage.getItem(`${GAME_SAVE_KEY}-${key}`)) ?? 0
    amount += value

    localStorage.setItem(`${GAME_SAVE_KEY}-${key}`, amount)
    return amount
}

export function removeValueFromSave(container, key, value) {
    let containerString = localStorage.getItem(`${GAME_SAVE_KEY}-${key}`)
    if (!containerString) {
        container = []
    } else {
        container.remove(value)
    }

    localStorage.setItem(`${GAME_SAVE_KEY}-${key}`, JSON.stringify(container))
}

export function clearSaveData(stageData) {
    localStorage.removeItem(`${GAME_SAVE_KEY}-potion-inventory`)
    localStorage.removeItem(`${GAME_SAVE_KEY}-potion-stocked`)
    localStorage.setItem(`${GAME_SAVE_KEY}-potion-inventory`, JSON.stringify([]))
    stageData.potionInventory = []
    localStorage.setItem(`${GAME_SAVE_KEY}-potion-stocked`, JSON.stringify([]))
    stageData.potionStocked = []

    stageData.currency = 100
    localStorage.setItem(`${GAME_SAVE_KEY}-currency`, stageData.currency)

    stageData.currentDay = 1
    localStorage.setItem(`${GAME_SAVE_KEY}-currentday`, stageData.currentDay)
}

export function loadSaveData(stageData) {
    let potionInventoryString = localStorage.getItem(`${GAME_SAVE_KEY}-potion-inventory`)
    if (!potionInventoryString) {
        localStorage.setItem(`${GAME_SAVE_KEY}-potion-inventory`, JSON.stringify([]))
        stageData.potionInventory = []
    } else {
        stageData.potionInventory = JSON.parse(potionInventoryString)
        stageData.potionInventory.sort()
    }

    let potionsStockedString = localStorage.getItem(`${GAME_SAVE_KEY}-potion-stocked`)
    if (!potionsStockedString) {
        localStorage.setItem(`${GAME_SAVE_KEY}-potion-stocked`, JSON.stringify([]))
        stageData.potionStocked = []
    } else {
        stageData.potionStocked = JSON.parse(potionsStockedString)
        stageData.potionStocked.sort()
    }

    stageData.currency = localStorage.getItem(`${GAME_SAVE_KEY}-currency`)
    if (!stageData.currency) {
        localStorage.setItem(`${GAME_SAVE_KEY}-currency`, 100)
        stageData.currency = 100
    }

    stageData.currentDay = localStorage.getItem(`${GAME_SAVE_KEY}-currentday`)
    if (!stageData.currentDay) {
        localStorage.setItem(`${GAME_SAVE_KEY}-currentday`, 1)
        stageData.currentDay = 1
    }
}

export async function init(inGame) {
    game = inGame

    loadSaveData(stageData)

    stageData.currentRoom = 2
    stageData.soundEffects = soundEffects

    stageData.selectedIngredients = []

    stageData.cameraPositions = [
        { "camera": new THREE.Vector3(-15, 5, 7), "focus": new THREE.Vector3(-13, 0.1, 0) },
        { "camera": new THREE.Vector3(1, 5, 7), "focus": new THREE.Vector3(0, 0.1, 0) },
        { "camera": new THREE.Vector3(12, 5, 7), "focus": new THREE.Vector3(18, 0.1, 0) },
        { "camera": new THREE.Vector3(1.37, 2.41, 3.77), "focus": new THREE.Vector3(-1.15, 0.79, -0.19) }
    ]

    const soundFilePaths = [
        'audio/click1.ogg',
        'audio/sinkWater1.ogg',
        'audio/doorOpen_1.ogg',
        'audio/doorClose_4.ogg',
        'audio/drawKnife2.ogg',
        'audio/witch_cackle1.ogg',
        'audio/bubbling.mp3',
        'audio/chest_close_creak.ogg',
        'audio/chest_open_creak.ogg',
        'audio/handleCoins.ogg',
        'audio/impactGlass_medium_000.ogg',
        'audio/impactGlass_medium_001.ogg',
        'audio/impactGlass_medium_002.ogg',
        'audio/impactGlass_medium_003.ogg',
        'audio/impactGlass_medium_004.ogg',
        'audio/impactWood_heavy_002.ogg',
        'audio/impactWood_heavy_004.ogg',
        'audio/impactSoft_medium_002.ogg',
        'audio/impactSoft_medium_004.ogg',
        'audio/cash-register.ogg',
        'audio/store-entrance-bell.ogg'
    ]
    soundFilePaths.forEach(path => {
        soundEffects[path] = new Howl({
            src: [path],
            preload: true,
        })
    })

    soundEffects['audio/doorClose_4.ogg'].volume(0.5)
    soundEffects['audio/doorOpen_1.ogg'].volume(0.5)

    let levelLoader = new LevelLoader(game)

    game.camera.position.copy(stageData.cameraPositions[3].camera)
    game.lookAtFocus = stageData.cameraPositions[3].focus.clone()

    await loadRgbeBackground(game, 'kloppenheim_02_puresky_1k.hdr')
    game.scene.background = null
    game.scene.fog = new THREE.Fog(0x000000, 12, 20)

    let instancedMeshes = await levelLoader.load(levelData.default)

    instancedMeshes.forEach(instancedMesh => {
        game.scene.add(instancedMesh)
    })

    stageData.ingredientInfo = ingredientInfo.default
    stageData.potionInfo = potionInfo.default

    Object.values(stageData.ingredientInfo).forEach(info => {
        ingredients.push(new Ingredient(info, game))
    })


    ///////////////
    /// Brewing ///
    ///////////////
    let shelfSpot = 0
    ingredients.forEach(async ingredient => {
        await ingredient.spawn(shelfSlots[shelfSpot++])
    })

    stageData.candycorn = await loadGltf(game, 'models/candycorn.gltf')

    stageData.candycorn.position.x = -4.5
    stageData.candycorn.position.z = -3.5
    game.scene.add(stageData.candycorn)

    stageData.pumpkin2 = await loadGltf(game, 'models/pumpkin_orange.gltf')
    game.entities.push(stageData.pumpkin2)
    stageData.pumpkin2.position.x = -10
    stageData.pumpkin2.position.y = 1
    stageData.pumpkin2.position.z = 0.5
    game.scene.add(stageData.pumpkin2)

    jackolantern = await loadGltf(game, 'models/pumpkin_orange_jackolantern.gltf')
    game.entities.push(jackolantern)
    jackolantern.spin = 0
    jackolantern.position.set(3, 0.1, 1)
    jackolantern.rotateOnAxis(THREE.Object3D.DEFAULT_UP, -Math.PI / 4)
    game.scene.add(jackolantern)

    candle = await loadGltf(game, 'models/candle_thin.gltf.glb')
    game.entities.push(candle)
    candle.position.x = 0
    candle.position.y = 1
    candle.position.z = -2.5
    candle.isLit = false
    game.scene.add(candle)

    candleLit = await loadGltf(game, 'models/candle_thin_lit.gltf.glb')
    game.entities.push(candleLit)
    candleLit.position.x = 0
    candleLit.position.y = 1
    candleLit.position.z = -2.5
    candleLit.visible = false
    game.scene.add(candleLit)

    stageData.cauldronUniforms = {
        uTime: { value: 0 },
        uPixelRatio: { value: Math.min(window.devicePixelRatio, 2) },
        uBlendTime: { value: 0 },
        uPotionColor: { value: new THREE.Vector3(0, 0.8, 0.2) },
        uNextPotionColor: { value: new THREE.Vector3(0, 0.8, 0.2) },
        uTransparency: { value: 0.8 }
    }

    stageData.cauldron = await loadGltf(game, 'models/simple_cauldron.gltf.glb')
    const cauldronBell = stageData.cauldron.getObjectByName("Sphere015")
    cauldronBell.material.side = THREE.DoubleSide
    const cauldronTop = stageData.cauldron.getObjectByName("Sphere015_1")
    const cauldronTopMaterial = new THREE.ShaderMaterial({
        fragmentShader: cauldronFragment,
        vertexShader: cauldronVertex,
        uniforms: stageData.cauldronUniforms,
        transparent: true,
        depthWrite: false,
        side: THREE.DoubleSide,
    })
    cauldronTop.material = cauldronTopMaterial

    game.entities.push(stageData.cauldron)
    stageData.cauldron.position.y = 0.1
    game.scene.add(stageData.cauldron)

    stageData.brewTutorial1 = buildCanvasText("Click to Brew", { font: "86px Alice" })
    stageData.brewTutorial1.position.z = 2.4
    stageData.brewTutorial1.position.y = 0.2
    stageData.brewTutorial1.rotateOnAxis(new THREE.Vector3(1, 0, 0), -Math.PI / 2)
    stageData.brewTutorial1.material.opacity = 0
    stageData.brewTutorial1.castShadow = false
    game.scene.add(stageData.brewTutorial1)

    stageData.brewWitch = await loadGltf(game, 'characters/character_witch.gltf')
    game.entities.push(stageData.brewWitch)
    stageData.brewWitch.position.x = 1
    stageData.brewWitch.position.y = 0.1
    stageData.brewWitch.position.z = -1
    stageData.brewWitch.stir = false
    stageData.brewWitch.bounce = false
    stageData.brewWitch.lookAt(stageData.cauldron.position)
    game.scene.add(stageData.brewWitch)


    stageData.shopWitch = await loadGltf(game, 'characters/character_witch.gltf')
    game.entities.push(stageData.shopWitch)
    stageData.shopWitch.position.x = -13
    stageData.shopWitch.position.y = 0.1
    stageData.shopWitch.position.z = -2
    game.scene.add(stageData.shopWitch)

    stageData.marketWitch = await loadGltf(game, 'characters/character_witch.gltf')
    game.entities.push(stageData.marketWitch)
    stageData.marketWitch.position.x = 20
    stageData.marketWitch.position.y = 0.1
    stageData.marketWitch.position.z = 2.5
    stageData.marketWitch.rotateOnAxis(THREE.Object3D.DEFAULT_UP, Math.PI)
    game.scene.add(stageData.marketWitch)

    stageData.spoon = await loadGltf(game, 'models/spoon.gltf')
    game.entities.push(stageData.spoon)
    stageData.spoon.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI)
    stageData.spoon.position.y = 1.25
    stageData.spoon.visible = false
    game.scene.add(stageData.spoon)

    book = await loadGltf(game, 'models/book_grey.gltf.glb')
    game.entities.push(book)
    book.rotateOnAxis(THREE.Object3D.DEFAULT_UP, Math.PI)
    book.position.x = 4.6
    book.position.y = 1.9
    book.position.z = -3.9
    game.scene.add(book)

    const bubbleGeometry = new THREE.SphereGeometry(0.1, 24, 24)
    const bubbleMaterial = cauldronTopMaterial
    //bubbleMaterial.uniforms.uTransparency.value = 0.3
    stageData.bubbleParticles = new THREE.InstancedMesh(bubbleGeometry, bubbleMaterial, bubbleCount)
    stageData.bubbleParticles.instanceMatrix.setUsage(THREE.DynamicDrawUsage)
    stageData.bubbleParticles.visible = false
    game.scene.add(stageData.bubbleParticles)

    const matrix = new THREE.Matrix4()
    for (let index = 0; index < bubbleCount; index++) {
        bubblePositions[index] = new THREE.Vector3(0.5 * (Math.random() - 0.5), 0.5, 0.5 * (Math.random() - 0.5))
        bubbleLifespan[index] = 7 - (index * 0.5)
        bubbleScale[index] = 0.2
        bubbleVelocity[index] = new THREE.Vector3(0, 0.01, 0)
    }

    const quaternion = new THREE.Quaternion()
    const scaleVector = new THREE.Vector3(1, 1, 1)
    for (let index = 0; index < bubbleCount; index++) {
        quaternion.setFromAxisAngle(THREE.Object3D.DEFAULT_UP, 0)
        const scale = scaleVector.set(bubbleScale[index], bubbleScale[index], bubbleScale[index])
        matrix.compose(bubblePositions[index], quaternion, scale)
        stageData.bubbleParticles.setMatrixAt(index, matrix)
    }

    doorway = await loadGltf(game, 'models/wall_doorway.glb')
    doorway.position.set(-6, 0, -2)
    doorway.castShadow = false
    doorway.traverse((child) => {
        if (child.isMesh) {
            child.castShadow = false
        }
    })
    doorway.rotateOnAxis(THREE.Object3D.DEFAULT_UP, Math.PI / 2)
    doorway.isOpen = false
    doorway.isMoving = false
    game.entities.push(doorway)
    game.scene.add(doorway)

    doorway2 = await loadGltf(game, 'models/wall_doorway.glb')
    doorway2.position.set(6, 0, 2)
    doorway2.castShadow = false
    doorway2.traverse((child) => {
        if (child.isMesh) {
            child.castShadow = false
        }
    })
    doorway2.rotateOnAxis(THREE.Object3D.DEFAULT_UP, Math.PI / 2)
    doorway2.isOpen = false
    doorway2.isMoving = false
    game.entities.push(doorway2)
    game.scene.add(doorway2)



    ////////////
    /// Shop ///
    ////////////

    // const debugSphereGeometry = new THREE.SphereGeometry(0.1)
    // const debugMaterial = new THREE.MeshBasicMaterial({color: "magenta"})
    // const debugSphere = new THREE.Mesh(debugSphereGeometry, debugMaterial)

    // customerSlots.forEach(slot => {
    //     debugSphere.position.copy(slot.position)
    //     game.scene.add(debugSphere.clone())
    // })

    stageData.displayedPotions = []
    updatePotionShelfDisplay()

    lollipop = await loadGltf(game, 'models/lollipop_green.gltf')
    lollipop.position.x = -11.5
    lollipop.position.y = 1.28
    lollipop.position.z = -3.4
    lollipop.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI / 2)
    lollipop.rotateOnAxis(new THREE.Vector3(0, 0, 1), Math.PI / 2 + -0.3)
    game.entities.push(lollipop)
    game.scene.add(lollipop)

    stageData.chest = await loadGltf(game, 'models/chest_large.glb')
    stageData.chest.scale.set(0.5, 0.5, 0.5)
    stageData.chest.position.x = -15.5
    stageData.chest.position.y = 1
    stageData.chest.position.z = 0
    stageData.chest.rotateOnAxis(THREE.Object3D.DEFAULT_UP, Math.PI / 4)
    game.entities.push(stageData.chest)
    game.scene.add(stageData.chest)

    stageData.shopTutorial1 = buildCanvasText("Click to Sell", { font: "100px Alice" })
    stageData.shopTutorial1.position.x = -15.5
    stageData.shopTutorial1.position.z = 0.2
    stageData.shopTutorial1.position.y = 1.8

    stageData.shopTutorial1.scale.set(0.6, 0.6, 0.6)
    stageData.shopTutorial1.material.opacity = 0
    stageData.shopTutorial1.castShadow = false
    game.scene.add(stageData.shopTutorial1)

    stageData.coin = await loadGltf(game, 'models/coin.gltf.glb')
    stageData.coin.position.x = -12
    stageData.coin.position.y = 1.1
    stageData.coin.position.z = 0
    game.entities.push(stageData.coin)
    game.scene.add(stageData.coin)


    stageData.customers = []

    //////////////
    /// Market ///
    //////////////
    stageData.sellingSkeleton = await loadGltf(game, 'characters/character_skeleton_mage.gltf')
    stageData.sellingSkeleton.position.x = 20
    stageData.sellingSkeleton.position.y = 0.1
    stageData.sellingSkeleton.position.z = -1
    stageData.sellingSkeleton.rotateOnAxis(THREE.Object3D.DEFAULT_UP, -Math.PI / 4)
    game.entities.push(stageData.sellingSkeleton)
    game.scene.add(stageData.sellingSkeleton)

    stageData.sign = await loadGltf(game, 'models/sign_left.gltf')
    stageData.sign.position.x = 14
    stageData.sign.position.y = 0.1
    stageData.sign.position.z = 2
    game.entities.push(stageData.sign)
    game.scene.add(stageData.sign)

    const mushroomCrate = await loadGltf(game, 'models/crate_mushrooms.gltf')
    mushroomCrate.scale.set(0.5, 0.5, 0.5)
    mushroomCrate.position.x = 19.5
    mushroomCrate.position.z = 0.5
    mushroomCrate.rotateOnAxis(THREE.Object3D.DEFAULT_UP, Math.PI / 4)
    game.entities.push(mushroomCrate)
    game.scene.add(mushroomCrate)

    const tomatoCrate = await loadGltf(game, 'models/crate_tomatoes.gltf')
    tomatoCrate.scale.set(0.5, 0.5, 0.5)
    tomatoCrate.position.x = 21
    game.entities.push(tomatoCrate)
    game.scene.add(tomatoCrate)

    const lettuceCrate = await loadGltf(game, 'models/crate_lettuce.gltf')
    lettuceCrate.scale.set(0.5, 0.5, 0.5)
    lettuceCrate.position.x = 18.5
    lettuceCrate.position.z = -0.5
    game.entities.push(lettuceCrate)
    game.scene.add(lettuceCrate)

    const coffin = await loadGltf(game, 'models/coffin_decorated.gltf')
    coffin.position.x = 23
    coffin.position.z = 6
    coffin.rotateOnAxis(THREE.Object3D.DEFAULT_UP, -Math.PI / 4)
    game.scene.add(coffin)

    const candy = await loadGltf(game, 'models/candy_orange_A.gltf')
    candy.position.x = 21
    candy.position.y = 0.25
    candy.position.z = 6
    candy.rotateOnAxis(THREE.Object3D.DEFAULT_UP, -Math.PI / 4)
    game.scene.add(candy)

    const candyBucket = await loadGltf(game, 'models/candy_bucket_B_decorated.gltf')
    candyBucket.position.x = 21.5
    candyBucket.position.y = 0.1
    candyBucket.position.z = 5.5
    candyBucket.rotateOnAxis(THREE.Object3D.DEFAULT_UP, -Math.PI / 2)
    game.scene.add(candyBucket)

    /**
     * Fireflies
     */
    stageData.firefliesUniforms = {
        uTime: { value: 0 },
        uPixelRatio: { value: Math.min(window.devicePixelRatio, 2) }
    }
    const firefliesMaterial = new THREE.ShaderMaterial({
        fragmentShader: firefliesFragment,
        vertexShader: firefliesVertex,
        uniforms: stageData.firefliesUniforms,
        transparent: true,
        depthWrite: false,
        blending: THREE.AdditiveBlending
    })
    const firefliesGeometry = new THREE.BufferGeometry()
    const firefliesCount = 50
    const positionArray = new Float32Array(firefliesCount * 3)
    const scaleArray = new Float32Array(firefliesCount)
    for (let i = 0; i < firefliesCount; i++) {
        positionArray[i * 3 + 0] = 24 * (Math.random() - 0.5) + 18
        positionArray[i * 3 + 1] = 4 * Math.random() + 2
        positionArray[i * 3 + 2] = 16 * (Math.random() - 0.5)

        scaleArray[i] = 0.8 * Math.random() + 1.2
    }

    firefliesGeometry.setAttribute('position', new THREE.BufferAttribute(positionArray, 3))
    firefliesGeometry.setAttribute('aScale', new THREE.BufferAttribute(scaleArray, 1))
    const fireflies = new THREE.Points(firefliesGeometry, firefliesMaterial)

    game.scene.add(fireflies)


    ////////////////
    /// Lighting ///
    ////////////////
    const ambientLight = new THREE.AmbientLight(0xFFFFFF, 1)
    game.scene.add(ambientLight)

    const shadowSize = 24
    directionalLight = new THREE.DirectionalLight(0xFFFFFF, 0.9)
    directionalLight.position.set(8, 10, 6)
    directionalLight.castShadow = true
    directionalLight.shadow.camera.left = -shadowSize
    directionalLight.shadow.camera.right = shadowSize
    directionalLight.shadow.camera.top = -shadowSize
    directionalLight.shadow.camera.bottom = shadowSize
    directionalLight.shadow.camera.far = 28
    directionalLight.shadow.mapSize.width = Math.min(game.renderer.capabilities.maxTextureSize, 2048)
    directionalLight.shadow.mapSize.height = Math.min(game.renderer.capabilities.maxTextureSize, 2048)
    directionalLight.shadow.bias = -0.005
    directionalLight.shadow.radius = 6
    directionalLight.cameraOffset = new THREE.Vector3()
    directionalLight.cameraOffset.copy(directionalLight.position)
    directionalLight.cameraOffset.sub(new THREE.Vector3(0, 0, 0))
    directionalLight.target = game.camera
    directionalLight.update = function () {
        const currentOffset = new THREE.Vector3()
        currentOffset.copy(directionalLight.cameraOffset).add(game.camera.position)
        directionalLight.position.set(currentOffset.x, currentOffset.y, currentOffset.z)
        if (directionalLight.shadow) {
            directionalLight.shadow.camera.position.set(currentOffset.x, currentOffset.y, currentOffset.z)
        }
    }

    game.scene.add(directionalLight)
    // const shadowHelper = new THREE.CameraHelper( directionalLight.shadow.camera )
    // game.scene.add(shadowHelper)


    mainMenuUI(game, stageData)
    navigationUI(game, stageData)
    brewUI(game, stageData)
    shopUI(game, stageData)

    updateGameStatusUI(game, stageData)


    stageData.beginBrew = () => { beginBrew(game, stageData) }
    stageData.beginSell = () => { beginSell(game, stageData) }


    ///////////
    // DEBUG //
    ///////////

    // moveToRoom(ROOM_SHOP)
    // closeMainMenuUI(game, stageData)

    // const planeGeometry = new THREE.PlaneGeometry(1,1)
    // const debugPlane = new THREE.Mesh(planeGeometry, textBubbleMaterial)
    // debugPlane.position.x = 2
    // debugPlane.position.y = 1
    // debugPlane.scale.set(2,2,2)
    // game.scene.add(debugPlane)

}

export function update(game) {
    let elapsedTime = game.clock.getElapsedTime()

    ingredients.forEach(ingredient => {
        ingredient.wobble()
    })

    stageData.candycorn.position.y = 0.125 * Math.sin(4.1 * elapsedTime) + 3.0
    stageData.candycorn.rotateOnAxis(THREE.Object3D.DEFAULT_UP, -0.02)

    directionalLight.update()

    raycast.setFromCamera(game.mousePosition, game.camera)

    Howler.pos(game.camera.position.x, game.camera.position.y, game.camera.position.z)
    Howler.orientation(game.camera.position.x, game.camera.position.y, game.camera.position.z, 0, 1, 0)


    const quaternion = new THREE.Quaternion()
    const scaleVector = new THREE.Vector3(1, 1, 1)
    const matrix = new THREE.Matrix4()

    for (let index = 0; index < bubbleCount; index++) {
        bubblePositions[index].add(bubbleVelocity[index])
        bubbleScale[index] = Math.min(bubbleScale[index] + 0.01, 1)

        bubbleLifespan[index] -= 0.1
        if (bubbleLifespan[index] <= 0) {
            bubbleLifespan[index] = 7
            bubblePositions[index].set(0.5 * (Math.random() - 0.5), 0.5, 0.5 * (Math.random() - 0.5))
            bubbleScale[index] = 0.2
        }
        //quaternion.setFromAxisAngle(THREE.Object3D.DEFAULT_UP, 0)
        const scale = scaleVector.set(bubbleScale[index], bubbleScale[index], bubbleScale[index])
        matrix.compose(bubblePositions[index], quaternion, scale)
        stageData.bubbleParticles.setMatrixAt(index, matrix)
    }
    stageData.bubbleParticles.instanceMatrix.needsUpdate = true

    if (stageData.brewWitch.stir) {
        stageData.spoon.position.x = 0.125 * Math.sin(4 * elapsedTime)
        stageData.spoon.position.z = 0.125 * Math.cos(4 * elapsedTime)
    }

    if (stageData.brewWitch.bounce) {
        stageData.brewWitch.scale.x = 1 + (0.03125 * Math.sin(12 * elapsedTime))
        stageData.brewWitch.scale.y = 1 + (-0.03125 * Math.sin(12 * elapsedTime))
        stageData.brewWitch.scale.z = 1 + (0.03125 * Math.sin(12 * elapsedTime))
    } else {
        stageData.brewWitch.scale.x = 1
        stageData.brewWitch.scale.y = 1
        stageData.brewWitch.scale.z = 1
    }

    if (jackolantern.spin > 0) {
        jackolantern.spin -= 0.05
        jackolantern.rotateOnAxis(THREE.Object3D.DEFAULT_UP, -0.1)
        if (jackolantern.spin < 0) {
            jackolantern.spin = 0
        }
    }

    stageData.sellingSkeleton.scale.x = 1 + (0.03125 * Math.sin(12 * elapsedTime))
    stageData.sellingSkeleton.scale.y = 1 + (-0.03125 * Math.sin(12 * elapsedTime))
    stageData.sellingSkeleton.scale.z = 1 + (0.03125 * Math.sin(12 * elapsedTime))

    stageData.firefliesUniforms.uTime.value = elapsedTime
    stageData.cauldronUniforms.uTime.value = elapsedTime

    stageData.customers.forEach(customer => {
        customer.update(elapsedTime)
    })

    candle.position.y = 0.625 * Math.sin(0.5 * elapsedTime) + 1.3
    candleLit.position.y = 0.625 * Math.sin(0.5 * elapsedTime) + 1.3

    let intersects = raycast.intersectObjects(game.entities.filter(entity => entity.visible))
    if (intersects.length > 0) {
        document.body.style.cursor = "pointer"
    } else {
        document.body.style.cursor = "default"
    }
}

function isAChildOf(parent, childToCheck) {
    if (parent == childToCheck) return true
    if (childToCheck.parent != null) {
        return isAChildOf(parent, childToCheck.parent)
    }
    return false
}

export function onClick() {

    //drawDebugLine(game, witch.position, getForwardVector(witch).multiplyScalar(10), 0xff0000, 1000)
    //drawDebugLine(game, witch.position, getRightVector(witch).multiplyScalar(10), 0x00ff00, 1000)

    let intersects = raycast.intersectObjects(game.entities.filter(entity => entity.visible))
    if (intersects.length > 0) {
        intersects.every(intersect => {

            switch (stageData.currentRoom) {
                case ROOM_SHOP:
                    //SHOP CLICK ACTIONS HERE

                    if (isAChildOf(stageData.chest, intersect.object)) {
                        if (stageData.isSellingPotions) {
                            return false
                        }

                        if (stageData.shopInactivityHandle) {
                            clearTimeout(stageData.shopInactivityHandle)
                            stageData.shopInactivityHandle = 0
                        }

                        if (stageData.chest.isStocking) {
                            closeShopUI(game, stageData)
                        } else {
                            openShopUI(game, stageData)

                            if (stageData.shopTutorial1.material.opacity == 1) {
                                gsap.to(stageData.shopTutorial1.material, {
                                    duration: 1.5, opacity: 0, onComplete: () => {
                                        stageData.shopTutorial1.castShadow = false
                                    }
                                })
                            }
                        }
                        return false
                    }

                    if (isAChildOf(stageData.shopWitch, intersect.object)) {
                        if (stageData.isSellingPotions) {
                            return false
                        }

                        if (stageData.shopInactivityHandle) {
                            clearTimeout(stageData.shopInactivityHandle)
                            stageData.shopInactivityHandle = 0
                        }

                        if (stageData.chest.isStocking) {
                            closeShopUI(game, stageData)
                        } else {
                            openShopUI(game, stageData)
                            if (stageData.shopTutorial1.material.opacity == 1) {
                                gsap.to(stageData.shopTutorial1.material, {
                                    duration: 1.5, opacity: 0, onComplete: () => {
                                        stageData.shopTutorial1.castShadow = false
                                    }
                                })
                            }
                        }
                        return false
                    }

                    if (isAChildOf(stageData.coin, intersect.object)) {
                        let coinMotionPath = [{ x: -12, y: 1.1, z: 1 }, { x: -12, y: 2, z: 2 }, { x: -15.5, y: 2, z: 2 }, { x: -15.5, y: 1.2, z: 0 }]

                        gsap.to(stageData.coin.position, {
                            duration: 3.5, motionPath: coinMotionPath, onComplete: () => {
                                stageData.soundEffects['audio/handleCoins.ogg'].play()
                                closeChest(stageData.chest, () => {
                                    stageData.coin.scale.set(0, 0, 0)
                                    stageData.coin.position.set(-12, 1.1, 0)
                                    gsap.to(stageData.coin.scale, {
                                        ease: "elastic",
                                        duration: 0.7, x: 1, y: 1, z: 1,
                                    })
                                })
                            }
                        })
                        setTimeout(() => {
                            openChest(stageData.chest)
                        }, 700)
                        return false
                    }

                    stageData.displayedPotions.forEach((potion) => {
                        if (isAChildOf(potion, intersect.object)) {


                            if (stageData.isSellingPotions) {
                                if (stageData.customers[0].isMatchingPotion(potion.potionData)) {
                                    const firstCustomer = stageData.customers[0]
                                    firstCustomer.hideDesire()
                                    const potionMotionPath = [{ x: -11.5, y: 2.75, z: -3.25 }, { x: -12, y: 2, z: -2 }, { x: -13, y: 0.75, z: 2.5 }]
                                    gsap.to(potion.position, {
                                        duration: 2, motionPath: potionMotionPath, onComplete: () => {
                                            removeValueFromSave(stageData.potionStocked, 'potion-stocked', potion.potionData.name)
                                            updatePotionShelfDisplay()
                                            firstCustomer.acceptPotion()
                                            stageData.soundEffects['audio/cash-register.ogg'].play()
                                            stageData.currency = addAmountToSave("currency", potion.potionData.value)
                                            updateGameStatusUI(game, stageData)
                                            //move money from customer to chest
                                            //add money to save data
                                            //move customer out of room
                                            nextCustomer()
                                            //display next customer desire
                                            //if no more customers or no more valid potions, end selling

                                        }
                                    })
                                } else {
                                    //TODO: potion does not match
                                    playBottleClink()
                                    gsap.fromTo(potion.rotation, { duration: 0.1, z: 0.2 }, { duration: 0.1, z: 0 })
                                    stageData.customers[0].rejectPotion()
                                    if (stageData.customers[0].rejectCount >= 3) {
                                        stageData.customers[0].showSad()
                                        nextCustomer()
                                    }
                                }
                            } else {
                                playBottleClink()
                                gsap.fromTo(potion.rotation, { duration: 0.1, z: 0.2 }, { duration: 0.1, z: 0 })
                            }
                        }

                    })


                    if (isAChildOf(stageData.pumpkin2, intersect.object)) {
                        if (stageData.pumpkin2.isMoving) {
                            return
                        }
                        stageData.pumpkin2.isMoving = true
                        gsap.to(stageData.pumpkin2.position, {
                            duration: 2, y: 3, onComplete: () => {
                                setTimeout(() => {
                                    stageData.soundEffects['audio/impactWood_heavy_002.ogg'].play()
                                }, 400)
                                setTimeout(() => {
                                    stageData.soundEffects['audio/impactSoft_medium_002.ogg'].play()
                                }, 800)
                                gsap.to(stageData.pumpkin2.position, {
                                    duration: 1, y: 1, ease: "bounce", onComplete: () => {
                                        stageData.pumpkin2.isMoving = false
                                    }
                                })
                            }
                        })
                        return false
                    }

                    break;
                case ROOM_BREW:
                    //BREW CLICK ACTIONS HERE
                    if (isAChildOf(jackolantern, intersect.object)) {
                        soundEffects['audio/drawKnife2.ogg'].play()
                        jackolantern.spin += Math.PI
                        return false
                    }

                    if (isAChildOf(book, intersect.object)) {
                        //soundEffects['audio/drawKnife2.ogg'].play()
                        //jackolantern.spin += Math.PI
                        let bookMotionPath = [{ x: 4.6, y: 1.9, z: 0 }, { x: 4.6, y: 1.9, z: -3.9 }]
                        gsap.to(book.position, {
                            duration: 6, motionPath: bookMotionPath, onComplete: () => {

                            }
                        })

                        return false
                    }

                    if (isAChildOf(stageData.brewWitch, intersect.object)) {
                        if (stageData.cauldron.isBrewing) {
                            return false
                        }

                        if (stageData.brewInactivityHandle) {
                            clearTimeout(stageData.brewInactivityHandle)
                            stageData.brewInactivityHandle = 0
                        }
                        if (stageData.cauldron.brewMenuOpen) {
                            closeBrewUI(game, stageData)
                        } else {
                            openBrewUI(game, stageData)
                            //cauldron.isBrewing = true
                            if (stageData.brewTutorial1.material.opacity == 1) {
                                gsap.to(stageData.brewTutorial1.material, {
                                    duration: 1.5, opacity: 0, onComplete: () => {
                                        stageData.brewTutorial1.castShadow = false
                                    }
                                })
                            }
                        }
                        return false
                    }

                    if (isAChildOf(stageData.cauldron, intersect.object)) {
                        if (stageData.cauldron.isBrewing) {
                            return false
                        }

                        if (stageData.brewInactivityHandle) {
                            clearTimeout(stageData.brewInactivityHandle)
                            stageData.brewInactivityHandle = 0
                        }
                        if (stageData.cauldron.brewMenuOpen) {
                            closeBrewUI(game, stageData)
                        } else {
                            openBrewUI(game, stageData)
                            if (stageData.brewTutorial1.material.opacity == 1) {
                                gsap.to(stageData.brewTutorial1.material, {
                                    duration: 1.5, opacity: 0, onComplete: () => {
                                        stageData.brewTutorial1.castShadow = false
                                    }
                                })
                            }
                            //cauldron.isBrewing = true
                        }
                        // let soundId = soundEffects['audio/sinkWater1.ogg'].play()
                        // soundEffects['audio/sinkWater1.ogg'].once('play', () => {
                        //     soundEffects['audio/sinkWater1.ogg'].volume(0.5, soundId)
                        //     // soundEffects['audio/sinkWater1.ogg'].pos(cauldron.position.x, cauldron.position.y, cauldron.position.z, soundId)
                        //     // soundEffects['audio/sinkWater1.ogg'].pannerAttr({
                        //     //     panningModel: 'HRTF',
                        //     //     refDistance: 1.0,
                        //     //     rolloffFactor: 0.8,
                        //     //     distanceModel: 'exponential',
                        //     // }, soundId)
                        //     soundEffects['audio/sinkWater1.ogg'].stereo(1, soundId)
                        //     //console.log(soundEffects['audio/sinkWater1.ogg'])
                        // }, soundId)
                        return false
                    }




                    if (isAChildOf(candle, intersect.object)) {
                        candle.isLit = true
                        candleLit.visible = true
                        candle.visible = false
                        return false
                    }

                    if (isAChildOf(candleLit, intersect.object)) {
                        candle.isLit = false
                        candleLit.visible = false
                        candle.visible = true
                        return false
                    }
                    break;
                case ROOM_MARKET:
                    //MARKET CLICK ACTIONS HERE

                    if (isAChildOf(stageData.sign, intersect.object)) {
                        previousRoom(game, stageData)
                    }
                    break;

            }

            if (isAChildOf(doorway, intersect.object)) {
                if (stageData.currentRoom == ROOM_BREW) {
                    previousRoom(game, stageData)
                } else if (stageData.currentRoom == ROOM_SHOP) {
                    nextRoom(game, stageData)
                }
                return false
            }

            if (isAChildOf(doorway2, intersect.object)) {

                if (stageData.currentRoom == ROOM_BREW) {
                    nextRoom(game, stageData)
                } else if (stageData.currentRoom == ROOM_MARKET) {
                    previousRoom(game, stageData)
                }
                return false
            }



        })
    }
}

function nextCustomer() {
    const firstCustomer = stageData.customers.shift()
    if (!firstCustomer) {
        return
    }
    const customerMotionPath = [{ x: -11, y: 0.1, z: 3 }, { x: -8, y: 0.1, z: 3 }, { x: -4, y: 0.1, z: 10 }]
    setTimeout(() => {
        stageData.soundEffects['audio/store-entrance-bell.ogg'].play()
    }, 4000)
    gsap.to(firstCustomer.object3d.position, {
        duration: 8, motionPath: customerMotionPath, onComplete: () => {
            game.scene.remove(firstCustomer.object3d)
        }
    })

    gsap.to(firstCustomer.object3d.rotation, {
        duration: 1, y: Math.PI, onComplete: () => {
        }, onUpdate: () => {
            firstCustomer.requestBillboard.lookAt(game.camera.position)
        }
    })

    //const shopperPosition = customerSlots[stageData.customers.length].position
    //move other customers forward
    for (let i = 0; i < stageData.customers.length; i++) {
        const customer = stageData.customers[i]
        const shopperPosition = customerSlots[i].position
        gsap.to(customer.object3d.position, { duration: 1.5, x: shopperPosition.x, y: shopperPosition.y, z: shopperPosition.z })
    }

    if (stageData.customers.length == 0) {
        stageData.isSellingPotions = false
        stageData.currentDay = addAmountToSave("currentday", 1)
        updateGameStatusUI(game, stageData)
    } else {
        stageData.customers[0].showDesire()
        if (stageData.potionStocked.length < 1) {
            stageData.customers[0].rejectPotion(3)
            stageData.customers[0].showSad()
            nextCustomer()
        }
    }


}

export function playBottleClink() {
    setTimeout(() => {
        let randomSound2 = Math.floor(5 * Math.random())
        stageData.soundEffects[`audio/impactGlass_medium_00${randomSound2}.ogg`].play()
    }, 50)
    let randomSound = Math.floor(5 * Math.random())
    stageData.soundEffects[`audio/impactGlass_medium_00${randomSound}.ogg`].play()
}

function openChest(chest) {
    let lid = chest.getObjectByName("chest_large_lid")
    if (chest.isOpen) {
        return
    }

    if (!chest.isMoving) {
        soundEffects['audio/chest_open_creak.ogg'].play()
        chest.isMoving = true
        gsap.to(lid.rotation, {
            duration: 2.5, x: -(Math.PI / 2) + (Math.PI / 8), ease: "elastic", onComplete: () => {
                chest.isOpen = true
                chest.isMoving = false
            }
        })
    }

}

function closeChest(chest, onComplete = () => { }) {
    let lid = chest.getObjectByName("chest_large_lid")
    if (!chest.isOpen) {
        return
    }
    if (!chest.isMoving) {
        soundEffects['audio/chest_close_creak.ogg'].play()

        chest.isMoving = true
        gsap.to(lid.rotation, {
            duration: 1.5, x: 0, ease: "bounce", onComplete: () => {
                chest.isOpen = false
                chest.isMoving = false
                onComplete()
            }
        })
    }
}

function openDoorway(doorway) {
    let door = doorway.getObjectByName("wall_doorway_door")
    if (doorway.isOpen) {
        return
    }
    if (!doorway.isMoving) {
        soundEffects['audio/doorOpen_1.ogg'].play()

        doorway.isMoving = true
        gsap.to(door.rotation, {
            duration: 2.5, y: -Math.PI / 2, ease: "elastic", onComplete: () => {
                doorway.isOpen = true
                doorway.isMoving = false
            }
        })
    }
}

function closeDoorway(doorway) {
    let door = doorway.getObjectByName("wall_doorway_door")
    if (!doorway.isOpen) {
        return
    }
    if (!doorway.isMoving) {
        soundEffects['audio/doorClose_4.ogg'].play()

        doorway.isMoving = true
        gsap.to(door.rotation, {
            duration: 2.5, y: 0, ease: "elastic", onComplete: () => {
                doorway.isOpen = false
                doorway.isMoving = false
            }
        })
    }
}

function moveToShop() {
    closeBrewUI(game, stageData)
    openDoorway(doorway)
    const camPosition = stageData.cameraPositions[0]
    gsap.to(game.camera.position, {
        duration: 2.5, x: camPosition.camera.x, y: camPosition.camera.y, z: camPosition.camera.z, onComplete: () => {
            closeDoorway(doorway)
        }
    })
    gsap.to(game.lookAtFocus, { duration: 2.5, x: camPosition.focus.x, y: camPosition.focus.y, z: camPosition.focus.z })
    stageData.currentRoom = ROOM_SHOP
    shopTutorialPrompt(stageData)
}

function moveToBrew() {
    let doorwayToOpen = doorway
    if (stageData.currentRoom == ROOM_MARKET) {
        doorwayToOpen = doorway2
    }
    openDoorway(doorwayToOpen)
    const camPosition = stageData.cameraPositions[1]
    gsap.to(game.camera.position, {
        duration: 2.5, x: camPosition.camera.x, y: camPosition.camera.y, z: camPosition.camera.z, onComplete: () => {
            closeDoorway(doorwayToOpen)
        }
    })
    gsap.to(game.lookAtFocus, { duration: 2.5, x: camPosition.focus.x, y: camPosition.focus.y, z: camPosition.focus.z })
    stageData.currentRoom = ROOM_BREW
    brewTutorialPrompt(stageData)
}

function moveToMarket() {
    closeBrewUI(game, stageData)
    openDoorway(doorway2)
    const camPosition = stageData.cameraPositions[2]
    gsap.to(game.camera.position, {
        duration: 2.5, x: camPosition.camera.x, y: camPosition.camera.y, z: camPosition.camera.z, onComplete: () => {
            closeDoorway(doorway2)
        }
    })
    gsap.to(game.lookAtFocus, { duration: 2.5, x: camPosition.focus.x, y: camPosition.focus.y, z: camPosition.focus.z })
    stageData.currentRoom = ROOM_MARKET
}

export function moveToRoom(roomId) {
    if (roomId == stageData.currentRoom) {
        return
    }
    switch (roomId) {
        case ROOM_SHOP:
            moveToShop()
            break;
        case ROOM_BREW:
            moveToBrew()
            break;
        case ROOM_MARKET:
            moveToMarket()
            break;
    }
}

export function onKeyPress() {
    if (game.keyboard['Digit1'] && stageData.currentRoom == ROOM_BREW) {
        moveToRoom(ROOM_SHOP)
    }

    if (game.keyboard['Digit2'] && stageData.currentRoom != ROOM_BREW) {
        moveToRoom(ROOM_BREW)
    }

    if (game.keyboard['Digit3'] && stageData.currentRoom == ROOM_BREW) {
        moveToRoom(ROOM_MARKET)
    }

    if (game.keyboard['Digit9'] || game.keyboard['F9']) {
        game.orbitControls.enabled = !game.orbitControls.enabled
        if (!game.orbitControls.enabled) {
            console.log(`position:`, game.camera.position)
            console.log(`focus:`, game.orbitControls.target)
            moveToRoom(stageData.currentRoom)
        }
    }

    if (game.keyboard['Escape']) {
        closeBrewUI(game, stageData)
        closeShopUI(game, stageData)
        hideNavigationUI(game, stageData)
        openMainMenuUI(game, stageData)
        hideGameStatusUI(game, stageData)
        stageData.currentRoom = ROOM_BREW
        game.camera.position.copy(stageData.cameraPositions[3].camera)
        game.lookAtFocus = stageData.cameraPositions[3].focus.clone()
    }
}

