{-# LANGUAGE
    TypeFamilies,
    DeriveFunctor,
    DeriveFoldable,
    DeriveDataTypeable,
    FlexibleInstances,
    FlexibleContexts,
    ConstraintKinds,
    GeneralizedNewtypeDeriving,
    NoMonomorphismRestriction #-}

-------------------------------------------------------------------------------------
-- |
-- Copyright   : (c) Hans Hoglund 2012
--
-- License     : BSD-style
--
-- Maintainer  : hans@hanshoglund.se
-- Stability   : experimental
-- Portability : non-portable (TF,GNTD)
--
-- Provides articulation.
--
-------------------------------------------------------------------------------------


module Music.Score.Articulation (
        HasArticulation(..),
        ArticulationT(..),

        -- ** Accents
        accent,
        marcato,
        accentLast,
        marcatoLast,
        accentAll,
        marcatoAll,

        -- ** Phrasing
        tenuto,
        separated,
        staccato,
        portato,
        legato,
        spiccato,

        -- ** Miscellaneous
        resetArticulation,

  ) where

import Data.Ratio
import Data.Foldable
import Data.Typeable
import Data.Semigroup
import qualified Data.List as List
import Data.VectorSpace
import Data.AffineSpace

import Music.Score.Voice
import Music.Score.Score
import Music.Time
import Music.Score.Part
import Music.Score.Combinators

class HasArticulation a where
    setBeginSlur :: Bool -> a -> a
    setContSlur :: Bool -> a -> a
    setEndSlur :: Bool -> a -> a
    setAccLevel :: Int -> a -> a
    setStaccLevel :: Int -> a -> a

newtype ArticulationT a = ArticulationT { getArticulationT :: (Bool, Bool, Int, Int, a, Bool) }
    deriving (Eq, Show, Ord, Functor, Foldable, Typeable)

--------------------------------------------------------------------------------
-- Articulation
--------------------------------------------------------------------------------

-- Accents

accent      :: (HasEvents s, HasPart' a, HasArticulation a) => s a -> s a
marcato     :: (HasEvents s, HasPart' a, HasArticulation a) => s a -> s a
accentAll   :: (HasEvents s, HasPart' a, HasArticulation a) => s a -> s a
marcatoAll  :: (HasEvents s, HasPart' a, HasArticulation a) => s a -> s a
accentLast  :: (HasEvents s, HasPart' a, HasArticulation a) => s a -> s a
marcatoLast :: (HasEvents s, HasPart' a, HasArticulation a) => s a -> s a
accent      = mapPhrase (setAccLevel 1) id id
marcato     = mapPhrase (setAccLevel 2) id id
accentAll   = mapPhrase (setAccLevel 1) (setAccLevel 1) (setAccLevel 1)
marcatoAll  = mapPhrase (setAccLevel 2) (setAccLevel 2) (setAccLevel 2)
accentLast  = mapPhrase id id (setAccLevel 1)
marcatoLast = mapPhrase id id (setAccLevel 2)

-- Phrasing

tenuto      :: (HasEvents s, HasPart' a, HasArticulation a) => s a -> s a
separated   :: (HasEvents s, HasPart' a, HasArticulation a) => s a -> s a
staccato    :: (HasEvents s, HasPart' a, HasArticulation a) => s a -> s a
portato     :: (HasEvents s, HasPart' a, HasArticulation a) => s a -> s a
legato      :: (HasEvents s, HasPart' a, HasArticulation a) => s a -> s a
spiccato    :: (HasEvents s, HasPart' a, HasArticulation a) => s a -> s a
tenuto      = mapPhrase (setStaccLevel (-2)) (setStaccLevel (-2)) (setStaccLevel (-2))
separated   = mapPhrase (setStaccLevel (-1)) (setStaccLevel (-1)) (setStaccLevel (-1))
staccato    = mapPhrase (setStaccLevel 1) (setStaccLevel 1) (setStaccLevel 1)
portato     = staccato . legato
legato      = mapPhrase (setBeginSlur True) id (setEndSlur True)
spiccato    = mapPhrase (setStaccLevel 2) (setStaccLevel 2) (setStaccLevel 2)

resetArticulation :: HasArticulation c => c -> c
resetArticulation = setBeginSlur False . setContSlur False . setEndSlur False . setAccLevel 0 . setStaccLevel 0