module Interpret where

import Types
import Control.Lens

-- | calculates the total duration of a `Beat`
totalDur :: Beat -> Rational
totalDur (Single hit)     = hit ^. dur
totalDur (Series c1 c2)   = totalDur c1 + totalDur c2
totalDur (Parallel c1 c2) = max (totalDur c1) (totalDur c2)
totalDur None             = 0

-- | merge two sorted lists of `Hit`s
mergeHits :: [Hit] -> [Hit] -> [Hit]
mergeHits [] ys = ys
mergeHits xs [] = xs
mergeHits (x:xs) (y:ys)
  | x <= y    = x : mergeHits xs (y:ys)
  | otherwise = y : mergeHits (x:xs) ys

-- | apply a tempo to a composition
applyTempo :: Rational -> Composition a -> Composition a
applyTempo n = cmap (\h -> h & dur *~ (60000 / n))

-- | convert a `Composition` to a list of `Hit`s
toHits :: Composition a -> [Hit]
toHits (Composition (beat, _)) = go 0 beat
  where
    go d (Single hit)     = [hit & dur .~ d]
    go d (Series b1 b2)   = go d b1 ++ go (d + totalDur b1) b2
    go d (Parallel b1 b2) = mergeHits (go d b1) (go d b2)
    go _ None             = []

-- | interpret a `Composition` with a given tempo
interpret :: Rational -> Composition a -> [Hit]
interpret n c = toHits $ applyTempo n c