{-# LANGUAGE TupleSections              #-}
{-# LANGUAGE ViewPatterns               #-}
{-# LANGUAGE ConstraintKinds            #-}
{-# LANGUAGE DefaultSignatures          #-}
{-# LANGUAGE DeriveFoldable             #-}
{-# LANGUAGE DeriveFunctor              #-}
{-# LANGUAGE DeriveTraversable          #-}
{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE NoMonomorphismRestriction  #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE RankNTypes                 #-}
{-# LANGUAGE StandaloneDeriving         #-}
{-# LANGUAGE TypeFamilies               #-}
{-# LANGUAGE UndecidableInstances       #-}

-------------------------------------------------------------------------------------
-- |
-- Copyright   : (c) Hans Hoglund 2012-2014
--
-- License     : BSD-style
--
-- Maintainer  : hans@hanshoglund.se
-- Stability   : experimental
-- Portability : non-portable (TF,GNTD)
--
-- A simple backend that supports rendering scores to lists of pitch and velocity.
--
-- This exists as a sanity check for the 'Backend' classes, and as an example.
--
module Music.Score.Export.NoteList (
    -- * Note list backend
    NoteList,
    toNoteList,
  ) where

import           Music.Dynamics.Literal
import           Music.Pitch.Literal
import qualified Codec.Midi                    as Midi
import           Control.Comonad               (Comonad (..), extract)
import           Control.Applicative
import           Data.Colour.Names             as Color
import           Data.Foldable                 (Foldable)
import qualified Data.Foldable
import           Data.Functor.Couple
import           Data.Maybe
import           Data.Ratio
import           Data.Traversable              (Traversable, sequenceA)
import           Music.Score.Internal.Export   hiding (MVoice)
import           System.Process
import           Music.Score.Internal.Quantize
import qualified Text.Pretty                   as Pretty
import qualified Data.List
import           Music.Score.Internal.Util (composed, unRatio, swap, retainUpdates)
import Music.Score.Export.DynamicNotation
import Data.Semigroup.Instances

import Music.Score.Export.Backend
import Music.Time
import Music.Score.Dynamics
import Music.Score.Part

import Music.Score.Export.Backend

import Data.Functor.Identity
import Data.Semigroup
import Control.Monad
import Data.VectorSpace hiding (Sum(..))
import Data.AffineSpace
import Control.Lens hiding (rewrite)
-- import Control.Lens.Operators hiding ((|>))

import Music.Time
import Music.Score.Meta
import Music.Score.Meta.Title
import Music.Score.Meta.Attribution
import Music.Score.Dynamics
import Music.Score.Articulation
import Music.Score.Part
import Music.Score.Tremolo
import Music.Score.Text
import Music.Score.Harmonics
import Music.Score.Slide
import Music.Score.Color
import Music.Score.Ties
import Music.Score.Export.Backend
import Music.Score.Meta.Time
import Music.Score.Phrases


-- |
-- A token to represent the note list backend.
--
data NoteList

instance HasBackend NoteList where
  type BackendScore NoteList     = []
  type BackendContext NoteList   = Identity
  type BackendNote NoteList      = [(Sum Int, Int)]
  type BackendMusic NoteList     = [(Sum Int, Int)]
  finalizeExport _ = concat

instance HasBackendScore NoteList [a] where
  type BackendScoreEvent NoteList [a] = a
  exportScore _ = fmap Identity

instance HasBackendScore NoteList (Score a) where
  type BackendScoreEvent NoteList (Score a) = a
  exportScore _ = fmap Identity . toListOf traverse

instance HasBackendNote NoteList a => HasBackendNote NoteList [a] where
  exportNote b ps = mconcat $ map (exportNote b) $ sequenceA ps

instance HasBackendNote NoteList Int where
  exportNote _ (Identity p) = [(mempty, p)]

instance HasBackendNote NoteList Double where
  exportNote _ (Identity p) = [(mempty, round p)]

-- TODO prettier
instance HasBackendNote NoteList a => HasBackendNote NoteList (DynamicT b a) where
  exportNote b = exportNote b . fmap extract 

instance HasBackendNote NoteList a => HasBackendNote NoteList (ArticulationT b a) where
  exportNote b = exportNote b . fmap extract 

instance HasBackendNote NoteList a => HasBackendNote NoteList (PartT n a) where
  exportNote b = exportNote b . fmap extract

instance HasBackendNote NoteList a => HasBackendNote NoteList (TremoloT a) where
  exportNote b = exportNote b . fmap extract

instance HasBackendNote NoteList a => HasBackendNote NoteList (TextT a) where
  exportNote b = exportNote b . fmap extract

instance HasBackendNote NoteList a => HasBackendNote NoteList (HarmonicT a) where
  exportNote b = exportNote b . fmap extract

instance HasBackendNote NoteList a => HasBackendNote NoteList (SlideT a) where
  exportNote b = exportNote b . fmap extract

instance HasBackendNote NoteList a => HasBackendNote NoteList (TieT a) where
  exportNote b = exportNote b . fmap extract

instance HasBackendNote NoteList a => HasBackendNote NoteList (ColorT a) where
  exportNote b = exportNote b . fmap extract 

-- |
-- Export music as a note list.
--  
toNoteList :: (HasBackendNote NoteList (BackendScoreEvent NoteList s), HasBackendScore NoteList s) => s -> [(Int, Int)]
toNoteList = over (mapped._1) getSum . export (undefined::NoteList)