{-
-- EPITECH PROJECT, 2025
-- Glados
-- File description:
-- VM Execution Engine
-}

{-|
Module      : VM.Bytecode.Runner
Description : The central Fetch-Decode-Execute loop.
Stability   : stable

This module orchestrates the execution of the Virtual Machine.
It fetches opcodes from the bytecode and dispatches them to the appropriate
instruction handlers defined in the Instruction modules.
-}
module VM.Bytecode.Runner
    ( runBytecode
    , executeInstruction
    ) where

import Control.Monad.State.Strict (get, liftIO)
import Data.Word (Word8)
import qualified Data.Vector as V

import VM.VMState (VMState(..), VirtualMachine)
import VM.Bytecode.Reader (readByte)
import VM.Instruction.Index
import VM.Instruction.Stack
import VM.Instruction.Arithmetic
import VM.Instruction.Logic
import VM.Instruction.Struct
import VM.Instruction.List
import VM.Instruction.Function
import VM.Instruction.Variable
import VM.Instruction.System
import VM.Instruction.Io

-- | Maps a raw opcode byte to its corresponding instruction function.
--
-- @args
--   - op: The 8-bit instruction code fetched from bytecode.
--
-- @details
--   This pattern matching acts as the specific "Decode" step of the CPU cycle.
--   Any unknown opcode will trigger a runtime error.
--
executeInstruction :: Word8 -> VirtualMachine ()
executeInstruction :: Word8 -> VirtualMachine ()
executeInstruction Word8
0x01 = VirtualMachine ()
instPush
executeInstruction Word8
0x02 = VirtualMachine ()
instPop
executeInstruction Word8
0x03 = VirtualMachine ()
instDup
executeInstruction Word8
0x04 = VirtualMachine ()
instSwap

executeInstruction Word8
0x10 = VirtualMachine ()
instAdd
executeInstruction Word8
0x11 = VirtualMachine ()
instSub
executeInstruction Word8
0x12 = VirtualMachine ()
instMul
executeInstruction Word8
0x13 = VirtualMachine ()
instDiv
executeInstruction Word8
0x14 = VirtualMachine ()
instMod

executeInstruction Word8
0x20 = VirtualMachine ()
instEq
executeInstruction Word8
0x21 = VirtualMachine ()
instLt
executeInstruction Word8
0x22 = VirtualMachine ()
instNot
executeInstruction Word8
0x23 = VirtualMachine ()
instAnd
executeInstruction Word8
0x24 = VirtualMachine ()
instOr
executeInstruction Word8
0x25 = VirtualMachine ()
instLe
executeInstruction Word8
0x26 = VirtualMachine ()
instTEq

executeInstruction Word8
0x30 = VirtualMachine ()
instJump
executeInstruction Word8
0x31 = VirtualMachine ()
instJumpIfFalse
executeInstruction Word8
0x32 = VirtualMachine ()
instJumpIfTrue

executeInstruction Word8
0x40 = VirtualMachine ()
instCall
executeInstruction Word8
0x41 = VirtualMachine ()
instTailCall
executeInstruction Word8
0x42 = VirtualMachine ()
instCallIndirect
executeInstruction Word8
0x43 = VirtualMachine ()
instRet

executeInstruction Word8
0x50 = VirtualMachine ()
instLoadLocal
executeInstruction Word8
0x51 = VirtualMachine ()
instStoreLocal
executeInstruction Word8
0x52 = VirtualMachine ()
instLoadGlobal
executeInstruction Word8
0x53 = VirtualMachine ()
instStoreGlobal
executeInstruction Word8
0x54 = VirtualMachine ()
instLoadCapture
executeInstruction Word8
0x55 = VirtualMachine ()
instStoreCapture

executeInstruction Word8
0x60 = VirtualMachine ()
instMakeClosure
executeInstruction Word8
0x61 = VirtualMachine ()
instGetFuncAddr
executeInstruction Word8
0x62 = VirtualMachine ()
instBuildStruct
executeInstruction Word8
0x63 = VirtualMachine ()
instGetStructField
executeInstruction Word8
0x64 = VirtualMachine ()
instAttrUpdate

executeInstruction Word8
0x70 = VirtualMachine ()
instPrint
executeInstruction Word8
0x71 = VirtualMachine ()
instHalt
executeInstruction Word8
0x72 = VirtualMachine ()
instExit
executeInstruction Word8
0x80 = VirtualMachine ()
instCast
executeInstruction Word8
0x81 = VirtualMachine ()
instTypeOf
executeInstruction Word8
0x82 = VirtualMachine ()
instSizeOf

executeInstruction Word8
0x90 = VirtualMachine ()
instCons
executeInstruction Word8
0x91 = VirtualMachine ()
instHead
executeInstruction Word8
0x92 = VirtualMachine ()
instTail
executeInstruction Word8
0x93 = VirtualMachine ()
instNth
executeInstruction Word8
0x94 = VirtualMachine ()
instBuildList
executeInstruction Word8
0x95 = VirtualMachine ()
instNthUpdate

executeInstruction Word8
0xA0 = VirtualMachine ()
instOpen
executeInstruction Word8
0xA1 = VirtualMachine ()
instRead
executeInstruction Word8
0xA2 = VirtualMachine ()
instWrite
executeInstruction Word8
0xA3 = VirtualMachine ()
instClose
executeInstruction Word8
0xA4 = VirtualMachine ()
instInput

executeInstruction Word8
0xFE = VirtualMachine ()
instCheckStack
executeInstruction Word8
0xFF = () -> VirtualMachine ()
forall a. a -> StateT VMState IO a
forall (m :: * -> *) a. Monad m => a -> m a
return () -- NOP
executeInstruction Word8
op = [Char] -> VirtualMachine ()
forall a. HasCallStack => [Char] -> a
error ([Char] -> VirtualMachine ()) -> [Char] -> VirtualMachine ()
forall a b. (a -> b) -> a -> b
$ [Char]
"VM Error: Unknown Opcode 0x" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Word8 -> [Char]
forall a. Show a => a -> [Char]
show Word8
op

-- Helper function to display the status properly.
--
debugTrace :: VMState -> IO ()
debugTrace :: VMState -> IO ()
debugTrace VMState
vm = let ip :: Int
ip = VMState -> Int
bytecodeIndex VMState
vm
                    stack :: Vector VMValue
stack = VMState -> Vector VMValue
vStack VMState
vm in
    [Char] -> IO ()
putStrLn ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ [Char]
"IP: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Int -> [Char]
forall a. Show a => a -> [Char]
show Int
ip [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" | Stack: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [VMValue] -> [Char]
forall a. Show a => a -> [Char]
show (Vector VMValue -> [VMValue]
forall a. Vector a -> [a]
V.toList Vector VMValue
stack)

-- | The main execution loop of the Virtual Machine.
--
-- @details
--   This function operates in a continuous cycle:
--   1. Checks if the VM 'isRunning' flag is True.
--   2. Fetches the next opcode using 'readByte'.
--   3. Calls 'executeInstruction' to run the associated logic.
--   4. Recursively calls itself to process the next instruction.
--
--   The loop terminates when 'instHalt' sets 'isRunning' to False
--   or if an error occurs (e.g., Stack Underflow).
--
runBytecode :: VirtualMachine ()
runBytecode :: VirtualMachine ()
runBytecode = do
    vm <- StateT VMState IO VMState
forall s (m :: * -> *). MonadState s m => m s
get
    case isRunning vm of
        Bool
False -> () -> VirtualMachine ()
forall a. a -> StateT VMState IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
        Bool
True -> do
            if VMState -> Bool
isDebug VMState
vm then 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
$ VMState -> IO ()
debugTrace VMState
vm else () -> VirtualMachine ()
forall a. a -> StateT VMState IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
            opcode <- VirtualMachine Word8
readByte
            executeInstruction opcode
            runBytecode