-- | 'NRT' operations.
module Sound.SC3.Server.NRT.Edit where

import Data.List {- base -}
import qualified Data.List.Ordered as L {- data-ordlist -}
import Sound.OSC {- hosc -}

import Sound.SC3.Server.Command
import Sound.SC3.Server.NRT

-- * List

-- | Inserts at the first position where it compares less but not
-- equal to the next element.
--
-- > import Data.Function
-- > insertBy (compare `on` fst) (3,'x') (zip [1..5] ['a'..])
-- > insertBy_post (compare `on` fst) (3,'x') (zip [1..5] ['a'..])
insertBy_post :: (a -> a -> Ordering) -> a -> [a] -> [a]
insertBy_post :: (a -> a -> Ordering) -> a -> [a] -> [a]
insertBy_post a -> a -> Ordering
cmp a
e [a]
l =
    case [a]
l of
      [] -> [a
e]
      a
h:[a]
l' -> case a -> a -> Ordering
cmp a
e a
h of
                Ordering
LT -> a
e a -> [a] -> [a]
forall a. a -> [a] -> [a]
: [a]
l
                Ordering
_ -> a
h a -> [a] -> [a]
forall a. a -> [a] -> [a]
: (a -> a -> Ordering) -> a -> [a] -> [a]
forall a. (a -> a -> Ordering) -> a -> [a] -> [a]
insertBy_post a -> a -> Ordering
cmp a
e [a]
l'

-- | 'insertBy_post' using 'compare'.
insert_post :: Bundle -> [Bundle] -> [Bundle]
insert_post :: Bundle -> [Bundle] -> [Bundle]
insert_post = (Bundle -> Bundle -> Ordering) -> Bundle -> [Bundle] -> [Bundle]
forall a. (a -> a -> Ordering) -> a -> [a] -> [a]
insertBy_post Bundle -> Bundle -> Ordering
forall a. Ord a => a -> a -> Ordering
compare

-- | Apply /f/ at all but last element, and /g/ at last element.
--
-- > at_last (* 2) negate [1..4] == [2,4,6,-4]
at_last :: (a -> b) -> (a -> b) -> [a] -> [b]
at_last :: (a -> b) -> (a -> b) -> [a] -> [b]
at_last a -> b
f a -> b
g [a]
x =
    case [a]
x of
      [] -> []
      [a
i] -> [a -> b
g a
i]
      a
i:[a]
x' -> a -> b
f a
i b -> [b] -> [b]
forall a. a -> [a] -> [a]
: (a -> b) -> (a -> b) -> [a] -> [b]
forall a b. (a -> b) -> (a -> b) -> [a] -> [b]
at_last a -> b
f a -> b
g [a]
x'

-- * NRT

-- | Merge two NRT scores.  Retains internal 'nrt_end' messages.
nrt_merge :: NRT -> NRT -> NRT
nrt_merge :: NRT -> NRT -> NRT
nrt_merge (NRT [Bundle]
p) (NRT [Bundle]
q) = [Bundle] -> NRT
NRT ([Bundle] -> [Bundle] -> [Bundle]
forall a. Ord a => [a] -> [a] -> [a]
L.merge [Bundle]
p [Bundle]
q)

-- | Merge a set of NRT.  Retains internal 'nrt_end' messages.
nrt_merge_set :: [NRT] -> NRT
nrt_merge_set :: [NRT] -> NRT
nrt_merge_set = (NRT -> NRT -> NRT) -> NRT -> [NRT] -> NRT
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr NRT -> NRT -> NRT
nrt_merge NRT
nrt_empty

-- | The empty NRT.
nrt_empty :: NRT
nrt_empty :: NRT
nrt_empty = [Bundle] -> NRT
NRT []

-- | Add bundle at first permissable location of NRT.
nrt_insert_pre :: Bundle -> NRT -> NRT
nrt_insert_pre :: Bundle -> NRT -> NRT
nrt_insert_pre Bundle
p (NRT [Bundle]
q) = [Bundle] -> NRT
NRT (Bundle -> [Bundle] -> [Bundle]
forall a. Ord a => a -> [a] -> [a]
insert Bundle
p [Bundle]
q)

-- | Add bundle at last permissable location of NRT.
nrt_insert_post :: Bundle -> NRT -> NRT
nrt_insert_post :: Bundle -> NRT -> NRT
nrt_insert_post Bundle
p (NRT [Bundle]
q) = [Bundle] -> NRT
NRT (Bundle -> [Bundle] -> [Bundle]
insert_post Bundle
p [Bundle]
q)

-- | 'bundleTime' of 'last' of 'nrt_bundles'.
nrt_end_time :: NRT -> Time
nrt_end_time :: NRT -> Time
nrt_end_time = Bundle -> Time
bundleTime (Bundle -> Time) -> (NRT -> Bundle) -> NRT -> Time
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Bundle] -> Bundle
forall a. [a] -> a
last ([Bundle] -> Bundle) -> (NRT -> [Bundle]) -> NRT -> Bundle
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NRT -> [Bundle]
nrt_bundles

-- | Apply temporal and message functions to bundle.
bundle_map :: (Time -> Time) -> ([Message] -> [Message]) -> Bundle -> Bundle
bundle_map :: (Time -> Time) -> ([Message] -> [Message]) -> Bundle -> Bundle
bundle_map Time -> Time
t_f [Message] -> [Message]
m_f (Bundle Time
t [Message]
m) = Time -> [Message] -> Bundle
Bundle (Time -> Time
t_f Time
t) ([Message] -> [Message]
m_f [Message]
m)

-- | Delete any internal 'nrt_end' messages, and require one at the
-- final bundle.
nrt_close :: NRT -> NRT
nrt_close :: NRT -> NRT
nrt_close (NRT [Bundle]
l) =
    let is_nrt_end_msg :: Message -> Bool
is_nrt_end_msg = ([Char] -> [Char] -> Bool
forall a. Eq a => a -> a -> Bool
== [Char]
"/nrt_end") ([Char] -> Bool) -> (Message -> [Char]) -> Message -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Message -> [Char]
messageAddress
        rem_end_msg :: Bundle -> Bundle
rem_end_msg = (Time -> Time) -> ([Message] -> [Message]) -> Bundle -> Bundle
bundle_map Time -> Time
forall a. a -> a
id ((Message -> Bool) -> [Message] -> [Message]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (Message -> Bool) -> Message -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Message -> Bool
is_nrt_end_msg))
        req_end_msg :: Bundle -> Bundle
req_end_msg = let f :: [Message] -> [Message]
f [Message]
m = if (Message -> Bool) -> [Message] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Message -> Bool
is_nrt_end_msg [Message]
m
                                then [Message]
m
                                else [Message]
m [Message] -> [Message] -> [Message]
forall a. [a] -> [a] -> [a]
++ [Message
nrt_end]
                      in (Time -> Time) -> ([Message] -> [Message]) -> Bundle -> Bundle
bundle_map Time -> Time
forall a. a -> a
id [Message] -> [Message]
f
    in [Bundle] -> NRT
NRT ((Bundle -> Bundle) -> (Bundle -> Bundle) -> [Bundle] -> [Bundle]
forall a b. (a -> b) -> (a -> b) -> [a] -> [b]
at_last Bundle -> Bundle
rem_end_msg Bundle -> Bundle
req_end_msg [Bundle]
l)

-- | Append /q/ to /p/, assumes last timestamp at /p/ precedes first at /q/.
nrt_append :: NRT -> NRT -> NRT
nrt_append :: NRT -> NRT -> NRT
nrt_append (NRT [Bundle]
p) (NRT [Bundle]
q) = [Bundle] -> NRT
NRT ([Bundle]
p [Bundle] -> [Bundle] -> [Bundle]
forall a. [a] -> [a] -> [a]
++ [Bundle]
q)