module Data.MediaBus.StreamSegment
    ( segmentC
    , segmentC'
    ) where

import           Conduit
import           Data.Monoid
import           Data.MediaBus.Stream
import           Data.MediaBus.Ticks
import           Data.MediaBus.Segment
import           Control.Lens
import           Data.Default
import           Data.MediaBus.Series
import           Data.Proxy
import           GHC.TypeLits

-- | The packetizer recombines incoming packets into 'Segment's of the given
-- size. The sequence numbers will be offsetted by the number extra frames
-- generated.
segmentC :: (Num s, Monad m, HasDuration c, CanSegment c, Monoid c, Default i, KnownNat r, Integral t, HasStaticDuration d)
         => Conduit (Stream i s (Ticks r t) p c) m (Stream i s (Ticks r t) p (Segment d c))
segmentC = segmentC' Proxy

segmentC' :: (Num s, Monad m, HasDuration c, CanSegment c, Monoid c, Default i, KnownNat r, Integral t, HasStaticDuration d)
          => proxy d
          -> Conduit (Stream i s (Ticks r t) p c) m (Stream i s (Ticks r t) p (Segment d c))
segmentC' dpx = evalStateC (0, Nothing) $ awaitForever go
  where
    segmentDurationInTicks =
        nominalDiffTime # segmentDuration
    segmentDuration = getStaticDuration dpx
    go (MkStream (Next (MkFrame !t !s !cIn))) = do
        !cRest <- _2 <<.= Nothing
        let tsOffset = negate (getDurationTicks cRest)
        !cRest' <- yieldLoop (maybe cIn (<> cIn) cRest) tsOffset
        _2 .= cRest'
      where
        yieldLoop !c !timeOffset =
            if getDuration c == segmentDuration
            then do
                yieldWithAdaptedSeqNumAndTimestamp (MkSegment c)
                return Nothing
            else case splitAfterDuration dpx c of
                Just (!packet, !rest) -> do
                    yieldWithAdaptedSeqNumAndTimestamp packet
                    _1 += 1
                    yieldLoop rest (timeOffset + segmentDurationInTicks)
                Nothing -> do
                    -- we just swallowed an incoming packet, therefore we need
                    -- to decrease the seqnums
                    _1 -=
                        1
                    return (Just c)
          where
            yieldWithAdaptedSeqNumAndTimestamp !p = do
                !seqNumOffset <- use _1
                yieldNextFrame (MkFrame (t + timeOffset) (s + seqNumOffset) p)
    go (MkStream (Start !frmCtx)) =
        yieldStartFrameCtx frmCtx