content: module Aws.S3.Commands.DeleteObjects where
  
  import           Aws.Core
  import           Aws.S3.Core
  import qualified Crypto.Hash          as CH
  import qualified Data.Map             as M
  import           Data.Maybe
  import qualified Data.Text            as T
  import qualified Data.Text.Encoding   as T
  import qualified Network.HTTP.Conduit as HTTP
  import qualified Network.HTTP.Types   as HTTP
  import qualified Text.XML             as XML
  import qualified Text.XML.Cursor      as Cu
  import           Text.XML.Cursor      (($/), (&|))
  import qualified Data.ByteString.Char8 as B
  import           Data.ByteString.Char8 ({- IsString -})
  import           Control.Applicative
  import           Prelude
  
  data DeleteObjects
      = DeleteObjects {
          dosBucket  :: Bucket
        , dosObjects :: [(Object, Maybe T.Text)] -- snd is an optional versionId
        , dosQuiet   :: Bool
        , dosMultiFactorAuthentication :: Maybe T.Text
        }
      deriving (Show)
  
  -- simple use case: neither mfa, nor version specified, quiet
  deleteObjects :: Bucket -> [T.Text] -> DeleteObjects
  deleteObjects bucket objs =
      DeleteObjects {
              dosBucket  = bucket
            , dosObjects = zip objs $ repeat Nothing
            , dosQuiet   = True
            , dosMultiFactorAuthentication = Nothing
            }
  
  data DeleteObjectsResponse
      = DeleteObjectsResponse {
          dorDeleted :: [DORDeleted]
        , dorErrors  :: [DORErrors]
        }
      deriving (Show)
  
  --omitting DeleteMarker because it appears superfluous
  data DORDeleted
      = DORDeleted {
          ddKey                   :: T.Text
        , ddVersionId             :: Maybe T.Text
        , ddDeleteMarkerVersionId :: Maybe T.Text
        }
      deriving (Show)
  
  data DORErrors
      = DORErrors {
          deKey     :: T.Text
        , deCode    :: T.Text
        , deMessage :: T.Text
        }
      deriving (Show)
  
  -- | ServiceConfiguration: 'S3Configuration'
  instance SignQuery DeleteObjects where
      type ServiceConfiguration DeleteObjects = S3Configuration
  
      signQuery DeleteObjects {..} = s3SignQuery S3Query
        {
          s3QMethod       = Post
        , s3QBucket       = Just $ T.encodeUtf8 dosBucket
        , s3QSubresources = HTTP.toQuery [("delete" :: B.ByteString, Nothing :: Maybe B.ByteString)]
        , s3QQuery        = []
        , s3QContentType  = Nothing
        , s3QContentMd5   = Just $ CH.hashlazy dosBody
        , s3QObject       = Nothing
        , s3QAmzHeaders   = maybeToList $ (("x-amz-mfa", ) . T.encodeUtf8) <$> dosMultiFactorAuthentication
        , s3QOtherHeaders = []
        , s3QRequestBody  = Just $ HTTP.RequestBodyLBS dosBody
        }
          where dosBody = XML.renderLBS XML.def XML.Document {
                      XML.documentPrologue = XML.Prologue [] Nothing []
                    , XML.documentRoot = root
                    , XML.documentEpilogue = []
                    }
                root = XML.Element {
                      XML.elementName = "Delete"
                    , XML.elementAttributes = M.empty
                    , XML.elementNodes = quietNode dosQuiet : (objectNode <$> dosObjects)
                    }
                objectNode (obj, mbVersion) = XML.NodeElement XML.Element {
                      XML.elementName = "Object"
                    , XML.elementAttributes = M.empty
                    , XML.elementNodes = keyNode obj : maybeToList (versionNode <$> mbVersion)
                    }
                versionNode = toNode "VersionId"
                keyNode     = toNode "Key"
                quietNode b = toNode "Quiet" $ if b then "true" else "false"
                toNode name content = XML.NodeElement XML.Element {
                      XML.elementName = name
                    , XML.elementAttributes = M.empty
                    , XML.elementNodes = [XML.NodeContent content]
                    }
  
  instance ResponseConsumer DeleteObjects DeleteObjectsResponse where
      type ResponseMetadata DeleteObjectsResponse = S3Metadata
  
      responseConsumer _ _ = s3XmlResponseConsumer parse
          where parse cursor = do
                    dorDeleted <- sequence $ cursor $/ Cu.laxElement "Deleted" &| parseDeleted
                    dorErrors  <- sequence $ cursor $/ Cu.laxElement "Error" &| parseErrors
                    return DeleteObjectsResponse {..}
                parseDeleted c = do
                    ddKey <- force "Missing Key" $ c $/ elContent "Key"
                    let ddVersionId = listToMaybe $ c $/ elContent "VersionId"
                        ddDeleteMarkerVersionId = listToMaybe $ c $/ elContent "DeleteMarkerVersionId"
                    return DORDeleted {..}
                parseErrors c = do
                    deKey     <- force "Missing Key" $ c $/ elContent "Key"
                    deCode    <- force "Missing Code" $ c $/ elContent "Code"
                    deMessage <- force "Missing Message" $ c $/ elContent "Message"
                    return DORErrors {..}
  
  instance Transaction DeleteObjects DeleteObjectsResponse
  
  instance AsMemoryResponse DeleteObjectsResponse where
      type MemoryResponse DeleteObjectsResponse = DeleteObjectsResponse
      loadToMemory = return
  