-- | Convert the Disciple Core Salt into to real C code.
--
--   The input module needs to be:
--      well typed,
--      fully named with no deBruijn indices,
--      have all functions defined at top-level,
--      a-normalised,
--      have a control-transfer primop at the end of every function body
--        (these are added by DDC.Core.Salt.Convert.Transfer)
--      
module DDC.Core.Salt.Convert
        ( seaOfSaltModule
        , initRuntime
        , seaNameOfSuper
        , seaNameOfLocal
        , sanitizeName
        , Error (..))

where
import DDC.Core.Salt.Convert.Type
import DDC.Core.Salt.Convert.Init
import DDC.Core.Salt.Convert.Name
import DDC.Core.Salt.Convert.Super
import DDC.Core.Salt.Convert.Base
import DDC.Core.Salt.Name
import DDC.Core.Salt.Platform
import DDC.Core.Compounds
import DDC.Core.Module                          as C
import DDC.Core.Exp
import DDC.Base.Pretty
import DDC.Control.Monad.Check                  (throw, evalCheck)
import qualified DDC.Type.Env                   as Env


-- | Convert a Disciple Core Salt module to C-source text.
seaOfSaltModule
        :: Show a 
        => Bool                 -- ^ Whether to emit top-level include macros.
                                --   Emitting makes the code easier to read during testing.
        -> Platform             -- ^ Target platform specification
        -> Module a Name        -- ^ Module to convert.
        -> Either (Error a) Doc

seaOfSaltModule withPrelude pp mm
 = {-# SCC seaOfSaltModule #-}
   evalCheck () $ convModuleM withPrelude pp mm


-- | Convert a Disciple Core Salt module to C source text.
convModuleM :: Show a => Bool -> Platform -> Module a Name -> ConvertM a Doc
convModuleM withPrelude pp mm@(ModuleCore{})
 | ([LRec bxs], _) <- splitXLets $ moduleBody mm
 = do   
        -- Top-level includes -------------------
        -- These include runtime system functions and macro definitions that
        -- the generated code refers to directly.
        let cIncludes
                | not withPrelude       = empty
                | otherwise
                = vcat    
                $  [ text "// Includes for helper macros and the runtime system. -----------------"
                   , text "#include \"Runtime.h\""
                   , text "#include \"Primitive.h\"" 
                   , line ]


        -- Globals for the runtime system -------
        --   If this is the main module then we define the globals for the
        --   runtime system at top-level.
        let cGlobals
                | not withPrelude       = empty

                | isMainModule mm
                = vcat  
                $  [ text "// Definitions of the runtime system variables. -----------------------"
                   , text "addr_t _DDC__heapTop = 0;"
                   , text "addr_t _DDC__heapMax = 0;" 
                   , line ]

                | otherwise
                = vcat  
                $  [ text "// External definitions for the runtime system variables. -------------"
                   , text "extern addr_t _DDC__heapTop;"
                   , text "extern addr_t _DDC__heapMax;" 
                   , line ]


        -- Import external symbols --------------
        dsImport
         <- mapM (\(misrc, nSuper, tSuper)
                        -> convSuperTypeM Env.empty misrc Nothing nSuper tSuper)
                 [ (Just isrc, nSuper, tSuper)
                        | (nSuper, isrc) <- C.moduleImportValues mm
                        , let tSuper     =  typeOfImportSource isrc ]

        let cExterns
                | not withPrelude       = empty
                | otherwise             
                = vcat  
                $  [ text "// External definitions for imported symbols. -------------------------"]
                ++ [ text "extern " <> doc <> semi | doc <- dsImport ]
                ++ [ line ]
                

        -- Function prototypes ------------------
        --   These are for the supers defined in this module, so that they
        --   can be recursive, and the function definitions don't need to
        --   be emitted in a particular order.
                
        dsProto         
         <- mapM (\(mesrc, nSuper, tSuper) 
                        -> convSuperTypeM Env.empty Nothing mesrc nSuper tSuper)
                 [ (mesrc, nSuper, tSuper) 
                        | (BName nSuper tSuper, _) <- bxs
                        , let mesrc     = lookup nSuper (moduleExportValues mm) ]
                                         
        let cProtos
                | not withPrelude       = empty
                | otherwise
                = vcat 
                $  [ text "// Function prototypes for locally defined supers. --------------------"]
                ++ [ doc <> semi | doc <- dsProto ]
                ++ [ line ]


        -- Super-combinator definitions ---------
        --   This is the code for locally defined functions.
        
        -- Build the top-level kind environment.
        let kenv        = Env.fromList
                        $ [ BName n (typeOfImportSource isrc) 
                                | (n, isrc) <- moduleImportTypes mm ]

        -- Build the top-level type environment.
        let tenv        = Env.fromList 
                        $ [ BName n (typeOfImportSource isrc)
                                | (n, isrc) <- moduleImportValues mm ]

        -- Convert all the super definitions to C code.
        let convSuperM' (BName n t) x
                = convSuperM pp mm kenv tenv n t x

            convSuperM' _ x
                = throw $ ErrorFunctionInvalid x

        dsSupers <- mapM (uncurry convSuperM') bxs
        let cSupers     
                = vcat  
                $  [ text "// Code for locally defined supers. -----------------------------------"]
                ++ dsSupers

        -- Paste everything together ------------
        return  $  cIncludes    -- Includes for helper macros and the runtime system.
                <> cGlobals     -- Definitions of the runtime system variables.
                <> cExterns     -- External definitions for imported symbols.
                <> cProtos      -- Function prototypes for locally defined supers.
                <> cSupers      -- Code for locally defined supers.

 | otherwise
 = throw $ ErrorNoTopLevelLetrec mm