module Main where

import Data.Binary
import Data.Binary.Typed

import qualified Data.ByteString.Lazy as BSL
import Data.Typeable (Typeable)
import Data.Int
import Text.Printf

main :: IO ()
main = do
      putStrLn "Comparison of message lengths depending on serialization method"
      putStrLn "==============================================================="
      putStrLn ""
      putStrLn (ppr (measure "maxBound :: Int"  (maxBound :: Int)))
      putStrLn (ppr (measure "\"Hello\""        "Hello"))
      putStrLn (ppr (measure "100 chars lipsum" lipsum))
      putStrLn (ppr (measure "Right (Left \"Hello\") :: Either (Char, Int) (Either String (Maybe Integer))"
                             (Right (Left "Hello") :: Either (Char, Int) (Either String (Maybe Integer)))))


lipsum :: String
lipsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam\
         \ vitae lacinia tellus. Maecenas posuere."

data EncodeLengths = EL { info           :: String
                        , binary         :: Int64
                        , typedUntyped   :: Int64
                        , typedHashed32  :: Int64
                        , typedHashed64  :: Int64
                        , typedShown     :: Int64
                        , typedFull      :: Int64
                        }

-- | Prettyprint 'EncodeLengths'
ppr :: EncodeLengths -> String
ppr el = unlines [ info el
                 , printf "  Binary:         %d" (binary el)
                 , printf "  Typed/Untyped:  %d (+%d, +%2.2f%%)"
                          (typedUntyped el)
                          (absolute (binary el) (typedUntyped el))
                          (percent  (binary el) (typedUntyped el))
                 , printf "  Typed/Hashed32: %d (+%d, +%2.2f%%)"
                          (typedHashed32 el)
                          (absolute (binary el) (typedHashed32 el))
                          (percent  (binary el) (typedHashed32 el))
                 , printf "  Typed/Hashed64: %d (+%d, +%2.2f%%)"
                          (typedHashed64 el)
                          (absolute (binary el) (typedHashed64 el))
                          (percent  (binary el) (typedHashed64 el))
                 , printf "  Typed/Shown:    %d (+%d, +%2.2f%%)"
                          (typedShown el)
                          (absolute (binary el) (typedShown el))
                          (percent  (binary el) (typedShown el))
                 , printf "  Typed/Full:     %d (+%d, +%2.2f%%)"
                          (typedFull el)
                          (absolute (binary el) (typedFull el))
                          (percent  (binary el) (typedFull el))
                 ]

-- | Calculate how much percent the first parameter deviates from the second.
percent :: Int64 -> Int64 -> Double
percent base x = let x'    = fromIntegral x
                     base' = fromIntegral base
                 in  abs ((x'-base')*100/base')


-- | Absolute deviation of the second from the first parameter
absolute :: Int64 -> Int64 -> Int64
absolute base x = x - base

-- | Measure the message lengths generated by different encodings.
measure :: (Binary a, Typeable a) => String -> a -> EncodeLengths
measure i x = EL { info = i
                 , binary         = BSL.length binary'
                 , typedUntyped   = BSL.length typedUntyped'
                 , typedHashed32  = BSL.length typedHashed32'
                 , typedHashed64  = BSL.length typedHashed64'
                 , typedShown     = BSL.length typedShown'
                 , typedFull      = BSL.length typedFull'
                 }

      where

      binary'        = encode                x
      typedUntyped'  = encodeTyped  Untyped  x
      typedHashed32' = encodeTyped  Hashed32 x
      typedHashed64' = encodeTyped  Hashed64 x
      typedShown'    = encodeTyped  Shown    x
      typedFull'     = encodeTyped  Full     x
