-- | Media segments with a fixed duration

{-# LANGUAGE UndecidableInstances #-}

module Data.MediaBus.Media.Segment
    ( Segment(..)
    , segmentContent
    , CanSegment(..)
    ) where

import           Data.MediaBus.Basics.Ticks
import           Data.MediaBus.Media.Media
import           Data.MediaBus.Media.Channels
import           Data.MediaBus.Media.Samples
import           Control.Lens
import           Data.Default
import           Data.Proxy
import           Test.QuickCheck
import           Control.DeepSeq
import           Text.Printf

-- | A segment is some content with a fixed (type level) duration.
newtype Segment (duration :: StaticTicks) c = MkSegment { _segmentContent :: c }
    deriving (NFData, Default, Arbitrary, Functor, Eq)

-- | An 'Iso' for the 'Segment' newtype.
segmentContent :: Iso (Segment d c) (Segment d c') c c'
segmentContent = iso _segmentContent MkSegment

instance (HasMedia c c') => HasMedia (Segment d c)  (Segment d c') where
  type MediaFrom (Segment d c) = MediaFrom c
  type MediaTo (Segment d c') = MediaTo c'
  media = segmentContent . media

instance (EachSample c c') => EachSample (Segment d c)  (Segment d c') where
  type SamplesFrom (Segment d c) = SamplesFrom c
  type SamplesTo (Segment d c') = SamplesTo c'
  eachSample = segmentContent . eachSample

instance (EachChannel c c') => EachChannel (Segment d c)  (Segment d c') where
  type ChannelsFrom (Segment d c) = ChannelsFrom c
  type ChannelsTo (Segment d c') = ChannelsTo c'
  eachChannel = segmentContent . eachChannel

instance (HasRate c) => HasRate (Segment d c) where
  type GetRate (Segment d c) = GetRate c
  type SetRate (Segment d c) r' = Segment d (SetRate c r')

instance (HasRate i, GetRate i ~ ri, SetRate i rj ~ j, KnownRate rj, CoerceRate i j ri rj) =>
      CoerceRate (Segment d i) (Segment d j) ri rj
    where
  coerceRate px (MkSegment !c) = MkSegment (coerceRate px c)

instance (HasStaticDuration d, Show c) =>
         Show (Segment d c) where
    showsPrec _d (MkSegment c) =
          showString "[| "
          . shows c
          . showString (printf " |%10s]" (show (getStaticDuration (Proxy :: Proxy d))))

instance KnownStaticTicks d =>
         HasStaticDuration (Segment d x) where
    type SetStaticDuration (Segment d x) pt = Segment pt x
    type GetStaticDuration (Segment d x) = d

instance HasStaticDuration d =>
         HasDuration (Segment d x) where
    getDuration _ = getStaticDuration (Proxy :: Proxy d)

-- | Class of types that support splitting values into parts with a certain
--   duration.
class CanSegment a where
    -- | Try to split the packet into the a part which has the given
    -- duration and a rest. If it is not possible to split of the desired duration,
    --  e.g. because the input data is too short, return `Nothing`.
    splitAfterDuration :: (HasStaticDuration d)
                       => proxy d
                       -> a
                       -> Maybe (Segment d a, a)