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

{-|
Module      : VM.Instruction.System
Description : System calls and VM control flow.
Stability   : stable
-}
module VM.Instruction.System
    ( instPrint
    , instHalt
    , instExit
    , instCheckStack
    ) where

import System.Exit (exitWith, ExitCode(..))
import Control.Monad.State.Strict (liftIO, modify, get)
import qualified Data.Vector as V
import qualified Data.Text.IO as TIO

import VM.VMState (VirtualMachine, VMState(..))
import VM.VMValue (valueToString, valueToInt)
import VM.VMStack (stackPop)
import VM.Bytecode.Reader (readInt32)

-- | Implements PRINT (Opcode 0x70).
--
-- @details
--   Pops the top value from the stack and prints its string representation
--   to the standard output (stdout), followed by a newline.
--   Useful for debugging or simple output programs.
--
instPrint :: VirtualMachine ()
instPrint :: VirtualMachine ()
instPrint = do
    val <- VirtualMachine VMValue
stackPop
    liftIO $ TIO.putStr (valueToString val)

-- | Implements HALT (Opcode 0x71).
--
-- @details
--   Sets the 'isRunning' flag of the VM state to False.
--   This will cause the main Execution loop to terminate gracefully
--   on the next cycle.
--
instHalt :: VirtualMachine ()
instHalt :: VirtualMachine ()
instHalt = (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 { isRunning = False }

-- | Implements CHECK_STACK (Opcode 0xFE).
--
-- @details
--   Ensures the stack has at least 'N' elements.
--   Reads 'N' as an Int32. Throws error if stack is too small.
--
instCheckStack :: VirtualMachine ()
instCheckStack :: VirtualMachine ()
instCheckStack = do
    required <- VirtualMachine Int
readInt32
    vm <- get
    case V.length (vStack vm) >= required of
        Bool
False -> [Char] -> VirtualMachine ()
forall a. HasCallStack => [Char] -> a
error ([Char] -> VirtualMachine ()) -> [Char] -> VirtualMachine ()
forall a b. (a -> b) -> a -> b
$ [Char]
"VM Error: Stack Check Failed (Required: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++
            Int -> [Char]
forall a. Show a => a -> [Char]
show Int
required [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
", Actual: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Int -> [Char]
forall a. Show a => a -> [Char]
show (Vector VMValue -> Int
forall a. Vector a -> Int
V.length (VMState -> Vector VMValue
vStack VMState
vm)) [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
")"
        Bool
True -> () -> VirtualMachine ()
forall a. a -> StateT VMState IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()

-- | Implements EXIT (Opcode 0x72).
--
-- @details
--   Pops an integer status code from the stack and terminates the process.
--   Useful for unit testing (exit 84 on failure).
--
instExit :: VirtualMachine ()
instExit :: VirtualMachine ()
instExit = do
    v <- VirtualMachine VMValue
stackPop
    let code = VMValue -> Int
valueToInt VMValue
v
    case code of
        Int
0 -> IO () -> VirtualMachine ()
forall a. IO a -> StateT VMState IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> VirtualMachine ()) -> IO () -> VirtualMachine ()
forall a b. (a -> b) -> a -> b
$ ExitCode -> IO ()
forall a. ExitCode -> IO a
exitWith ExitCode
ExitSuccess
        Int
_ -> IO () -> VirtualMachine ()
forall a. IO a -> StateT VMState IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> VirtualMachine ()) -> IO () -> VirtualMachine ()
forall a b. (a -> b) -> a -> b
$ ExitCode -> IO ()
forall a. ExitCode -> IO a
exitWith (Int -> ExitCode
ExitFailure Int
code)