    --------------------------------------------------------------------
    -- |
    -- 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
      , Attr
      , Feed(..)
      , Entry(..)
      , EntryContent(..)
      , Category(..)
      , Generator(..)
      , Link(..)
      , TextContent(..)
      , txtToString
      , Source(..)
      , Person(..)
      , InReplyTo(..)
      , InReplyTotal(..)
      , newCategory
      , nullFeed
      , nullEntry
      , nullGenerator
      , nullLink
      , nullSource
      , nullPerson
      ) where

    import Prelude.Compat

    import Data.Text (Text, unpack)
    import Data.XML.Compat
    import Data.XML.Types as XML

    -- *Core types
    -- NOTE: In the future we may want to have more structured
    -- types for these.
    type URI = Text

    type NCName = Text

    type Date = Text

    type MediaType = Text

    data Feed = Feed
      { feedId :: URI
      , 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 :: [Attr]
      , feedOther :: [XML.Element]
      } deriving (Show)

    data Entry = Entry
      { entryId :: URI
      , 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 :: [Attr]
      , entryOther :: [XML.Element]
      } deriving (Show)

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

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

    data Generator = Generator
      { genURI :: Maybe URI
      , genVersion :: Maybe Text
      , genText :: Text
      } 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 Text
      , linkTitle :: Maybe Text
      , linkLength :: Maybe Text
      , linkAttrs :: [Attr]
      , linkOther :: [XML.Element]
      } deriving (Show)

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

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

    data Source = Source
      { sourceAuthors :: [Person]
      , sourceCategories :: [Category]
      , sourceGenerator :: Maybe Generator
      , sourceIcon :: Maybe URI
      , sourceId :: Maybe URI
      , 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 :: Text
      , personURI :: Maybe URI
      , personEmail :: Maybe Text
      , personOther :: [XML.Element]
      } deriving (Show)

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

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

    -- *Smart Constructors
    newCategory ::
         Text -- ^catTerm
      -> Category
    newCategory t = Category {catTerm = t, catScheme = Nothing, catLabel = Just t, catOther = []}

    nullFeed ::
         URI -- ^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 ::
         URI -- ^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 ::
         Text -- ^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 = []}