content: {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE DeriveDataTypeable #-}
  {-# LANGUAGE RecordWildCards #-}
  
  -- | Data structures pertaining to Discord Embed
  module Discord.Internal.Types.Embed where
  
  import Data.Aeson
  import Data.Time.Clock
  import Data.Default (Default, def)
  import qualified Data.Text as T
  import qualified Data.ByteString as B
  import Data.Functor ((<&>))
  
  import Network.HTTP.Client.MultipartFormData (PartM, partFileRequestBody)
  import Network.HTTP.Client (RequestBody(RequestBodyBS))
  
  import Discord.Internal.Types.Color (DiscordColor)
  
  createEmbed :: CreateEmbed -> Embed
  createEmbed CreateEmbed{..} =
    let
      emptyMaybe :: T.Text -> Maybe T.Text
      emptyMaybe t = if T.null t then Nothing else Just t
  
      embedImageToUrl :: T.Text -> CreateEmbedImage -> T.Text
      embedImageToUrl place cei = case cei of
                              CreateEmbedImageUrl t -> t
                              CreateEmbedImageUpload _ -> T.filter (/=' ') $ "attachment://" <> createEmbedTitle <> place <> ".png"
  
      embedAuthor = EmbedAuthor createEmbedAuthorName
                                (emptyMaybe createEmbedAuthorUrl)
                                (embedImageToUrl "author" <$> createEmbedAuthorIcon)
                                Nothing
      embedImage = (embedImageToUrl "image" <$> createEmbedImage) <&>
                    \image -> EmbedImage image Nothing Nothing Nothing
      embedThumbnail = (embedImageToUrl "thumbnail" <$> createEmbedThumbnail) <&>
                        \thumbnail -> EmbedThumbnail thumbnail Nothing Nothing Nothing
      embedFooter = EmbedFooter createEmbedFooterText
                                (embedImageToUrl "footer" <$> createEmbedFooterIcon)
                                Nothing
  
    in Embed { embedAuthor      = Just embedAuthor
             , embedTitle       = emptyMaybe createEmbedTitle
             , embedUrl         = emptyMaybe createEmbedUrl
             , embedThumbnail   = embedThumbnail
             , embedDescription = emptyMaybe createEmbedDescription
             , embedFields      = createEmbedFields
             , embedImage       = embedImage
             , embedFooter      = Just embedFooter
             , embedColor       = createEmbedColor
             , embedTimestamp   = createEmbedTimestamp
  
             -- can't set these
             , embedVideo       = Nothing
             , embedProvider    = Nothing
             }
  
  -- | The 'Default' instance of this type yields a 'def' value whose fields are all empty.
  -- As such, the 'def' value is not a valid embed and needs to be adjusted before being used.
  data CreateEmbed = CreateEmbed
    { createEmbedAuthorName  :: T.Text
    , createEmbedAuthorUrl   :: T.Text
    , createEmbedAuthorIcon  :: Maybe CreateEmbedImage
    , createEmbedTitle       :: T.Text
    , createEmbedUrl         :: T.Text
    , createEmbedThumbnail   :: Maybe CreateEmbedImage
    , createEmbedDescription :: T.Text
    , createEmbedFields      :: [EmbedField]
    , createEmbedImage       :: Maybe CreateEmbedImage
    , createEmbedFooterText  :: T.Text
    , createEmbedFooterIcon  :: Maybe CreateEmbedImage
    , createEmbedColor       :: Maybe DiscordColor
    , createEmbedTimestamp   :: Maybe UTCTime
    } deriving (Show, Read, Eq, Ord)
  
  data CreateEmbedImage = CreateEmbedImageUrl T.Text
                        | CreateEmbedImageUpload B.ByteString
    deriving (Show, Read, Eq, Ord)
  
  instance Default CreateEmbed where
   def = CreateEmbed "" "" Nothing "" "" Nothing "" [] Nothing "" Nothing Nothing Nothing
  
  -- | An embed attached to a message.
  data Embed = Embed
    { embedAuthor      :: Maybe EmbedAuthor
    , embedTitle       :: Maybe T.Text     -- ^ Title of the embed
    , embedUrl         :: Maybe T.Text     -- ^ URL of embed
    , embedThumbnail   :: Maybe EmbedThumbnail -- ^ Thumbnail in top-right
    , embedDescription :: Maybe T.Text     -- ^ Description of embed
    , embedFields      :: [EmbedField]     -- ^ Fields of the embed
    , embedImage       :: Maybe EmbedImage
    , embedFooter      :: Maybe EmbedFooter
    , embedColor       :: Maybe DiscordColor    -- ^ The embed color
    , embedTimestamp   :: Maybe UTCTime    -- ^ The time of the embed content
    , embedVideo       :: Maybe EmbedVideo -- ^ Only present for "video" types
    , embedProvider    :: Maybe EmbedProvider -- ^ Only present for "video" types
    } deriving (Show, Read, Eq, Ord)
  
  -- TODO
  instance ToJSON Embed where
    toJSON Embed{..} = object
     [ "author"      .= embedAuthor
     , "title"       .= embedTitle
     , "url"         .= embedUrl
     , "description" .= embedDescription
     , "thumbnail"   .= embedThumbnail
     , "fields"      .= embedFields
     , "image"       .= embedImage
     , "footer"      .= embedFooter
     , "color"       .= embedColor
     , "timestamp"   .= embedTimestamp
     , "video"       .= embedVideo
     , "provider"    .= embedProvider
      ]
  
  instance FromJSON Embed where
    parseJSON = withObject "embed" $ \o ->
      Embed <$> o .:? "author"
            <*> o .:? "title"
            <*> o .:? "url"
            <*> o .:? "thumbnail"
            <*> o .:? "description"
            <*> o .:? "fields" .!= []
            <*> o .:? "image"
            <*> o .:? "footer"
            <*> o .:? "color"
            <*> o .:? "timestamp"
            <*> o .:? "video"
            <*> o .:? "provider"
  
  
  data EmbedThumbnail = EmbedThumbnail
    { embedThumbnailUrl :: T.Text
    , embedThumbnailProxyUrl :: Maybe T.Text
    , embedThumbnailHeight :: Maybe Integer
    , embedThumbnailWidth :: Maybe Integer
    } deriving (Show, Read, Eq, Ord)
  
  instance ToJSON EmbedThumbnail where
    toJSON (EmbedThumbnail a b c d) = object
      [ "url" .= a
      , "proxy_url" .= b
      , "height" .= c
      , "width" .= d
      ]
  
  instance FromJSON EmbedThumbnail where
    parseJSON = withObject "thumbnail" $ \o ->
      EmbedThumbnail <$> o .: "url"
                     <*> o .:? "proxy_url"
                     <*> o .:? "height"
                     <*> o .:? "width"
  
  data EmbedVideo = EmbedVideo
    { embedVideoUrl :: Maybe T.Text
    , embedProxyUrl :: Maybe T.Text
    , embedVideoHeight :: Maybe Integer
    , embedVideoWidth :: Maybe Integer
    } deriving (Show, Read, Eq, Ord)
  
  instance ToJSON EmbedVideo where
    toJSON (EmbedVideo a a' b c) = object
      [ "url" .= a
      , "height" .= b
      , "width" .= c
      , "proxy_url" .= a'
      ]
  
  instance FromJSON EmbedVideo where
    parseJSON = withObject "video" $ \o ->
      EmbedVideo <$> o .:? "url"
                 <*> o .:? "proxy_url"
                 <*> o .:? "height"
                 <*> o .:? "width"
  
  data EmbedImage = EmbedImage
    { embedImageUrl :: T.Text
    , embedImageProxyUrl :: Maybe T.Text
    , embedImageHeight :: Maybe Integer
    , embedImageWidth :: Maybe Integer
    } deriving (Show, Read, Eq, Ord)
  
  instance ToJSON EmbedImage where
    toJSON (EmbedImage a b c d) = object
      [ "url" .= a
      , "proxy_url" .= b
      , "height" .= c
      , "width" .= d
      ]
  
  instance FromJSON EmbedImage where
    parseJSON = withObject "image" $ \o ->
      EmbedImage <$> o .:  "url"
                 <*> o .:? "proxy_url"
                 <*> o .:? "height"
                 <*> o .:? "width"
  
  data EmbedProvider = EmbedProvider
    { embedProviderName :: Maybe T.Text
    , embedProviderUrl :: Maybe T.Text
    } deriving (Show, Read, Eq, Ord)
  
  instance ToJSON EmbedProvider where
    toJSON (EmbedProvider a b) = object
      [ "name" .= a
      , "url" .= b
      ]
  
  instance FromJSON EmbedProvider where
    parseJSON = withObject "provider" $ \o ->
      EmbedProvider <$> o .:? "name"
                    <*> o .:? "url"
  
  data EmbedAuthor = EmbedAuthor
    { embedAuthorName :: T.Text
    , embedAuthorUrl :: Maybe T.Text
    , embedAuthorIconUrl :: Maybe T.Text
    , embedAuthorProxyIconUrl :: Maybe T.Text
    } deriving (Show, Read, Eq, Ord)
  
  instance ToJSON EmbedAuthor where
    toJSON (EmbedAuthor a b c d) = object
      [ "name" .= a
      , "url" .= b
      , "icon_url" .= c
      , "proxy_icon_url" .= d
      ]
  
  instance FromJSON EmbedAuthor where
    parseJSON = withObject "author" $ \o ->
      EmbedAuthor <$> o .:  "name"
                  <*> o .:? "url"
                  <*> o .:? "icon_url"
                  <*> o .:? "proxy_icon_url"
  
  data EmbedFooter = EmbedFooter
    { embedFooterText :: T.Text
    , embedFooterIconUrl :: Maybe T.Text
    , embedFooterProxyIconUrl :: Maybe T.Text
    } deriving (Show, Read, Eq, Ord)
  
  instance ToJSON EmbedFooter where
    toJSON (EmbedFooter a b c) = object
      [ "text" .= a
      , "icon_url" .= b
      , "proxy_icon_url" .= c
      ]
  
  instance FromJSON EmbedFooter where
    parseJSON = withObject "footer" $ \o ->
      EmbedFooter <$> o .:  "text"
                  <*> o .:? "icon_url"
                  <*> o .:? "proxy_icon_url"
  
  data EmbedField = EmbedField
    { embedFieldName :: T.Text
    , embedFieldValue :: T.Text
    , embedFieldInline :: Maybe Bool
    } deriving (Show, Read, Eq, Ord)
  
  instance ToJSON EmbedField where
    toJSON (EmbedField a b c) = object
      [ "name" .= a
      , "value" .= b
      , "inline" .= c
      ]
  
  instance FromJSON EmbedField where
    parseJSON = withObject "field" $ \o ->
      EmbedField <$> o .:  "name"
                 <*> o .:  "value"
                 <*> o .:? "inline"
  
  
  maybeEmbed :: Maybe CreateEmbed -> [PartM IO]
  maybeEmbed =
        let mkPart (name,content) = partFileRequestBody name (T.unpack name) (RequestBodyBS content)
            uploads CreateEmbed{..} = [(T.filter (/=' ') $ createEmbedTitle<>n,c) | (n, Just (CreateEmbedImageUpload c)) <-
                                            [ ("author.png", createEmbedAuthorIcon)
                                            , ("thumbnail.png", createEmbedThumbnail)
                                            , ("image.png", createEmbedImage)
                                            , ("footer.png", createEmbedFooterIcon) ]]
        in maybe [] (map mkPart . uploads)
  