module Music.Score.Export.ArticulationNotation (
    Slur(..),
    Mark(..),
    ArticulationNotation(..),
    notateArticulation,
  ) where
import Data.Semigroup
import Data.Functor.Context
import Data.Functor.Adjunction (unzipR)
import Control.Lens 
import Music.Score.Articulation
import Music.Score.Ties
import Music.Time
data Slur = NoSlur | BeginSlur | EndSlur
  deriving (Eq, Ord, Show)
data Mark = NoMark | Staccato | MoltoStaccato | Marcato |Accent |Tenuto
  deriving (Eq, Ord, Show)
instance Monoid Slur where
  mempty = NoSlur
  mappend NoSlur a = a
  mappend a NoSlur = a
  mappend a _ = a
instance Monoid Mark where
  mempty = NoMark
  mappend NoMark a = a
  mappend a NoMark = a
  mappend a _ = a
newtype ArticulationNotation 
  = ArticulationNotation { getArticulationNotation :: ([Slur], [Mark]) }
instance Wrapped ArticulationNotation where
  type Unwrapped ArticulationNotation = ([Slur], [Mark])
  _Wrapped' = iso getArticulationNotation ArticulationNotation
instance Rewrapped ArticulationNotation ArticulationNotation
type instance Articulation ArticulationNotation = ArticulationNotation
instance Transformable ArticulationNotation where
  transform _ = id
instance Tiable ArticulationNotation where
  toTied (ArticulationNotation (slur, marks)) 
    = (ArticulationNotation (slur1, marks1), 
       ArticulationNotation (slur2, marks2))
    where
      (marks1, marks2) = splitMarks marks
      (slur1, slur2)   = splitSlurs slur
      splitSlurs = unzipR . fmap splitSlur
      splitMarks = unzipR . fmap splitMark
      splitSlur NoSlur    = (mempty,    mempty)
      splitSlur BeginSlur = (BeginSlur, mempty)
      splitSlur EndSlur   = (mempty,    EndSlur)
      splitMark NoMark        = (NoMark, mempty)
      splitMark Staccato      = (Staccato, mempty)
      splitMark MoltoStaccato = (MoltoStaccato, mempty)
      splitMark Marcato       = (Marcato, mempty)
      splitMark Accent        = (Accent, mempty)
      splitMark Tenuto        = (Tenuto, mempty)
instance Monoid ArticulationNotation where
  mempty = ArticulationNotation ([], [])
  ArticulationNotation ([], []) `mappend` y = y
  x `mappend` ArticulationNotation ([], []) = x
  x `mappend` y = x
getSeparationMarks :: Double -> [Mark]
getSeparationMarks = fst . getSeparationMarks'
hasSlur' :: Double -> Bool
hasSlur' = snd . getSeparationMarks'
getSeparationMarks' :: Double -> ([Mark], Bool)
getSeparationMarks' x
  |              x <= (1) = ([], True)
  | (1) <  x && x <  1    = ([], False)
  | 1    <= x && x <  2    = ([Staccato], False)
  | 2    <= x              = ([MoltoStaccato], False)
getAccentMarks :: Double -> [Mark]
getAccentMarks x
  |              x <= (1) = []
  | (1) <  x && x <  1    = []
  | 1    <= x && x <  2    = [Accent]
  | 2    <= x              = [Marcato]
  | otherwise           = []
hasSlur :: (Real (Separation t), Articulated t) => t -> Bool
hasSlur y = hasSlur' (realToFrac $ view separation $ y)
allMarks :: (Real (Separation t), Real (Accentuation t), Articulated t) => t -> [Mark]
allMarks y = mempty
  <> getSeparationMarks (realToFrac $ y^.separation) 
  <> getAccentMarks (realToFrac $ y^.accentuation)
notateArticulation :: (Ord a, Articulated a, Real (Separation a), Real (Accentuation a)) => Ctxt a -> ArticulationNotation
notateArticulation (getCtxt -> x) = go x
  where
    go (Nothing, y, Nothing) = ArticulationNotation ([], allMarks y)
    go (Just x,  y, Nothing) = ArticulationNotation (if hasSlur x && hasSlur y then [EndSlur] else [], allMarks y)
    go (Nothing, y, Just z)  = ArticulationNotation (if hasSlur y && hasSlur z then [BeginSlur] else [], allMarks y)
    go (Just x,  y, Just z)  = ArticulationNotation (slur3 x y z, allMarks y)
      where
        slur3 x y z = case (hasSlur x, hasSlur y, hasSlur z) of
          (True, True, True)  -> []
          (False, True, True) -> [BeginSlur]
          (True, True, False) -> [EndSlur]
          _                   -> []