module HarmonyParser (parseHarmony) where
import Text.ParserCombinators.Parsec
import qualified Melody
import MelodyParser (melodyElementP)
import qualified Harmony
import Control.Monad
import Control.Monad.Instances
import Data.List as List
import Data.Char as Char
import qualified Midi

data Command = SetChannel Int
             | MelodyElement (Melody.MelodyElement Rational Rational)
               deriving Show

fileP::Parser [Command]
fileP = do many whiteP
           result <- many commandP
           many whiteP
           eof
           return result

whiteP::Parser ()
whiteP = do oneOf " \n\r\t|"; return () <?> "white space"
         <|>
         do char '%'; manyTill anyChar newline; return () <?> "comment"

commandP::Parser Command
commandP = do result <- (setChannelP <|> liftM MelodyElement melodyElementP)
              many1 whiteP
              return result

setChannelP::Parser Command
setChannelP = char 'v' >> liftM SetChannel intP <?> "voice command"

intP::Parser Int
intP = liftM read (many1 digit) <?> "integer"

type Duration = Rational

tagByChannel chan [] = []
tagByChannel chan (SetChannel newChan : rest) = tagByChannel newChan rest
tagByChannel chan (MelodyElement cmd : rest) = (chan,cmd) : tagByChannel chan rest

groupByTag [] = []
groupByTag stream@((tag,_):_) =
    (tag,map snd matching) : groupByTag rest
        where (matching,rest) = partition ((==tag).fst) stream

parseHarmony::String->Either ParseError (Harmony.Harmony Rational Rational)
parseHarmony f = fmap (Harmony.Harmony . map addChannelAttributes . groupByTag . tagByChannel 0) (runParser fileP () "" f)

addChannelAttributes::(Int,[Melody.MelodyElement Rational Rational])->(Int,Int,Int,Melody.Melody Rational Rational)
addChannelAttributes (channel,melodyElements) = (channel,48,64,Melody.Melody melodyElements)

test = do f <- readFile "song3.txt"
          let Right song = parseHarmony f
          writeFile "test.mid" (Midi.tofile 80 . Harmony.tomidi 80 $ song)

test2 = do f <- readFile "song4.txt"
           let Right song = parseHarmony f
           writeFile "test.mid" (Midi.tofile 80 . Harmony.tomidi 80 $ song)