module C where

import Data.List
import Text.PrettyPrint.HughesPJ

newtype Program = Program [TopLevel]

data TopLevel
    = Function Type Var [Var] [Body]
    | Include FilePath
    | GlobalVar Type Var (Maybe (Maybe Int)) (Maybe Atom)
    | TVerbatim String
      deriving Show

data Body
    = App Var [Atom]
    | Assign Var Atom
    | Verbatim String
      deriving Show

data Atom
    = AtomVar String
    | AtomChar Char
      deriving Show

type Var = String
type Type = String

data TypeSpecifier
    = Void
    | Char
    | Short
    | Int
    | Long
    | Float
    | Double
    | Signed
    | Unsigned
    | Struct
    | Enum
    | TypeDef
      deriving Show


ppProgram :: Program -> Doc
ppProgram (Program tl) = vcat $ map ppTopLevel tl

ppTopLevel :: TopLevel -> Doc
ppTopLevel (Include fp) = text "#include" <+> text fp
ppTopLevel (Function retType name args body) =
    text retType <+> text name <+> parens (commaSep text args) $+$
         lbrace $+$
                nest 2 (vcat (map ppBody body)) $+$
         rbrace
ppTopLevel (GlobalVar vtype name arraySize val) =
    text vtype <+> text name <> ppArraySize arraySize <> maybe empty (\v -> space <> equals <+> ppAtom v) val <> semi
ppTopLevel (TVerbatim str) = text str

ppArraySize :: Maybe (Maybe Int) -> Doc
ppArraySize Nothing = empty
ppArraySize (Just Nothing) = text "[]"
ppArraySize (Just (Just n)) = brackets (int n)

ppBody :: Body -> Doc
ppBody (App var atoms) =
    text var <+> parens (commaSep ppAtom atoms) <> semi
ppBody (Assign var atom) =
    text var <+> equals <+> ppAtom atom <> semi
ppBody (Verbatim str) =
    text str

ppAtom :: Atom -> Doc
ppAtom (AtomVar var) = text var
ppAtom (AtomChar c) = text (show c)

commaSep :: (a -> Doc) -> [a] -> Doc
commaSep pp = hcat . punctuate comma . map pp 
