{-# LANGUAGE OverloadedStrings #-}

{-

This file is part of the Haskell package playlists. It is subject to
the license terms in the LICENSE file found in the top-level directory
of this distribution and at git://pmade.com/playlists/LICENSE. No part
of playlists package, including this file, may be copied, modified,
propagated, or distributed except according to the terms contained in
the LICENSE file.

-}

--------------------------------------------------------------------------------
module Text.Playlist.M3U.Reader (parsePlaylist) where

--------------------------------------------------------------------------------
import Control.Applicative
import Control.Monad (void)
import Data.Attoparsec.ByteString
import Data.Attoparsec.ByteString.Char8 (signed, double)
import Data.Maybe (catMaybes)
import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8)
import Text.Playlist.Internal.Attoparsec
import Text.Playlist.Types

--------------------------------------------------------------------------------
-- | Parser for a complete M3U playlist.
parsePlaylist :: Parser Playlist
parsePlaylist = do
  ts <- many1 parseTrack
  void (many' commentOrDirective) -- Trailing comments.
  return ts

--------------------------------------------------------------------------------
-- | Parser for a single track in a M3U file.
parseTrack :: Parser Track
parseTrack = do
  -- Get the length and title closest to the URL or Nothing.
  (title, len) <- maybeTitleAndLength . reverse <$> many' commentOrDirective
  url   <- parseURL
  return Track { trackURL      = url
               , trackTitle    = title
               , trackDuration = len
               }
    where
      maybeTitleAndLength lst =
        case catMaybes lst of
          []    -> (Nothing, Nothing)
          x : _ -> x
--------------------------------------------------------------------------------
-- | Parser for URL or file name in a M3U file.  The URL is the entire
-- line so this parser extracts the entire line and decodes it.
parseURL :: Parser Text
parseURL = decodeUtf8 <$> takeWhile1 (not . isEOL) <* skipSpace

--------------------------------------------------------------------------------
-- | Comment parser with a twist.  In the extended M3U format metadata
-- for a track can be placed in a comment that appears just before the
-- URL.  This parser succeeds if the current line is a comment, and
-- always skips over the entire comment.  If the comment represents an
-- EXTINF directive then that information will be returned in a @Just@.
-- If it's just a regular comment then @Nothing@ is returned.
commentOrDirective :: Parser (Maybe (Maybe Text, Maybe Float))
commentOrDirective = do
  skipSpace
  skip (== 35) -- Comment character "#"
  isDirective <- (string "EXTINF:" >> return True) <|> return False
  if isDirective then directive <|> comment else comment
    where
      comment   = skipLine >> return Nothing
      directive = do
        mlen <- (Just . realToFrac <$> signed double) <|> return Nothing -- Parse length.
        skip (== 44)                                                     -- Skip comma.
        mtext <- (Just . decodeUtf8 <$> takeWhile1 (not . isEOL)) <|> return Nothing
        skipLine
        return (Just (mtext, mlen))
