    {- |
    System Common messages
    -}
    module Sound.MIDI.Message.System.Common (
       T(..), TimeNibbleType(..), get, put,
       ) where

    import           Sound.MIDI.Parser.Primitive
    import qualified Sound.MIDI.Parser.Class as Parser

    import Control.Monad.Trans.Class (lift, )
    import Control.Monad (liftM, liftM2, )

    import qualified Sound.MIDI.Writer.Basic as Writer
    import Sound.MIDI.Monoid ((+#+))

    import qualified Sound.MIDI.Bit as Bit

    import Data.Ix(Ix)

    data T =
         TimeCodeQuarterFrame TimeNibbleType Int
       | SongPositionPointer Int
       | SongSelect Int
       | TuneRequest
    --   | EndOfSystemExclusive

    data TimeNibbleType =
         FrameLS
       | FrameMS
       | SecondsLS
       | SecondsMS
       | MinutesLS
       | MinutesMS
       | HoursLS
       | HoursMS -- ^ also includes SMPTE type
       deriving (Eq, Ord, Show, Enum, Ix)

    -- * serialization

    get :: Parser.C parser => Int -> Parser.Fallible parser T
    get code =
       case code of
          0xF1 ->
             do dat <- get1
                let (nib, value)  = Bit.splitAt 4 dat
                let (msb, nibble) = Bit.splitAt 3 nib
                lift $ Parser.warnIf (msb/=0)
                   "TimeCodeQuarterFrame: most significant bit must 0"
                return $ TimeCodeQuarterFrame (toEnum nibble) value
          0xF2 -> liftM2 (\lsb msb -> SongPositionPointer (lsb + Bit.shiftL 7 msb)) get1 get1
          0xF3 -> liftM SongSelect get1
          0xF6 -> return TuneRequest
          _    -> Parser.giveUp ("invalid System Common code:" ++ show code)

    put :: Writer.C writer => T -> writer
    put msg =
       case msg of
          TimeCodeQuarterFrame nibble value ->
             Writer.putByte 0xF1 +#+
             Writer.putIntAsByte (Bit.shiftL 4 (fromEnum nibble) + value)
          SongPositionPointer pos ->
             Writer.putByte 0xF2 +#+
                let (msb,lsb) = Bit.splitAt 7 pos
                in  Writer.putIntAsByte lsb +#+
                    Writer.putIntAsByte msb
          SongSelect song ->
             Writer.putByte 0xF3 +#+
             Writer.putIntAsByte song
          TuneRequest ->
             Writer.putByte 0xF6