{-
-- EPITECH PROJECT, 2026
-- Glados
-- File description:
-- Struct
-}

{-|
Module      : VM.Instruction.Struct
Description : Implementation of structures.
Stability   : stable

Handles the creation of structures.
-}
module VM.Instruction.Struct
    ( instBuildStruct
    , instGetStructField
    , instAttrUpdate
    , pushStruct
    ) where

import qualified Data.Vector as V
import Control.Monad.State.Strict (get, put)

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

-- | Helper for instBuildStruct to manipulate the stack.
--
-- @args
--   - vm: Current VM state
--   - count: Number of fields in the struct
--
pushStruct :: VMState -> Int -> VirtualMachine ()
pushStruct :: VMState -> Int -> VirtualMachine ()
pushStruct VMState
vm Int
count =
    let start :: Int
start = Vector VMValue -> Int
forall a. Vector a -> Int
V.length (VMState -> Vector VMValue
vStack VMState
vm) Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
count
        fields :: Vector VMValue
fields = Int -> Int -> Vector VMValue -> Vector VMValue
forall a. Int -> Int -> Vector a -> Vector a
V.slice Int
start Int
count (VMState -> Vector VMValue
vStack VMState
vm) in
    VMState -> VirtualMachine ()
forall s (m :: * -> *). MonadState s m => s -> m ()
put (VMState
vm { vStack = V.take start (vStack vm) }) VirtualMachine () -> VirtualMachine () -> VirtualMachine ()
forall a b.
StateT VMState IO a -> StateT VMState IO b -> StateT VMState IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>>
       VMValue -> VirtualMachine ()
stackPush (Vector VMValue -> VMValue
VStruct Vector VMValue
fields)

-- | Implements BUILD_STRUCT (Opcode 0x62).
--
-- @details
--   Pop 'n' elements from the stack and wraps them in a VStruct.
--   1. Reads 'n' (Int32).
--   2. Consumes 'n' values.
--   3. Pushes VStruct.
--
instBuildStruct :: VirtualMachine ()
instBuildStruct :: VirtualMachine ()
instBuildStruct = do
    count <- VirtualMachine Int
readInt32
    vm <- get
    case V.length (vStack vm) >= count of
        Bool
False -> [Char] -> VirtualMachine ()
forall a. HasCallStack => [Char] -> a
error [Char]
"VM Error: BUILD_STRUCT Stack Underflow"
        Bool
True -> VMState -> Int -> VirtualMachine ()
pushStruct VMState
vm Int
count

-- | Implements GET_STRUCT_FIELD (Opcode 0x63).
--
-- @details
--   Reads a field index and retrieves the value from a Struct on the stack.
--   1. Reads 'index' (Int32).
--   2. Pops 'struct' (VStruct).
--   3. Pushes 'struct[index]'.
--
-- @throws
--   Error if the popped value is not a VStruct or if index is out of bounds.
--
instGetStructField :: VirtualMachine ()
instGetStructField :: VirtualMachine ()
instGetStructField = do
    idx <- VirtualMachine Int
readInt32
    v <- stackPop
    case v of
        VStruct Vector VMValue
fields -> case Int
idx Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0 Bool -> Bool -> Bool
&& Int
idx Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Vector VMValue -> Int
forall a. Vector a -> Int
V.length Vector VMValue
fields of
            Bool
True  -> VMValue -> VirtualMachine ()
stackPush (Vector VMValue
fields Vector VMValue -> Int -> VMValue
forall a. Vector a -> Int -> a
V.! Int
idx)
            Bool
False -> [Char] -> VirtualMachine ()
forall a. HasCallStack => [Char] -> a
error ([Char] -> VirtualMachine ()) -> [Char] -> VirtualMachine ()
forall a b. (a -> b) -> a -> b
$ [Char]
"VM Error: Struct Field Access Out of Bounds " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++
                [Char]
"(" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Int -> [Char]
forall a. Show a => a -> [Char]
show Int
idx [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
")"
        VMValue
_ -> [Char] -> VirtualMachine ()
forall a. HasCallStack => [Char] -> a
error [Char]
"VM Error: GET_STRUCT_FIELD expects a Struct"

-- | Implements ATTR_UPDATE (Opcode 0x64).
--
-- @details
--   Updates a field in a structure.
--   Because structures are immutable, this creates a shallow copy.
--
--   Stack Order:
--     Top    -> New Value
--     Next   -> Field Index (Int)
--     Bottom -> Struct
--
instAttrUpdate :: VirtualMachine ()
instAttrUpdate :: VirtualMachine ()
instAttrUpdate = do
    v <- VirtualMachine VMValue
stackPop
    idx <- stackPop
    struct <- stackPop
    case (struct, idx) of
        (VStruct Vector VMValue
vec, VInt IntValue
vi) -> let i :: Int
i = IntValue -> Int
intValueToInt IntValue
vi in
            case Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0 Bool -> Bool -> Bool
&& Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Vector VMValue -> Int
forall a. Vector a -> Int
V.length Vector VMValue
vec of
                Bool
True -> VMValue -> VirtualMachine ()
stackPush (Vector VMValue -> VMValue
VStruct (Vector VMValue
vec Vector VMValue -> [(Int, VMValue)] -> Vector VMValue
forall a. Vector a -> [(Int, a)] -> Vector a
V.// [(Int
i, VMValue
v)]))
                Bool
False -> [Char] -> VirtualMachine ()
forall a. HasCallStack => [Char] -> a
error ([Char] -> VirtualMachine ()) -> [Char] -> VirtualMachine ()
forall a b. (a -> b) -> a -> b
$ [Char]
"VM Error: attr_update OOB (" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Int -> [Char]
forall a. Show a => a -> [Char]
show Int
i [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
")"
        (VMValue
other, VMValue
_) -> [Char] -> VirtualMachine ()
forall a. HasCallStack => [Char] -> a
error ([Char] -> VirtualMachine ()) -> [Char] -> VirtualMachine ()
forall a b. (a -> b) -> a -> b
$ [Char]
"VM Error: attr_update not struct " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ VMValue -> [Char]
forall a. Show a => a -> [Char]
show VMValue
other