{-
-- EPITECH PROJECT, 2025
-- Glados
-- File description:
-- VM Value Data Type
-}

module VM.VMValue
    ( VMValue(..)
    , getValueName
    , castValue
    , valueToString
    , valueToInt
    , eqValue
    , stringToValue
    , charToValue
    ) where

import Data.Word (Word8)
import Data.Vector (Vector)
import qualified Data.Vector as V
import qualified Data.Text as T
import Data.Text (Text)
import Data.Char (chr, ord)

import Common.Type.Integer (IntValue(..), intValueToInt)

-- | Represents a runtime value within the Virtual Machine.
--
-- @details
--   This data structure encompasses all possible types that can exist
--   on the stack or in memory during the execution of the program.
--   It bridges the gap between the static AST and the dynamic runtime.
--
data VMValue
    = VInt IntValue
      -- ^ Represents an integer value (wrapping the Common IntValue type).
      --   Used for all arithmetic operations.

    | VBool Bool
      -- ^ Represents a boolean value (#t or #f).
      --   Used for logic operations and conditional jumps.

    | VList (Vector VMValue)
      -- ^ Represents a dynamic list of values.
      --   Corresponds to the 'AList' node in the AST.

    | VStruct (Vector VMValue)
      -- ^ Represents an instance of a structure.
      --   Fields are stored contiguously. The compiler resolves field names to indices.

    | VClosure Int (Vector VMValue)
      -- ^ Represents a closure (anonymous function with captured context).
      --   @param Int The absolute instruction pointer address of the function code.
      --   @param Vector The captured environment (free variables) from the creation scope.

    | VFuncPtr Int
      -- ^ Represents a raw function pointer without captured variables.
      --   @param Int The absolute instruction pointer address.

    | VVoid
      -- ^ Represents the absence of a value (Unit/Null).
      --   Used for functions that do not return data.
    deriving (VMValue -> VMValue -> Bool
(VMValue -> VMValue -> Bool)
-> (VMValue -> VMValue -> Bool) -> Eq VMValue
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: VMValue -> VMValue -> Bool
== :: VMValue -> VMValue -> Bool
$c/= :: VMValue -> VMValue -> Bool
/= :: VMValue -> VMValue -> Bool
Eq, Int -> VMValue -> ShowS
[VMValue] -> ShowS
VMValue -> String
(Int -> VMValue -> ShowS)
-> (VMValue -> String) -> ([VMValue] -> ShowS) -> Show VMValue
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> VMValue -> ShowS
showsPrec :: Int -> VMValue -> ShowS
$cshow :: VMValue -> String
show :: VMValue -> String
$cshowList :: [VMValue] -> ShowS
showList :: [VMValue] -> ShowS
Show)

-- | Helper: Extracts a Char from a VMValue if and only if it is an
--           integer type representing a character.
--
-- @param val The value to check.
-- @return Just Char if the value is IChar or UIChar, Nothing otherwise.
--
getCharFromValue :: VMValue -> Maybe Char
getCharFromValue :: VMValue -> Maybe Char
getCharFromValue (VInt (IChar Int8
c))  = Char -> Maybe Char
forall a. a -> Maybe a
Just (Int -> Char
chr (Int8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int8
c))
getCharFromValue (VInt (UIChar Word8
c)) = Char -> Maybe Char
forall a. a -> Maybe a
Just (Int -> Char
chr (Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
c))
getCharFromValue VMValue
_ = Maybe Char
forall a. Maybe a
Nothing

-- | Helper: Attempts to convert a list of VMValues into a Text string.
--
-- @param vals The list of values to inspect.
-- @return Just Text if all elements are characters, Nothing otherwise.
--
tryExtractString :: [VMValue] -> Maybe Text
tryExtractString :: [VMValue] -> Maybe Text
tryExtractString [] = Maybe Text
forall a. Maybe a
Nothing
tryExtractString [VMValue]
vals = (String -> Text) -> Maybe String -> Maybe Text
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap String -> Text
T.pack Maybe String
maybeChars
    where maybeChars :: Maybe String
maybeChars = (VMValue -> Maybe Char) -> [VMValue] -> Maybe String
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM VMValue -> Maybe Char
getCharFromValue [VMValue]
vals

-- | Converts a VMValue into a human-readable Text representation.
--
-- @param val The value to convert.
-- @return A Text representing the value.
--
-- @details
--   This function handles type-specific formatting:
--   - **Chars**: Converted to single-character strings.
--   - **Lists**: If the list contains only Characters, it is rendered as a raw string
--     (e.g., "Hello"). Otherwise, it uses list notation (e.g., "[1, 2, #t]").
--   - **Structs**: Rendered with curly braces (e.g., "{10, 20}").
--   - **Bools**: Rendered as #t or #f (Scheme style).
--
valueToString :: VMValue -> Text
valueToString :: VMValue -> Text
valueToString VMValue
VVoid = String -> Text
T.pack String
"void"
valueToString (VInt (IChar Int8
c)) = Char -> Text
T.singleton (Int -> Char
chr (Int -> Char) -> Int -> Char
forall a b. (a -> b) -> a -> b
$ Int8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int8
c)
valueToString (VInt (UIChar Word8
c)) = Char -> Text
T.singleton (Int -> Char
chr (Int -> Char) -> Int -> Char
forall a b. (a -> b) -> a -> b
$ Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
c)
valueToString (VInt IntValue
i) = String -> Text
T.pack (Int -> String
forall a. Show a => a -> String
show (IntValue -> Int
intValueToInt IntValue
i))
valueToString (VBool Bool
True) = String -> Text
T.pack String
"True"
valueToString (VBool Bool
False) = String -> Text
T.pack String
"False"
valueToString (VClosure Int
addr Vector VMValue
caps) = String -> Text
T.pack String
"#<procedure @" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
    String -> Text
T.pack (Int -> String
forall a. Show a => a -> String
show Int
addr) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
T.pack String
" captures:" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
    String -> Text
T.pack (Int -> String
forall a. Show a => a -> String
show (Vector VMValue -> Int
forall a. Vector a -> Int
V.length Vector VMValue
caps)) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
T.pack String
">"
valueToString (VFuncPtr Int
addr) = String -> Text
T.pack String
"#<function @" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
    String -> Text
T.pack (Int -> String
forall a. Show a => a -> String
show Int
addr) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
T.pack String
">"
valueToString (VStruct Vector VMValue
v) = String -> Text
T.pack String
"{" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
    Text -> [Text] -> Text
T.intercalate (String -> Text
T.pack String
", ") ((VMValue -> Text) -> [VMValue] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map VMValue -> Text
valueToString (Vector VMValue -> [VMValue]
forall a. Vector a -> [a]
V.toList Vector VMValue
v)) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
T.pack String
"}"
valueToString (VList Vector VMValue
v) = let elements :: [VMValue]
elements = Vector VMValue -> [VMValue]
forall a. Vector a -> [a]
V.toList Vector VMValue
v in
    case [VMValue] -> Maybe Text
tryExtractString [VMValue]
elements of
        Just Text
txt -> Text
txt
        Maybe Text
Nothing -> String -> Text
T.pack String
"[" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> (Text -> [Text] -> Text
T.intercalate (String -> Text
T.pack String
", ")
            ((VMValue -> Text) -> [VMValue] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map VMValue -> Text
valueToString [VMValue]
elements)) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
T.pack String
"]"

-- | Converts a single Character to a VMValue.
--
-- @arg c: The character to convert.
--
-- @details
--   Wraps the character's ordinal value into an IChar (Int8).
--   Used as a helper for string conversion.
--
charToValue :: Char -> VMValue
charToValue :: Char -> VMValue
charToValue Char
c = IntValue -> VMValue
VInt (Int8 -> IntValue
IChar (Int -> Int8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Char -> Int
ord Char
c)))

-- | Converts a Text string into a VMValue.
--
-- @arg txt: The text string to convert.
--
-- @details
--   Transforms the Text into a VList of VInt (IChar).
--   This reverses the logic of 'valueToString' for strings, creating
--   the standard list-based string representation used by the VM.
--
stringToValue :: Text -> VMValue
stringToValue :: Text -> VMValue
stringToValue Text
txt = Vector VMValue -> VMValue
VList ([VMValue] -> Vector VMValue
forall a. [a] -> Vector a
V.fromList ((Char -> VMValue) -> String -> [VMValue]
forall a b. (a -> b) -> [a] -> [b]
map Char -> VMValue
charToValue (Text -> String
T.unpack Text
txt)))

-- | Helper to extract a raw integer value for casting purposes.
--
-- @details
--   - Integers: returns the raw value.
--   - Booleans: True -> 1, False -> 0.
--   - Pointers: returns the address.
--   - Others: returns 0 (safe default).
valueToInt :: VMValue -> Int
valueToInt :: VMValue -> Int
valueToInt (VInt IntValue
i) = IntValue -> Int
intValueToInt IntValue
i
valueToInt (VBool Bool
True) = Int
1
valueToInt (VBool Bool
False) = Int
0
valueToInt (VFuncPtr Int
addr) = Int
addr
valueToInt VMValue
_ = Int
0

-- | Recursively determines the precise type name of a value.
--
-- @arg val: The VMValue to inspect.
-- @return The formatted type string (e.g., "[int]", "Player", "[[char]]").
--
-- @details
--   - Primitives: Returns "int", "bool", "void".
--   - Chars: Distinguished from Ints via IChar/UIChar -> "char".
--   - Structs: Returns the stored name (e.g., "Player").
--   - Lists: Recursively inspects the *first* element to build the type syntax.
--     [] -> "[void]"
--     [1, 2] -> "[int]"
--     ["hi"] -> "[[char]]"
--
getValueName :: VMValue -> Text
getValueName :: VMValue -> Text
getValueName VMValue
VVoid = String -> Text
T.pack String
"void"
getValueName (VBool Bool
_) = String -> Text
T.pack String
"bool"
getValueName (VClosure Int
_ Vector VMValue
_) = String -> Text
T.pack String
"function"
getValueName (VFuncPtr Int
_) = String -> Text
T.pack String
"function"
getValueName (VInt (IChar Int8
_)) = String -> Text
T.pack String
"char"
getValueName (VInt (UIChar Word8
_)) = String -> Text
T.pack String
"char"
getValueName (VInt IntValue
_) = String -> Text
T.pack String
"int"
getValueName (VStruct Vector VMValue
_) = String -> Text
T.pack String
"[struct]"
getValueName (VList Vector VMValue
v)
    | Vector VMValue -> Bool
forall a. Vector a -> Bool
V.null Vector VMValue
v = String -> Text
T.pack String
"[void]"
    | Bool
otherwise = String -> Text
T.pack String
"[" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> VMValue -> Text
getValueName (Vector VMValue -> VMValue
forall a. Vector a -> a
V.head Vector VMValue
v) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
T.pack String
"]"

-- | Converts a VMValue to a specific target TypeID.
--
-- @args
--   - typeId: The target type identifier (see ASM_SPEC).
--   - val: The value to convert.
--
-- @details
--   Performs a native conversion (truncation or extension) via 'fromIntegral'.
--   Supports converting between Booleans, Integers (signed/unsigned) and Chars.
--
castValue :: Word8 -> VMValue -> VMValue
castValue :: Word8 -> VMValue -> VMValue
castValue Word8
0x00 VMValue
v = Bool -> VMValue
VBool (VMValue -> Int
valueToInt VMValue
v Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
0)
castValue Word8
0x01 VMValue
v = IntValue -> VMValue
VInt (Int8 -> IntValue
I8 (Int -> Int8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (VMValue -> Int
valueToInt VMValue
v)))
castValue Word8
0x02 VMValue
v = IntValue -> VMValue
VInt (Word8 -> IntValue
UI8 (Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (VMValue -> Int
valueToInt VMValue
v)))
castValue Word8
0x03 VMValue
v = IntValue -> VMValue
VInt (Int16 -> IntValue
I16 (Int -> Int16
forall a b. (Integral a, Num b) => a -> b
fromIntegral (VMValue -> Int
valueToInt VMValue
v)))
castValue Word8
0x04 VMValue
v = IntValue -> VMValue
VInt (Word16 -> IntValue
UI16 (Int -> Word16
forall a b. (Integral a, Num b) => a -> b
fromIntegral (VMValue -> Int
valueToInt VMValue
v)))
castValue Word8
0x05 VMValue
v = IntValue -> VMValue
VInt (Int32 -> IntValue
I32 (Int -> Int32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (VMValue -> Int
valueToInt VMValue
v)))
castValue Word8
0x06 VMValue
v = IntValue -> VMValue
VInt (Word32 -> IntValue
UI32 (Int -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (VMValue -> Int
valueToInt VMValue
v)))
castValue Word8
0x07 VMValue
v = IntValue -> VMValue
VInt (Int64 -> IntValue
I64 (Int -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (VMValue -> Int
valueToInt VMValue
v)))
castValue Word8
0x08 VMValue
v = IntValue -> VMValue
VInt (Word64 -> IntValue
UI64 (Int -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (VMValue -> Int
valueToInt VMValue
v)))
castValue Word8
0x09 VMValue
v = IntValue -> VMValue
VInt (Int8 -> IntValue
IChar (Int -> Int8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (VMValue -> Int
valueToInt VMValue
v)))
castValue Word8
0x10 VMValue
v = IntValue -> VMValue
VInt (Word8 -> IntValue
UIChar (Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (VMValue -> Int
valueToInt VMValue
v)))
castValue Word8
_ VMValue
v = VMValue
v

-- | Helper to compare two vectors using the custom equality logic.
checkVectorEq :: V.Vector VMValue -> V.Vector VMValue -> Bool
checkVectorEq :: Vector VMValue -> Vector VMValue -> Bool
checkVectorEq Vector VMValue
v1 Vector VMValue
v2 =
    Vector VMValue -> Int
forall a. Vector a -> Int
V.length Vector VMValue
v1 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Vector VMValue -> Int
forall a. Vector a -> Int
V.length Vector VMValue
v2 Bool -> Bool -> Bool
&& Vector Bool -> Bool
V.and ((VMValue -> VMValue -> Bool)
-> Vector VMValue -> Vector VMValue -> Vector Bool
forall a b c. (a -> b -> c) -> Vector a -> Vector b -> Vector c
V.zipWith VMValue -> VMValue -> Bool
eqValue Vector VMValue
v1 Vector VMValue
v2)

-- | Recursive equality check for VMValues.
--
-- @details
--   Handles loose integer comparison inside Lists and Structs.
--   e.g., [I8 5] == [I64 5] returns True.
--
eqValue :: VMValue -> VMValue -> Bool
eqValue :: VMValue -> VMValue -> Bool
eqValue (VInt IntValue
a) (VInt IntValue
b) = IntValue -> Int
intValueToInt IntValue
a Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== IntValue -> Int
intValueToInt IntValue
b
eqValue (VBool Bool
a) (VBool Bool
b) = Bool
a Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Bool
b
eqValue (VFuncPtr Int
a) (VFuncPtr Int
b) = Int
a Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
b
eqValue VMValue
VVoid VMValue
VVoid = Bool
True
eqValue (VList Vector VMValue
a) (VList Vector VMValue
b) = Vector VMValue -> Vector VMValue -> Bool
checkVectorEq Vector VMValue
a Vector VMValue
b
eqValue (VStruct Vector VMValue
a) (VStruct Vector VMValue
b) = Vector VMValue -> Vector VMValue -> Bool
checkVectorEq Vector VMValue
a Vector VMValue
b
eqValue VMValue
_ VMValue
_ = Bool
False