module Data.Kicad.SExpr.Write
( pretty
, write
, writeKeyword
)
where
import Data.Char (toLower, isLower, isNumber)
import Data.List (intercalate)
import Text.PrettyPrint.Compact

import Data.Kicad.SExpr.SExpr

{-| Pretty-print a 'SExpr' as a readable 'Doc'. -}
pretty :: SExpr -> Doc
pretty (List xs) = text "(" <> align (sep $ map pretty xs) <> text ")"
pretty atm = text $ write atm


{-| Serialize an SExpr as a compact s-expression 'String'. -}
write :: SExpr -> String
write (AtomKey kw)  = writeKeyword kw
write (AtomStr atm) |  (atm == "")
                    || head atm `elem` '.':'-':['0'..'9']
                    || foldr
                        (\c z -> z || c `elem` ')':'(':'\\':'\"':['\0'..' '])
                            False atm = show atm -- escaped string with quotes
                    | otherwise       = atm
-- this should just be printf "%g" but that doesn't work as it should
write (AtomDbl atm) = strip_zeros $ break (== '.') $ show atm
    where strip_zeros (s1,s2) = s1 ++ dot_if_needed (reverse
                                           $ dropWhile (=='0') $ reverse s2)
          dot_if_needed s     = if s == "." then "" else s
write (List    sxs) = "(" ++ unwords (map write sxs) ++ ")"

{-| Write a 'Keyword' as a 'String'. Removes \"Key\" from the 'Show'
   instance and converts the rest to underscore (e.g KeyFpTextType becomes
   "fp_text_type"). -}
writeKeyword :: Keyword -> String
writeKeyword = intercalate "_" . splitCapWords . drop 3 . show
    where
        splitCapWords "" = []
        splitCapWords (x:xs) =
            let (word, rest) = span (\c -> isLower c || isNumber c) xs
            in (toLower x : word) : splitCapWords rest