{-
-- EPITECH PROJECT, 2025
-- Glados
-- File description:
-- Semantic Types definition
-}

module AST.Semantics.Type (
    Type(..),
    StructDef(..),
    CheckEnv(..),
    emptyEnv,
    parseType,
    typeToString,
    areTypesCompatible,
) where

import qualified Data.Text as DT
import qualified Data.Map.Strict as Map

-- | Enumeration of all possible types in the language.
data Type
    = TyInt                -- Primitive integer type (int)
    | TyBool               -- Primitive boolean type (bool)
    | TyVoid               -- Void type (void)
    | TyList Type          -- List type (e.g., [int], [[char]])
    | TyFunc [Type] Type   -- Function type (Args -> Return)
    | TyStruct DT.Text      -- Custom structure type identified by name
    | TyAuto               -- Placeholder for type inference
    deriving (Int -> Type -> ShowS
[Type] -> ShowS
Type -> String
(Int -> Type -> ShowS)
-> (Type -> String) -> ([Type] -> ShowS) -> Show Type
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Type -> ShowS
showsPrec :: Int -> Type -> ShowS
$cshow :: Type -> String
show :: Type -> String
$cshowList :: [Type] -> ShowS
showList :: [Type] -> ShowS
Show, Type -> Type -> Bool
(Type -> Type -> Bool) -> (Type -> Type -> Bool) -> Eq Type
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Type -> Type -> Bool
== :: Type -> Type -> Bool
$c/= :: Type -> Type -> Bool
/= :: Type -> Type -> Bool
Eq)

-- | Definition of a structure (Name + Field Map).
data StructDef = StructDef
    { StructDef -> Text
structName :: DT.Text
    , StructDef -> Map Text Type
structFields :: Map.Map DT.Text Type
    } deriving (Int -> StructDef -> ShowS
[StructDef] -> ShowS
StructDef -> String
(Int -> StructDef -> ShowS)
-> (StructDef -> String)
-> ([StructDef] -> ShowS)
-> Show StructDef
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> StructDef -> ShowS
showsPrec :: Int -> StructDef -> ShowS
$cshow :: StructDef -> String
show :: StructDef -> String
$cshowList :: [StructDef] -> ShowS
showList :: [StructDef] -> ShowS
Show)

-- | Verification Environment.
data CheckEnv = CheckEnv
    { CheckEnv -> Map Text Type
envVars :: Map.Map DT.Text Type        -- Symbol table for variables
    , CheckEnv -> Map Text StructDef
envStructs :: Map.Map DT.Text StructDef -- Symbol table for structs
    } deriving (Int -> CheckEnv -> ShowS
[CheckEnv] -> ShowS
CheckEnv -> String
(Int -> CheckEnv -> ShowS)
-> (CheckEnv -> String) -> ([CheckEnv] -> ShowS) -> Show CheckEnv
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> CheckEnv -> ShowS
showsPrec :: Int -> CheckEnv -> ShowS
$cshow :: CheckEnv -> String
show :: CheckEnv -> String
$cshowList :: [CheckEnv] -> ShowS
showList :: [CheckEnv] -> ShowS
Show)

-- | Initial empty environment.
emptyEnv :: CheckEnv
emptyEnv :: CheckEnv
emptyEnv = Map Text Type -> Map Text StructDef -> CheckEnv
CheckEnv Map Text Type
forall k a. Map k a
Map.empty Map Text StructDef
forall k a. Map k a
Map.empty

-- | Parses a raw type string (from Parser) into a Semantic Type.
parseType :: DT.Text -> Type
parseType :: Text -> Type
parseType Text
t
    | Text
t Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== String -> Text
DT.pack String
"int"  = Type
TyInt
    | Text
t Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== String -> Text
DT.pack String
"char" = Type
TyInt 
    | Text
t Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== String -> Text
DT.pack String
"bool" = Type
TyBool
    | Text
t Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== String -> Text
DT.pack String
"void" Bool -> Bool -> Bool
|| Text
t Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== String -> Text
DT.pack String
"Void" = Type
TyVoid
    | Text
t Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== String -> Text
DT.pack String
"auto" = Type
TyAuto
    | Text -> Text -> Bool
DT.isPrefixOf (String -> Text
DT.pack String
"[") Text
t Bool -> Bool -> Bool
&& Text -> Text -> Bool
DT.isSuffixOf (String -> Text
DT.pack String
"]") Text
t =
        let inner :: Text
inner = HasCallStack => Text -> Text
Text -> Text
DT.init (HasCallStack => Text -> Text
Text -> Text
DT.tail Text
t)
        in Type -> Type
TyList (Text -> Type
parseType Text
inner)
    | Bool
otherwise = Text -> Type
TyStruct Text
t

-- | Helper to convert a Semantic Type back to a readable String.
typeToString :: Type -> String
typeToString :: Type -> String
typeToString Type
TyInt = String
"int"
typeToString Type
TyBool = String
"bool"
typeToString Type
TyVoid = String
"void"
typeToString Type
TyAuto = String
"auto"
typeToString (TyList Type
t) = String
"[" String -> ShowS
forall a. [a] -> [a] -> [a]
++ Type -> String
typeToString Type
t String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"]"
typeToString (TyStruct Text
n) = Text -> String
DT.unpack Text
n
typeToString (TyFunc [Type]
args Type
ret) = 
    String
"(" String -> ShowS
forall a. [a] -> [a] -> [a]
++ [String] -> String
unwords ((Type -> String) -> [Type] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Type -> String
typeToString [Type]
args) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" -> " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Type -> String
typeToString Type
ret String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
")"

-- | Checks if two types are semantically compatible.
-- Replaces usage of (==) with explicit pattern matching.
areTypesCompatible :: Type -> Type -> Bool
areTypesCompatible :: Type -> Type -> Bool
areTypesCompatible Type
TyAuto Type
_ = Bool
True
areTypesCompatible Type
_ Type
TyAuto = Bool
True
areTypesCompatible Type
TyInt Type
TyInt   = Bool
True
areTypesCompatible Type
TyBool Type
TyBool = Bool
True
areTypesCompatible Type
TyVoid Type
TyVoid = Bool
True
areTypesCompatible (TyList Type
a) (TyList Type
b) = Type -> Type -> Bool
areTypesCompatible Type
a Type
b
areTypesCompatible (TyStruct Text
a) (TyStruct Text
b) = Text
a Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
b
areTypesCompatible (TyFunc [Type]
args1 Type
ret1) (TyFunc [Type]
args2 Type
ret2) = 
    [Type] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Type]
args1 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== [Type] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Type]
args2 Bool -> Bool -> Bool
&&
    [Bool] -> Bool
forall (t :: * -> *). Foldable t => t Bool -> Bool
and ((Type -> Type -> Bool) -> [Type] -> [Type] -> [Bool]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith Type -> Type -> Bool
areTypesCompatible [Type]
args1 [Type]
args2) Bool -> Bool -> Bool
&&
    Type -> Type -> Bool
areTypesCompatible Type
ret1 Type
ret2
areTypesCompatible Type
_ Type
_ = Bool
False