{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RecordWildCards     #-}


{-

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.Internal.Resolve
       ( resolve
       ) where

--------------------------------------------------------------------------------
import Control.Monad
import Data.Text (Text)
import qualified Data.Text as Text

--------------------------------------------------------------------------------
import Text.Playlist.Internal.Format
import Text.Playlist.Types

--------------------------------------------------------------------------------
-- Internal type to track when a playlist may need to be processed a
-- another time.  (Such as when a remote playlist refers to other
-- remote playlists.)
data Resolution = Flat Playlist | Again Playlist

--------------------------------------------------------------------------------
-- | If the given 'Playlist' contains tracks that reference remote
-- playlists, this function will recursively download and process
-- these playlists.  Returns a flattened playlist that should not
-- contain any references to other playlists.
--
-- You supply the downloading function as the second argument.  Use
-- whichever HTTP library that makes you happy.
--
-- There are two error conditions that are ignored by this function:
--
--   1. The nesting of playlists exceeds a (hard-coded) limit.  In
--      this case no playlists beyond the limit are processed.  Open a
--      pull request if you'd like to have a resolveN function that
--      allows you to specific the depth limit or one that returns an
--      error.
--
--   2. A downloaded playlist contains a syntax error.  In this case
--      the playlist is consider to have no tracks and is ignored.
--      Open a pull request if you want a version of this function
--      that returns some sort of an error instead of ignoring bad
--      playlists.
resolve :: forall m. (Monad m)
        => Playlist
        -- ^ A 'Playlist' that may contain references to other
        -- playlists.

        -> (Text -> m Playlist)
        -- ^ Downloading function.  This function should take a URL
        -- and return a parsed playlist.
        --
        -- It's expected that the URL points to another playlist that
        -- needs to be parsed and possibly resolved.

        -> m Playlist
        -- ^ A fully resolved 'Playlist'.  (All tracks should be files
        -- and not links to other playlists.)
resolve playlist download = go 10 playlist where

  ----------------------------------------------------------------------------
  -- Recursively process tracks in the 'Playlist' with a maximum depth
  -- of @n@.
  go :: Int -> Playlist -> m Playlist
  go _ [] = return []
  go 0 xs = return xs
  go n xs = fmap join $ forM xs $ \track -> do
    r <- process track

    case r of
      Flat p  -> return p
      Again p -> go (n - 1) p

  ----------------------------------------------------------------------------
  -- Process a single track.
  process :: Track -> m Resolution
  process t@Track {..} =
    case fileNameToFormat (Text.unpack trackURL) of
      Nothing -> return (Flat [t])
      Just _  -> Again <$> download trackURL