    {-# LANGUAGE FlexibleInstances    #-}
    {-# LANGUAGE OverloadedStrings    #-}
    {-# LANGUAGE ScopedTypeVariables  #-}
    {-# LANGUAGE TypeSynonymInstances #-}
    module Text.ICalendar.Parser
        ( parseICalendar
        , parseICalendarFile
        , parseICal
        , parseICalFile
        , DecodingFunctions(..)
        ) where

    import           Control.Applicative
    import           Control.Monad
    import           Control.Monad.Error
    import           Control.Monad.RWS          (runRWS)
    import           Data.ByteString.Lazy       (ByteString)
    import qualified Data.ByteString.Lazy.Char8 as B
    import           Data.Monoid
    import           Prelude

    import Text.Parsec.ByteString.Lazy ()
    import Text.Parsec.Pos
    import Text.Parsec.Prim            hiding (many, (<|>))
    import Text.Parsec.Text.Lazy       ()

    import Text.ICalendar.Parser.Common
    import Text.ICalendar.Parser.Components
    import Text.ICalendar.Parser.Content
    import Text.ICalendar.Types

    -- | Parse a ByteString containing iCalendar data.
    --
    -- Returns either an error, or a tuple of the result and a list of warnings.
    parseICalendar :: DecodingFunctions
                   -> FilePath -- ^ Used in error messages.
                   -> ByteString
                   -> Either String ([VCalendar], [String])
    parseICalendar s f bs = do
        a <- either (Left . show) Right $ runParser parseToContent s f bs
        when (null a) $ throwError "Missing content."
        let xs = map (runCP s . parseVCalendar) a
        (x, w) <- ((flip.).) flip foldM ([], []) xs $ \(x, ws) (g, (pos, _), w) ->
                 case g of
                      Left e -> Left $ show pos ++ ": " ++ e
                      Right y -> Right (y:x, w <> ws)
        return (x, w)

    -- | Deprecated synonym for parseICalendar
    parseICal :: DecodingFunctions
              -> FilePath
              -> ByteString
              -> Either String ([VCalendar], [String])
    parseICal = parseICalendar
    {-# DEPRECATED parseICal "Use parseICalendar instead" #-}

    -- | Parse an iCalendar file.
    parseICalendarFile :: DecodingFunctions
                       -> FilePath
                       -> IO (Either String ([VCalendar], [String]))
    parseICalendarFile s f = parseICal s f <$> B.readFile f

    -- | Deprecated synonym for parseICalendarFile
    parseICalFile :: DecodingFunctions
                  -> FilePath
                  -> IO (Either String ([VCalendar], [String]))
    parseICalFile = parseICalendarFile
    {-# DEPRECATED parseICalFile "Use parseICalendarFile instead" #-}

    runCP :: DecodingFunctions -> ContentParser a
          -> (Either String a, (SourcePos, [Content]), [String])
    runCP s = ((flip .) . flip) runRWS s (undefined, undefined) . runErrorT