{-# LANGUAGE IncoherentInstances, TypeSynonymInstances #-}
-- | Class and Instances for pretty printing Your data.
--
-- Minimal definition for instances @Pretty@  - method @pp@ . 
module Data.JSON2.Pretty where

import Data.JSON2.Types
import Text.PrettyPrint
import Data.Ratio
import qualified Data.Map as Map
import Data.Map(Map)
import qualified Data.Set as Set
import Data.Set(Set)

class Show a => Pretty a where
    pp :: a -> Doc
    pp = text . show

    pprint :: a -> String
    pprint = render . pp

-- Instances

instance Pretty ()
instance Pretty Char
instance Pretty Int        
instance Pretty Integer
instance Pretty Float
instance Pretty Double
instance Pretty Rational
-- String
instance Pretty String where
    pp = text
-- Maybe
instance Pretty a => Pretty (Maybe a) where
    pp (Just x) = text "Just" <+> pp x
    pp Nothing  = text "Noting"
-- Either
instance (Pretty a,Pretty b) => Pretty (Either a b) where
    pp (Right x) = text "Right" <+> pp x
    pp (Left x) = text "Left" <+> pp x
-- List
instance Pretty a => Pretty [a] where
    pp xs =  brackets $ fsep $ punctuate comma $ map pp xs
-- Map
instance (Pretty k, Pretty v) => Pretty (Map k v) where
    pp m = braces $ sep $ punctuate comma $ map ppair xs
        where xs = Map.toList m
              ppair (k,v) = pp k <> colon <+> pp v
-- Set
instance Pretty a => Pretty (Set a) where
    pp s =  braces $ fsep $ punctuate comma $ map pp (Set.toList s)
-- Tuples
instance (Pretty a, Pretty b) => Pretty (a, b) where
    pp (a, b) =  parens $ pp a <> comma <+> pp b
instance (Pretty a, Pretty b, Pretty c) => Pretty (a, b, c) where
    pp (a, b, c) =  parens $ pp a <> comma <+> pp b <> comma <+> pp c
instance (Pretty a, Pretty b, Pretty c, Pretty d) => Pretty (a, b, c, d) where
    pp (a, b, c, d) =  parens $ pp a <> comma <+> pp b <> comma <+> pp c
                             <> comma <+> pp d
instance (Pretty a, Pretty b, Pretty c, Pretty d, Pretty e)
                             => Pretty (a, b, c, d, e) where
    pp (a, b, c, d, e) =  parens $ pp a <> comma <+> pp b <> comma <+> pp c
                             <> comma <+> pp d <> comma <+> pp e
-- JSON
instance Pretty Json where
    pp (JNumber x)
        | denominator x == 1 = text $ show (numerator x)
        | otherwise          = text $ show (fromRational x :: Double)
    pp (JBool True) = text "true"
    pp (JBool False) = text "false"
    pp JNull = text "null"
    pp (JString x) = doubleQuotes $ text $ escJString x
    pp (JArray xs) =  brackets $ fsep $ punctuate comma $ map pp xs
    pp (JObject m)= braces $ sep $ punctuate comma $ map ppair xs
        where xs = Map.toList m
              ppair (k,v) = (doubleQuotes $ text k) <> colon <+> pp v