module Sound.Hommage.Seq
 ( runNumNotation 
 , bpmToDur 
 , Seq (..)
 , noteSeq
 , noteSeq'
 , mixdownNumSeq
 , applySeq
-- , applySeqS
 , applySeqE
 , filterSeq 
 )
 where

--import Control.Monad.Reader
--import Control.Monad.State
import Data.List
import Data.Ratio

import Sound.Hommage.Misc
import Sound.Hommage.Notation

-------------------------------------------------------------------------------
-- | A 'Seq' represents a temporal sequence of \"Moments\". Each moment 
--   can contain some events of type @a@.
newtype Seq a = SEQ { unSeq :: [[a]] }

instance Arrangeable (Seq a) where
 parallel (SEQ xs) (SEQ ys) = SEQ $ merge (++) xs ys
 sequent  (SEQ xs) (SEQ ys) = SEQ (xs ++ ys)

--instance IsDur d => Musical (d -> Seq a) where
-- rest = \d -> SEQ ( replicate (fromIntegral $ absDur $ durFrom d) [] )

instance IsDur d => Musical (WithDur d (Seq a)) where
 rest = WithDur $ \d -> SEQ ( replicate (fromIntegral $ absDur $ durFrom d) [] )

--instance Musical ((Dur, s) -> Seq a) where
-- rest = \d -> SEQ ( replicate (fromIntegral $ absDur $ fst d) [] )

-- | Creates a 'Seq' with the length that the 'Reader' reads. 
--   (The length will be in any case at least 1).
noteSeq :: IsDur d => a -> WithDur d (Seq a)
noteSeq a = WithDur $ \d -> let l = fromIntegral $ absDur $ durFrom d in -- if l > 0 then 
                            SEQ ( [a] : replicate (l-1) [] )             -- else emptySeq

noteSeq' :: IsDur d => WithDur d a -> WithDur d (Seq a)
noteSeq' ra = WithDur $ \d -> unWithDur (noteSeq $ unWithDur ra d) d
-------------------------------------------------------------------------------
filterSeq :: (a -> Maybe b) -> Seq a -> Seq b
filterSeq check (SEQ xs) = SEQ $ map filt xs
 where
  filt (a:as) = case check a of
                 Nothing -> filt as
                 Just b  -> b : filt as
  filt []     = []

applySeq :: (s -> s) -> Seq (s -> a) -> s -> (Seq a) 
applySeq next (SEQ l) = \init -> SEQ (loop init l)
 where
  loop state (x:xs) = let state' = next state 
                      in seq state ((map_ ($ state) x : seq state' (loop state' xs)))
--                      in seq state (map_ ($ state) x : seq state' (loop state' xs))
--                      in (map_ ($ state) x : (loop state' xs))
  loop _     _      = []


applySeqE :: (s -> s) -> Seq (Either (s -> a) (s -> s)) -> s -> Seq a
applySeqE next (SEQ l) = \init -> SEQ (loop init l)
 where
  loop state (x:xs) = let (fs, us) = uneitherlist x
                          state'  = foldl (flip ($)) state us
                          state'' = next state'
                      in seq state' ((map ($ state') fs : seq state'' (loop state'' xs)))
  loop _     _      = []

{-
applySeqS :: (s -> s) -> Seq (State s a) -> Reader s (Seq a)
applySeqS next (SEQ l) = Reader $ \init -> SEQ (loop init l)
 where
  loop state (x:xs) = let (a, state') = runState (iter x) state 
                          state'' = next state'
                      in a : {- seq state'' -} (loop state'' xs)
  loop _     _      = []
  iter (f:fs) = f >>= \a -> iter fs >>= \r -> return (a : r)
  iter []     = return []
-}
-------------------------------------------------------------------------------
mixdownNumSeq :: Num a => Seq [a] -> [a]
mixdownNumSeq (SEQ s) = loop s
 where
  -- loop :: Num a => [[[a]]] -> [a]
  loop [] = []
  loop (fs:fl) = case map sum $ transpose fs of
                  []  -> 0 : loop fl
                  h:t -> seq h (h : (merge (+) t (loop fl)))

runNumNotation :: (IsDur d, Num a) => Notation (WithDur d [a]) -> d -> [a]
runNumNotation n = \d -> mixdownNumSeq $ unWithDur (runNotationWith noteSeq' n) d

bpmToDur :: Double -> Dur
bpmToDur bpm = round (44100 * (240.0 / bpm)) % 1
-------------------------------------------------------------------------------