content: {-# LANGUAGE CPP           #-}
  {-# LANGUAGE DeriveFunctor #-}
  {-# LANGUAGE LambdaCase    #-}
  {-# LANGUAGE Rank2Types    #-}
  {-# LANGUAGE TypeFamilies  #-}
  
  module Pos.Binary.Limit
         ( Limit (..)
         , (<+>)
         , mlBool
         , mlMaybe
         , mlEither
         , mlTuple
         , mlTriple
         , vectorOf
         , vectorOfNE
         ) where
  
  import           Universum
  
  -- | A limit on the length of something (in bytes).
  --   TODO should check for overflow in the Num instance.
  --   Although, if the limit is anywhere near maxBound :: Word32 then something
  --   is almost certainly amiss.
  newtype Limit t = Limit { getLimit :: Word32 }
      deriving (Eq, Ord, Show, Num, Enum, Real, Integral)
  
  instance Functor Limit where
      fmap _ (Limit x) = Limit x
  
  -- TODO: use <*> instead of <+>
  infixl 4 <+>
  (<+>) :: Limit (a -> b) -> Limit a -> Limit b
  Limit x <+> Limit y = Limit $ x + y
  
  mlBool :: Limit Bool
  mlBool = 1
  
  mlMaybe :: Limit a -> Limit (Maybe a)
  mlMaybe lim = Just <$> lim + 1
  
  mlEither :: Limit a -> Limit b -> Limit (Either a b)
  mlEither limA limB = 1 + max (Left <$> limA) (Right <$> limB)
  
  mlTuple :: Limit a -> Limit b -> Limit (a, b)
  mlTuple limA limB = (,) <$> limA <+> limB
  
  mlTriple :: Limit a -> Limit b -> Limit c -> Limit (a, b, c)
  mlTriple limA limB limC = (,,) <$> limA <+> limB <+> limC
  
  -- | Given a limit for a list item, generate limit for a list with N elements
  vectorOf :: Int -> Limit l -> Limit [l]
  vectorOf k (Limit x) =
      Limit $ encodedListLength + x * (fromIntegral k)
    where
      -- should be enough for most reasonable cases
      -- FIXME this is silly.
      -- Better solution: never read in an arbitrary-length structure from the
      -- network. If you want a list, read in one item at a time.
      encodedListLength = 20
  
  vectorOfNE :: Int -> Limit l -> Limit (NonEmpty l)
  vectorOfNE k (Limit x) =
      Limit $ encodedListLength + x * (fromIntegral k)
    where
      encodedListLength = 20
  