/*
 * This file is part of LiquidBounce (https://github.com/CCBlueX/LiquidBounce)
 *
 * Copyright (c) 2015 - 2026 CCBlueX
 *
 * LiquidBounce is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * LiquidBounce is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LiquidBounce. If not, see <https://www.gnu.org/licenses/>.
 */
package net.ccbluex.liquidbounce.features.module.modules.player

import net.ccbluex.liquidbounce.config.types.group.Mode
import net.ccbluex.liquidbounce.config.types.group.ModeValueGroup
import net.ccbluex.liquidbounce.config.types.group.ToggleableValueGroup
import net.ccbluex.liquidbounce.config.types.list.Tagged
import net.ccbluex.liquidbounce.event.events.MovementInputEvent
import net.ccbluex.liquidbounce.event.handler
import net.ccbluex.liquidbounce.event.once
import net.ccbluex.liquidbounce.event.tickHandler
import net.ccbluex.liquidbounce.event.waitTicks
import net.ccbluex.liquidbounce.features.module.ClientModule
import net.ccbluex.liquidbounce.features.module.ModuleCategories
import net.ccbluex.liquidbounce.features.module.modules.player.ModuleAntiAFK.CustomMode.Rotate.angle
import net.ccbluex.liquidbounce.features.module.modules.player.ModuleAntiAFK.CustomMode.Rotate.ignoreOpenInventory
import net.ccbluex.liquidbounce.features.module.modules.player.ModuleAntiAFK.CustomMode.Rotate.rotations
import net.ccbluex.liquidbounce.utils.aiming.RotationManager
import net.ccbluex.liquidbounce.utils.aiming.RotationsValueGroup
import net.ccbluex.liquidbounce.utils.aiming.data.Rotation
import net.ccbluex.liquidbounce.utils.kotlin.Priority
import net.ccbluex.liquidbounce.utils.kotlin.random
import net.ccbluex.liquidbounce.utils.movement.DirectionalInput
import net.minecraft.world.InteractionHand
import kotlin.random.Random

/**
 * AntiAFK module
 *
 * Prevents you from being kicked for AFK.
 */

object ModuleAntiAFK : ClientModule("AntiAFK", ModuleCategories.PLAYER) {
    private val modes = choices(
        "Mode", RandomInteraction, arrayOf(
            OldMode, RandomInteraction, CustomMode
        )
    )

    private object OldMode : Mode("Old") {
        override val parent: ModeValueGroup<Mode>
            get() = modes

        @Suppress("unused")
        val repeatable = tickHandler {
            waitTicks(10)
            player.yRot += 180f
        }

        @Suppress("unused")
        val movementInputEvent = handler<MovementInputEvent> {
            it.directionalInput = it.directionalInput.copy(
                forwards = true
            )
        }

    }

    private object RandomInteraction : Mode("RandomInteraction") {
        override val parent: ModeValueGroup<Mode>
            get() = modes

        var randomDirection = DirectionalInput.NONE

        private val interactions by multiEnumChoice("Interaction",
            Interaction.YAW,
            Interaction.PITCH,
            Interaction.SWING_HAND,
        )

        private val delay by intRange("Delay", 4..7, 0..20, suffix = "ticks")

        @Suppress("unused")
        val repeatable = tickHandler {
            interactions.randomOrNull()?.let {
                it.perform()
                waitTicks(delay.random())
            }
        }

        @Suppress("unused")
        val movementInputEvent = handler<MovementInputEvent> {
            it.directionalInput = randomDirection
        }

        @Suppress("unused", "MagicNumber")
        private enum class Interaction(
            override val tag: String,
            val perform: suspend () -> Unit,
        ): Tagged {
            JUMP("Jump", {
                once<MovementInputEvent> { event ->
                    event.jump = true
                }
            }),
            SWING_HAND("SwingHand", {
                if (!player.swinging) {
                    player.swing(InteractionHand.MAIN_HAND)
                }
            }),
            CHANGE_SLOT("ChangeSlot", {
                player.inventory.selectedSlot = Random.nextInt(0, 9)
            }),
            YAW("Yaw", {
                player.yRot += (-180f..180f).random()
            }),
            PITCH("Pitch", {
                player.xRot = ((-5f..5f).random() + player.xRot).coerceIn(-90f, 90f)
            }),
            RANDOM_DIRECTION("RandomDirection", {
                randomDirection = DirectionalInput(
                    Random.nextBoolean(),
                    Random.nextBoolean(),
                    Random.nextBoolean(),
                    Random.nextBoolean()
                )
                waitTicks(delay.random())
                randomDirection = DirectionalInput.NONE
            })
        }
    }

    private object CustomMode : Mode("Custom") {
        override val parent: ModeValueGroup<Mode>
            get() = modes


        private object Rotate : ToggleableValueGroup(ModuleAntiAFK, "Rotate", true) {
            val ignoreOpenInventory by boolean("IgnoreOpenInventory", true)
            val rotations = tree(RotationsValueGroup(this))
            val delay by int("Delay", 5, 0..20, "ticks")
            val angle by float("Angle", 1f, -180f..180f)
        }

        private object Swing : ToggleableValueGroup(ModuleAntiAFK, "Swing", true) {
            val delay by int("Delay", 5, 0..20, "ticks")
        }

        init {
            tree(Rotate)
            tree(Swing)
        }

        val jump by boolean("Jump", true)
        val move by boolean("Move", true)

        @Suppress("unused")
        val swingRepeatable = tickHandler {
            if (Swing.enabled && !player.swinging) {
                waitTicks(Swing.delay)
                player.swing(InteractionHand.MAIN_HAND)
            }
        }

        @Suppress("unused")
        val repeatable = tickHandler {
            if (move) {
                mc.options.keyUp.isDown = true
            }

            if (jump && player.onGround()) {
                once<MovementInputEvent> { event ->
                    event.jump = true
                }
            }

            if (Rotate.enabled) {
                waitTicks(Rotate.delay)
                val currentRotation = RotationManager.serverRotation
                val pitchRandomization = Random.nextDouble(-5.0, 5.0).toFloat()
                RotationManager.setRotationTarget(
                    Rotation(
                        currentRotation.yaw + angle, (currentRotation.pitch + pitchRandomization).coerceIn(-90f, 90f)
                    ), ignoreOpenInventory, rotations, Priority.IMPORTANT_FOR_USAGE_1, ModuleAntiAFK
                )
            }

        }
    }
}
