{-
-- EPITECH PROJECT, 2025
-- Glados
-- File description:
-- Index Instructions
-}

{-|
Module      : VM.Instruction.Index
Description : Implementation of branching and jump operations.
Stability   : stable

Handles relative jumps and conditional branching instructions.
These instructions modify the 'bytecodeIndex' (Instruction Pointer) of the VMState.
-}
module VM.Instruction.Index
    ( instJump
    , instJumpIfFalse
    , instJumpIfTrue
    ) where

import Control.Monad.State.Strict (modify)

import VM.VMState (VirtualMachine, VMState(..))
import VM.VMValue (VMValue(..))
import VM.Bytecode.Reader (readInt32)
import VM.VMStack (stackPop)
import Common.Type.Integer (intValueToInt)

-- | Helper to check if a value is "Truthy".
--
-- @details
--   - Bool: True is True, False is False.
--   - Int: 0 is False, anything else is True.
--   - Others: Considered False (safe default) or error depending on preference.
--
isTruth :: VMValue -> Bool
isTruth :: VMValue -> Bool
isTruth (VBool Bool
b) = Bool
b
isTruth (VInt IntValue
i) = IntValue -> Int
intValueToInt IntValue
i Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
0
isTruth VMValue
_ = Bool
False

-- | Implements Unconditional Jump (Opcode 0x30).
--
-- @details
--   1. Reads a signed 32-bit integer (offset) from the bytecode.
--   2. Adds this offset to the current 'bytecodeIndex'.
--
--   Note: Since 'readInt32' advances the index by 4 bytes, the offset
--   is effectively relative to the instruction immediately following the JUMP.
--
instJump :: VirtualMachine ()
instJump :: VirtualMachine ()
instJump = do
    offset <- VirtualMachine Int
readInt32
    modify $ \VMState
vm -> VMState
vm { bytecodeIndex = bytecodeIndex vm + offset }

-- | Implements Jump If False (Opcode 0x31).
--
-- @details
--   Conditional jump used for 'if' statements and loops.
--   1. Reads the jump offset.
--   2. Pops a value from the stack.
--   3. If the value is 'VBool False', applies the jump.
--   4. If 'VBool True', continues execution normally (fall-through).
--
-- @throws
--   Error "VM Error: JUMP_IF_FALSE expects Boolean" if the popped value is not a boolean.
--
instJumpIfFalse :: VirtualMachine ()
instJumpIfFalse :: VirtualMachine ()
instJumpIfFalse = do
    offset <- VirtualMachine Int
readInt32
    v <- stackPop
    case isTruth v of
        Bool
False ->
            (VMState -> VMState) -> VirtualMachine ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((VMState -> VMState) -> VirtualMachine ())
-> (VMState -> VMState) -> VirtualMachine ()
forall a b. (a -> b) -> a -> b
$ \VMState
vm -> VMState
vm { bytecodeIndex = bytecodeIndex vm + offset }
        Bool
True -> () -> VirtualMachine ()
forall a. a -> StateT VMState IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()

-- | Implements Jump If True (Opcode 0x32).
--
-- @details
--   Conditional jump (reverse logic of JumpIfFalse).
--   1. Reads the jump offset.
--   2. Pops a value from the stack.
--   3. If the value is 'VBool True', applies the jump.
--
-- @throws
--   Error "VM Error: JUMP_IF_TRUE expects Boolean" if the popped value is not a boolean.
--
instJumpIfTrue :: VirtualMachine ()
instJumpIfTrue :: VirtualMachine ()
instJumpIfTrue = do
    offset <- VirtualMachine Int
readInt32
    v <- stackPop
    case isTruth v of
        Bool
True ->
            (VMState -> VMState) -> VirtualMachine ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((VMState -> VMState) -> VirtualMachine ())
-> (VMState -> VMState) -> VirtualMachine ()
forall a b. (a -> b) -> a -> b
$ \VMState
vm -> VMState
vm { bytecodeIndex = bytecodeIndex vm + offset }
        Bool
False -> () -> VirtualMachine ()
forall a. a -> StateT VMState IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()