-- | Custom data type for timestamps (milliseconds since 1970)

{-# LANGUAGE DeriveDataTypeable #-}
module Data.ULID.TimeStamp (
    ULIDTimeStamp,
    mkULIDTimeStamp,
    getULIDTimeStamp
) where

import           Control.DeepSeq
import           Control.Monad
import           Data.Binary
import           Data.Binary.Roll
import           Data.Data
import           Data.Maybe
import           Data.Text as T
import           Data.Time.Clock
import           Data.Time.Clock.POSIX

import qualified Data.ULID.Base32 as B32


numBytes = 6 -- 48 bits

-- | UNIX time in milliseconds
newtype ULIDTimeStamp = ULIDTimeStamp Integer
    deriving (Eq, Ord, Typeable, Data)

instance Show ULIDTimeStamp where
    show (ULIDTimeStamp i) = T.unpack $ B32.encode 10 i

instance Read ULIDTimeStamp where
    readsPrec _ = fmap
        (\(int, rest)->(ULIDTimeStamp int, T.unpack rest))
        . (B32.decode) 10
        . T.pack

instance Binary ULIDTimeStamp where
    put (ULIDTimeStamp i) = mapM_ put (unroll numBytes i)
    get = ULIDTimeStamp <$> roll <$> replicateM numBytes get

instance NFData ULIDTimeStamp where
    rnf (ULIDTimeStamp i) = rnf i


-- | Generate a ULID Timestamp based on a specified time
mkULIDTimeStamp
  :: POSIXTime  -- ^ Specified UNIX time with millisecond precision
                -- (e.g. 1469918176.385)
  -> ULIDTimeStamp
mkULIDTimeStamp = ULIDTimeStamp . round . (*1000)


-- | Generate a ULID Timestamp based on current system UNIX time
getULIDTimeStamp :: IO ULIDTimeStamp
getULIDTimeStamp = mkULIDTimeStamp <$> getPOSIXTime