module Bytezap.Write.Internal where

import Bytezap.Poke ( Poke )

-- | A 'Poke' buffer write operation with the associated length to be written.
--
-- The length may be either exact or a maximum.
--
-- TODO strictness?
data Write (lt :: LengthType) s = Write
  { forall (lt :: LengthType) s. Write lt s -> Int
writeLength :: Int
  -- ^ Length of the write in bytes.
  --
  -- This is not statically asserted. Any time you construct a 'Write', you must
  -- promise this.
  --
  -- For @'Write' 'ExactLength' s@, this is an exact measurement.
  -- For @'Write' 'MaxLength'   s@, this is a maximum.

  , forall (lt :: LengthType) s. Write lt s -> Poke s
writeOp :: Poke s
  -- ^ The 'Poke' buffer write operation.
  }

-- | What a buffer write length field means.
data LengthType
  = ExactLength -- ^ Exact length to be written.
  | MaxLength   -- ^ Maximum length to be written.

-- | Sequence the writes, sum the lengths.
instance Semigroup (Write lt s) where
    -- TODO strictness? INLINE[1]? INLINE[0]?
    <> :: Write lt s -> Write lt s -> Write lt s
(<>) = Write lt s -> Write lt s -> Write lt s
forall (ltl :: LengthType) s (ltr :: LengthType)
       (lt :: LengthType).
Write ltl s -> Write ltr s -> Write lt s
writeCombine

-- | The empty 'Write' is the empty 'Poke', which writes zero bytes.
instance Monoid (Write lt s) where
    mempty :: Write lt s
mempty = Int -> Poke s -> Write lt s
forall (lt :: LengthType) s. Int -> Poke s -> Write lt s
Write Int
0 Poke s
forall a. Monoid a => a
mempty

-- | Turn a @'Write' 'ExactLength'@ into a @'Write' 'MaxLength'@.
writeMax :: Write ExactLength s -> Write MaxLength s
writeMax :: forall s. Write 'ExactLength s -> Write 'MaxLength s
writeMax (Write Int
l Poke s
p) = Int -> Poke s -> Write 'MaxLength s
forall (lt :: LengthType) s. Int -> Poke s -> Write lt s
Write Int
l Poke s
p

-- | Sequence a @'Write' 'MaxLength'@ and a @'Write' 'ExactLength'@
--   left-to-right.
writeMaxExact :: Write MaxLength s -> Write ExactLength s -> Write MaxLength s
writeMaxExact :: forall s.
Write 'MaxLength s -> Write 'ExactLength s -> Write 'MaxLength s
writeMaxExact = Write 'MaxLength s -> Write 'ExactLength s -> Write 'MaxLength s
forall (ltl :: LengthType) s (ltr :: LengthType)
       (lt :: LengthType).
Write ltl s -> Write ltr s -> Write lt s
writeCombine

-- | Sequence a @'Write' 'MaxLength'@ and a @'Write' 'ExactLength'@
--   left-to-right.
writeExactMax :: Write ExactLength s -> Write MaxLength s -> Write MaxLength s
writeExactMax :: forall s.
Write 'ExactLength s -> Write 'MaxLength s -> Write 'MaxLength s
writeExactMax = Write 'ExactLength s -> Write 'MaxLength s -> Write 'MaxLength s
forall (ltl :: LengthType) s (ltr :: LengthType)
       (lt :: LengthType).
Write ltl s -> Write ltr s -> Write lt s
writeCombine

-- | Sequence two 'Write's left-to-right.
--
-- Unsafe, as it ignores 'LengthType's.
--
-- TODO strictness? INLINE[1]? INLINE[0]?
writeCombine :: Write ltl s -> Write ltr s -> Write lt s
writeCombine :: forall (ltl :: LengthType) s (ltr :: LengthType)
       (lt :: LengthType).
Write ltl s -> Write ltr s -> Write lt s
writeCombine (Write Int
ll Poke s
lp) (Write Int
rl Poke s
rp) = Int -> Poke s -> Write lt s
forall (lt :: LengthType) s. Int -> Poke s -> Write lt s
Write (Int
ll Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
rl) (Poke s
lp Poke s -> Poke s -> Poke s
forall a. Semigroup a => a -> a -> a
<> Poke s
rp)