-- |
--
-- Copyright:
--   This file is part of the package vimeta. It is subject to the
--   license terms in the LICENSE file found in the top-level
--   directory of this distribution and at:
--
--     https://github.com/pjones/vimeta
--
--   No part of this package, including this file, may be copied,
--   modified, propagated, or distributed except according to the terms
--   contained in the LICENSE file.
--
-- License: BSD-2-Clause
--
-- Utility functions for downloading files.
module Vimeta.Core.Download
  ( withArtwork,
    withDownload,
  )
where

import qualified Data.ByteString.Lazy as LByteString
import qualified Data.Text as Text
import qualified Network.HTTP.Client as HC
import System.FilePath
import System.IO (hFlush)
import System.IO.Temp (withSystemTempFile)
import Vimeta.Core.Config
import Vimeta.Core.Vimeta

-- | Try to download artwork and run the given function.  The
-- function will be passed a 'FilePath' if the artwork was downloaded.
--
-- See the 'withDownload' function for more details.
withArtwork ::
  (MonadIO m) =>
  [Text] ->
  (Maybe FilePath -> Vimeta IO a) ->
  Vimeta m a
withArtwork urls = withDownload (listToMaybe $ candidates urls)
  where
    candidates :: [Text] -> [Text]
    candidates = filter checkExtension . reverse
    checkExtension :: Text -> Bool
    checkExtension = goodExtension . takeExtension . toString . Text.toLower
    goodExtension :: String -> Bool
    goodExtension ext = ext == ".jpg" || ext == ".png"

-- | Download the given URL to a temporary file and pass the file
-- name to the given function.
--
-- The reason a function needs to be passed to 'withDownload' is the
-- result of using 'withSystemTempFile' to store the downloaded file.
-- The file will be automatically removed after the given function
-- completes.
withDownload ::
  (MonadIO m) =>
  -- | URL.
  Maybe Text ->
  -- | Function to call and pass the file name to.
  (Maybe FilePath -> Vimeta IO a) ->
  -- | Result of above function.
  Vimeta m a
withDownload Nothing f = do
  verbose "no URL to download"
  runIOE $ runVimeta (f Nothing)
withDownload url f = do
  context <- ask

  let dryRun = configDryRun $ ctxConfig context
      manager = ctxManager context

  case (dryRun, url) of
    (True, Nothing) ->
      verbose "dry-run: nothing to download"
        >> runWithoutTempFile f
    (False, Nothing) ->
      verbose "nothing to download"
        >> runWithoutTempFile f
    (True, Just u) ->
      verbose ("dry-run:" <> u)
        >> runWithoutTempFile f
    (False, Just u) ->
      verbose u
        >> runWithTempFile u manager f

-- | Helper function to run the download action with a temporary file.
runWithTempFile ::
  (MonadIO m) =>
  Text ->
  HC.Manager ->
  (Maybe FilePath -> Vimeta IO a) ->
  Vimeta m a
runWithTempFile url manager vio = do
  context <- ask

  runIOE $
    withSystemTempFile "vimeta" $ \name h -> do
      downloadToHandle manager (toString url) h
      execVimetaWithContext context $ vio (Just name)

-- | Helper function to run an action without needing a temporary file.
runWithoutTempFile ::
  (MonadIO m) =>
  (Maybe FilePath -> Vimeta IO a) ->
  Vimeta m a
runWithoutTempFile vio = do
  context <- ask
  runIOE $ execVimetaWithContext context $ vio Nothing

-- | Helper function to the actual HTTP downloading into a file handle.
downloadToHandle :: HC.Manager -> String -> Handle -> IO ()
downloadToHandle manager url handle = do
  request <- HC.parseRequest url
  response <- HC.httpLbs request manager
  LByteString.hPut handle (HC.responseBody response)
  hFlush handle