-- generate C-Code with combinators, high-level, safe haskell.

-- | Helper functions for writing free-form C code.
module Language.Copilot.AdHocC (
         varDecl, arrDecl, varInit, arrayInit, funcDecl
       , includeBracket, includeQuote, printf, printfNewline
    ) where

import Data.List (intersperse) 
import Language.Atom (Type(..))
import Language.Atom.Code (cType) -- C99

-- | Takes a type and a list of variable names and declares them.
varDecl :: Type -> [String] -> String
varDecl t vars = 
    cType t ++ " " ++ unwords (intersperse "," vars) ++ ";"

-- | Takes a type and a list of array names and their sizes declares them.
arrDecl :: Type -> [(String, Int)] -> String
arrDecl t arrs = 
    cType t ++ " " ++ unwords (intersperse "," mkArrs) ++ ";"
  where mkArrs = map (\(a,size) -> a ++ "[" ++ show size ++ "]") arrs

-- | Takes a type and a variable and initializes it.  It is YOUR responsibility
-- to ensure that @val@ is of type @t@.
varInit :: Show a => Type -> String -> a -> String
varInit t var val = cType t ++ " " ++ var ++ " = " ++ show val ++ ";"

-- | Takes a type and an array and initializes it.  It is YOUR responsibility to
-- ensure that @vals@ is of type @t@.
arrayInit :: Show a => Type -> String -> [a] -> String
arrayInit t var vals = 
  cType t ++ " " ++ var ++ "[" ++ show (length vals)
        ++ "] = " ++ bracesListShow ++ ";"
  where 
  -- Show a list with braces {} rather than brackets [].
  bracesListShow :: String
  bracesListShow =
    "{" ++ (foldl (++) "" $ intersperse "," $ map show vals) ++ "}"

-- | Declare function prototypes, given a return type, a function name, and a
-- | list of argument types.  Use 'Nothing' for a return type of @void@.
funcDecl :: Maybe Type -> String -> [Type] -> String
funcDecl t fn argTs = makeRet t ++ " " ++ fn ++ "(" ++ makeArgTs ++ ");"
  where makeRet Nothing = "void"
        makeRet (Just t') = cType t'
        makeArgTs | null argTs = "void"
                  | otherwise  = unwords (intersperse "," $ map cType argTs)

-- | Add an include of a library
includeBracket :: String -> String
includeBracket lib = "#include <" ++ lib ++ ">"

-- | Add an include of a header file
includeQuote :: String -> String
includeQuote lib = "#include \"" ++ lib ++ "\""

printfPre :: String -> String
printfPre = ("printf(\"" ++)

printfPost :: [String] -> String
printfPost vars = 
  let sep = if null vars then " " else ", " 
  in "\"" ++ sep ++ unwords (intersperse "," vars) ++ ");"

newline :: String
newline = "\\n"

-- | printf, with and without a newline (nl) character.
printf, printfNewline :: String -> [String] -> String
printfNewline text vars = (printfPre text) ++ newline ++ (printfPost vars)
printf text vars = (printfPre text) ++ (printfPost vars)