    --------------------------------------------------------------------
    -- |
    -- Module    : Text.Atom.Feed
    -- Copyright : (c) Galois, Inc. 2008,
    --             (c) Sigbjorn Finne 2009-
    -- License   : BSD3
    --
    -- Maintainer: Sigbjorn Finne <sof@forkIO.com>
    -- Stability : provisional
    -- Portability: portable
    --
    --------------------------------------------------------------------

    module Text.Atom.Feed
      ( URI
      , NCName
      , Date
      , MediaType
      , Feed (..)
      , Entry (..)
      , EntryContent (..)
      , Category (..)
      , Generator (..)
      , Link (..)
      , TextContent (..)
      , txtToString
      , Source (..)
      , Person (..)
      , InReplyTo (..)
      , InReplyTotal (..)
      , newCategory
      , nullFeed
      , nullEntry
      , nullGenerator
      , nullLink
      , nullSource
      , nullPerson
      ) where

    import qualified Text.XML.Light as XML

    -- *Core types

    -- NOTE: In the future we may want to have more structured
    -- types for these.
    type URI        = String
    type NCName     = String
    type Date       = String
    type MediaType  = String

    data Feed
     = Feed
          { feedId           :: String
          , feedTitle        :: TextContent
          , feedUpdated      :: Date
          , feedAuthors      :: [Person]
          , feedCategories   :: [Category]
          , feedContributors :: [Person]
          , feedGenerator    :: Maybe Generator
          , feedIcon         :: Maybe URI
          , feedLinks        :: [Link]
          , feedLogo         :: Maybe URI
          , feedRights       :: Maybe TextContent
          , feedSubtitle     :: Maybe TextContent
          , feedEntries      :: [Entry]
          , feedAttrs        :: [XML.Attr]
          , feedOther        :: [XML.Element]
          }
         deriving (Show)

    data Entry
     = Entry
          { entryId           :: String
          , entryTitle        :: TextContent
          , entryUpdated      :: Date
          , entryAuthors      :: [Person]
          , entryCategories   :: [Category]
          , entryContent      :: Maybe EntryContent
          , entryContributor  :: [Person]
          , entryLinks        :: [Link]
          , entryPublished    :: Maybe Date
          , entryRights       :: Maybe TextContent
          , entrySource       :: Maybe Source
          , entrySummary      :: Maybe TextContent
          , entryInReplyTo    :: Maybe InReplyTo
          , entryInReplyTotal :: Maybe InReplyTotal
          , entryAttrs        :: [XML.Attr]
          , entryOther        :: [XML.Element]
          }
         deriving (Show)

    data EntryContent
     = TextContent   String
     | HTMLContent   String
     | XHTMLContent  XML.Element
     | MixedContent  (Maybe String) [XML.Content]
     | ExternalContent (Maybe MediaType) URI
         deriving (Show)

    data Category
     = Category
           { catTerm   :: String         -- ^ the tag\/term of the category.
           , catScheme :: Maybe URI      -- ^ optional URL for identifying the categorization scheme.
           , catLabel  :: Maybe String   -- ^ human-readable label of the category
           , catOther  :: [XML.Element]  -- ^ unknown elements, for extensibility.
           }
         deriving (Show)

    data Generator
     = Generator
           { genURI     :: Maybe URI
           , genVersion :: Maybe String
           , genText    :: String
           }
         deriving (Eq, Show)

    data Link
     = Link
          { linkHref     :: URI
             -- ToDo: make the switch over to using the Atom.Feed.Link relation type.
          , linkRel      :: Maybe (Either NCName URI)
          , linkType     :: Maybe MediaType
          , linkHrefLang :: Maybe String
          , linkTitle    :: Maybe String
          , linkLength   :: Maybe String
          , linkAttrs    :: [XML.Attr]
          , linkOther    :: [XML.Element]
          }
         deriving (Show)

    data TextContent
     = TextString  String
     | HTMLString  String
     | XHTMLString XML.Element
         deriving (Show)

    txtToString :: TextContent -> String
    txtToString (TextString s) = s
    txtToString (HTMLString s) = s
    txtToString (XHTMLString x) = show x

    data Source
     = Source
          { sourceAuthors     :: [Person]
          , sourceCategories  :: [Category]
          , sourceGenerator   :: Maybe Generator
          , sourceIcon        :: Maybe URI
          , sourceId          :: Maybe String
          , sourceLinks       :: [Link]
          , sourceLogo        :: Maybe URI
          , sourceRights      :: Maybe TextContent
          , sourceSubtitle    :: Maybe TextContent
          , sourceTitle       :: Maybe TextContent
          , sourceUpdated     :: Maybe Date
          , sourceOther       :: [XML.Element]
          }
         deriving (Show)

    data Person
     = Person
         { personName  :: String
         , personURI   :: Maybe URI
         , personEmail :: Maybe String
         , personOther :: [XML.Element]
         }
         deriving (Show)

    data InReplyTo
     = InReplyTo
         { replyToRef     :: URI
         , replyToHRef    :: Maybe URI
         , replyToType    :: Maybe MediaType
         , replyToSource  :: Maybe URI
         , replyToOther   :: [XML.Attr]
         , replyToContent :: [XML.Content]
         }
         deriving (Show)

    data InReplyTotal
     = InReplyTotal
         { replyToTotal      :: Integer -- non-negative :)
         , replyToTotalOther :: [XML.Attr]
         }
         deriving (Show)

    -- *Smart Constructors

    newCategory :: String -- ^catTerm
                -> Category
    newCategory t = Category
      { catTerm   = t
      , catScheme = Nothing
      , catLabel  = Just t
      , catOther  = []
      }

    nullFeed :: String  -- ^feedId
             -> TextContent -- ^feedTitle
             -> Date -- ^feedUpdated
             -> Feed
    nullFeed i t u = Feed
          { feedId           = i
          , feedTitle        = t
          , feedUpdated      = u
          , feedAuthors      = []
          , feedCategories   = []
          , feedContributors = []
          , feedGenerator    = Nothing
          , feedIcon         = Nothing
          , feedLinks        = []
          , feedLogo         = Nothing
          , feedRights       = Nothing
          , feedSubtitle     = Nothing
          , feedEntries      = []
          , feedAttrs        = []
          , feedOther        = []
          }

    nullEntry :: String -- ^entryId
              -> TextContent -- ^entryTitle
              -> Date -- ^entryUpdated
              -> Entry
    nullEntry i t u = Entry
          { entryId           = i
          , entryTitle        = t
          , entryUpdated      = u
          , entryAuthors      = []
          , entryCategories   = []
          , entryContent      = Nothing
          , entryContributor  = []
          , entryLinks        = []
          , entryPublished    = Nothing
          , entryRights       = Nothing
          , entrySource       = Nothing
          , entrySummary      = Nothing
          , entryInReplyTo    = Nothing
          , entryInReplyTotal = Nothing
          , entryAttrs        = []
          , entryOther        = []
          }

    nullGenerator :: String -- ^genText
                  -> Generator
    nullGenerator t = Generator
      { genURI     = Nothing
      , genVersion = Nothing
      , genText    = t
      }

    nullLink :: URI -- ^linkHref
             -> Link
    nullLink uri = Link
      { linkHref      = uri
      , linkRel       = Nothing
      , linkType      = Nothing
      , linkHrefLang  = Nothing
      , linkTitle     = Nothing
      , linkLength    = Nothing
      , linkAttrs     = []
      , linkOther     = []
      }

    nullSource :: Source
    nullSource = Source
          { sourceAuthors     = []
          , sourceCategories  = []
          , sourceGenerator   = Nothing
          , sourceIcon        = Nothing
          , sourceId          = Nothing
          , sourceLinks       = []
          , sourceLogo        = Nothing
          , sourceRights      = Nothing
          , sourceSubtitle    = Nothing
          , sourceTitle       = Nothing
          , sourceUpdated     = Nothing
          , sourceOther       = []
          }

    nullPerson :: Person
    nullPerson = Person
      { personName  = ""
      , personURI   = Nothing
      , personEmail = Nothing
      , personOther = []
      }