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

{-|
Module      : VM.Instruction.Logic
Description : Implementation of comparison and logic operations.
Stability   : stable
-}
module VM.Instruction.Logic
    ( instEq
    , instTEq
    , instLt
    , instLe
    , instNot
    , instAnd
    , instOr
    ) where

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

-- | Implements Logical NOT (Opcode 0x22).
--
-- @details
--   Pops a boolean and pushes its inverse.
--   Stack: [a] -> [not a]
--
-- @throws
--   Error if the popped value is not a VBool.
--
instNot :: VirtualMachine ()
instNot :: VirtualMachine ()
instNot = do
    v <- VirtualMachine VMValue
stackPop
    case v of
        VBool Bool
b -> VMValue -> VirtualMachine ()
stackPush (Bool -> VMValue
VBool (Bool -> Bool
not Bool
b))
        VMValue
_ -> [Char] -> VirtualMachine ()
forall a. HasCallStack => [Char] -> a
error [Char]
"VM Error: NOT expects Boolean"

-- | Implements Logical AND (Opcode 0x23).
--
-- @details
--   Pops two booleans and pushes their conjunction.
--   Stack: [a, b] -> [a && b]
--
instAnd :: VirtualMachine ()
instAnd :: VirtualMachine ()
instAnd = do
    v2 <- VirtualMachine VMValue
stackPop
    v1 <- stackPop
    case (v1, v2) of
        (VBool Bool
b1, VBool Bool
b2) -> VMValue -> VirtualMachine ()
stackPush (Bool -> VMValue
VBool (Bool
b1 Bool -> Bool -> Bool
&& Bool
b2))
        (VMValue, VMValue)
_ -> [Char] -> VirtualMachine ()
forall a. HasCallStack => [Char] -> a
error [Char]
"VM Error: AND expects Booleans"

-- | Implements Logical OR (Opcode 0x24).
--
-- @details
--   Pops two booleans and pushes their disjunction.
--   Stack: [a, b] -> [a || b]
--
instOr :: VirtualMachine ()
instOr :: VirtualMachine ()
instOr = do
    v2 <- VirtualMachine VMValue
stackPop
    v1 <- stackPop
    case (v1, v2) of
        (VBool Bool
b1, VBool Bool
b2) -> VMValue -> VirtualMachine ()
stackPush (Bool -> VMValue
VBool (Bool
b1 Bool -> Bool -> Bool
|| Bool
b2))
        (VMValue, VMValue)
_ -> [Char] -> VirtualMachine ()
forall a. HasCallStack => [Char] -> a
error [Char]
"VM Error: OR expects Booleans"

-- | Implements Equality Comparison (Opcode 0x20).
--
-- @details
--   Pops two values and performs a deep equality check.
--   Supports: Ints (loose), Bools, Lists (recursive), Structs, FuncPtrs.
--
instEq :: VirtualMachine ()
instEq :: VirtualMachine ()
instEq = do
    v2 <- VirtualMachine VMValue
stackPop
    v1 <- stackPop
    stackPush (VBool (eqValue v1 v2))

-- | Implements Typed Equality (Strict ===).
--
-- @details
--   Checks if two values are identical in Type AND Value.
--   No implicit casting (I8 5 !== I64 5).
--   Standard Haskell '==' on VMValue does exactly this.
--
instTEq :: VirtualMachine ()
instTEq :: VirtualMachine ()
instTEq = do
    v2 <- VirtualMachine VMValue
stackPop
    v1 <- stackPop
    stackPush (VBool (v1 == v2))

-- | Implements Less Than Comparison (Opcode 0x21).
--
-- @details
--   Pops two values and checks if the second popped (Left) is less than the first (Right).
--   Stack: [a, b] -> [a < b] (Push VBool)
--
instLt :: VirtualMachine ()
instLt :: VirtualMachine ()
instLt = do
    v2 <- VirtualMachine VMValue
stackPop
    v1 <- stackPop
    case (v1, v2) of
        (VInt IntValue
i1, VInt IntValue
i2) -> VMValue -> VirtualMachine ()
stackPush (Bool -> VMValue
VBool (
            IntValue -> Int
intValueToInt IntValue
i1 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< IntValue -> Int
intValueToInt IntValue
i2))
        (VMValue, VMValue)
_ -> [Char] -> VirtualMachine ()
forall a. HasCallStack => [Char] -> a
error [Char]
"VM Error: LT expects Integers"

-- | Implements Less or Equal Comparison (Opcode 0x25).
--
-- @details
--   Pops two integers and checks if Left <= Right.
--   Stack: [a, b] -> [a <= b]
--
instLe :: VirtualMachine ()
instLe :: VirtualMachine ()
instLe = do
    v2 <- VirtualMachine VMValue
stackPop
    v1 <- stackPop
    case (v1, v2) of
        (VInt IntValue
i1, VInt IntValue
i2) -> VMValue -> VirtualMachine ()
stackPush (Bool -> VMValue
VBool (
            IntValue -> Int
intValueToInt IntValue
i1 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= IntValue -> Int
intValueToInt IntValue
i2))
        (VMValue, VMValue)
_ -> [Char] -> VirtualMachine ()
forall a. HasCallStack => [Char] -> a
error [Char]
"VM Error: LE expects Integers"