content: {-# LANGUAGE OverloadedStrings #-}
  
  module Main where
  
  import qualified Control.Error.Util         as Err
  import qualified Data.ByteString.Lazy       as BSL
  import qualified Data.ByteString.Lazy.Char8 as BS8
  import qualified Data.Text                  as T
  import qualified Data.Text.IO               as TIO
  import qualified Network.URI
  import qualified Network.Wreq               as Wreq
  import qualified Options.Applicative        as Opt
  import qualified Options.Applicative.Text   as Opt
  import qualified Web.Giphy                  as Giphy
  
  import           Control.Applicative        (optional, (<**>), (<|>))
  import           Control.Lens               (Getting (), preview)
  import           Control.Lens.At            (at)
  import           Control.Lens.Cons          (_head)
  import           Control.Lens.Operators
  import           Control.Lens.Prism         (_Left, _Right)
  import           Control.Monad              (join, unless)
  import           Data.Bifunctor             (second)
  import           Data.Monoid                (First ())
  import           Data.Version               (Version (), showVersion)
  import           Paths_givegif              (version)
  import           System.Environment         (getProgName)
  import           System.IO                  (stderr)
  
  import           Console
  
  data Options = Options
    { optNoPreview :: Bool
    , optMode      :: SearchMode
    }
  
  data SearchMode = OptSearch T.Text | OptTranslate T.Text | OptRandom (Maybe T.Text)
  
  options :: Opt.Parser Options
  options =
    Options <$> Opt.switch ( Opt.long "no-preview"
                          <> Opt.short 'n'
                          <> Opt.help "Don't render an inline image preview." )
            <*> ( ( OptSearch <$> Opt.textOption ( Opt.long "search"
                                            <> Opt.short 's'
                                            <> Opt.help "Use search to find a matching GIF." ) )
            <|> ( OptTranslate <$> Opt.textOption ( Opt.long "translate"
                                             <> Opt.short 't'
                                             <> Opt.help "Use translate to find a matching GIF." ) )
            <|> ( OptRandom <$> optional ( Opt.textArgument ( Opt.metavar "RANDOM_TAG" ) ) ) )
  
  cliParser :: String -> Version -> Opt.ParserInfo Options
  cliParser progName ver =
    Opt.info ( Opt.helper <*> options <**> versionInfo )
      ( Opt.fullDesc
     <> Opt.progDesc "Find GIFs on the command line."
     <> Opt.header progName )
    where
      versionInfo = Opt.infoOption ( unwords [progName, showVersion ver] )
        ( Opt.short 'V'
       <> Opt.long "version"
       <> Opt.hidden
       <> Opt.help "Show version information" )
  
  apiKey :: Giphy.Key
  apiKey = Giphy.Key "dc6zaTOxFJmzC"
  
  taggedPreview
    :: t
    -> Getting (First a) s a
    -> s
    -> Either t a
  taggedPreview tag l s = Err.note tag $ preview l s
  
  dupe :: a -> (a, a)
  dupe = join (,)
  
  main :: IO ()
  main = do
    progName <- getProgName
    Opt.execParser (cliParser progName version) >>= run
    where
      run :: Options -> IO ()
      run opts = do
        let config = Giphy.GiphyConfig apiKey
        let app = getApp opts
        resp <- Giphy.runGiphy app config
  
        -- Get the first result and turn the left side into a String error
        let fstRes = resp & _Right %~ taggedPreview "No results found." _head
                          & _Left  %~ T.pack . show
                          & join
  
        -- Turn the right hand side into an Either.
        let fstUrl = fstRes & _Right %~ taggedPreview "No images attached."
                                        ( Giphy.gifImages
                                        . at "original"
                                        . traverse
                                        . Giphy.imageUrl
                                        . traverse )
                                      & join
  
        let dupeUrl = fstUrl & _Right %~ second show . dupe
        resp' <- sequence $ traverse Wreq.get <$> dupeUrl
  
        case resp' of
          Right r -> uncurry (printGif opts) r
          Left e -> TIO.hPutStrLn stderr $ "Error: " <> e
  
      getApp :: Options -> Giphy.Giphy [Giphy.Gif]
      getApp opts =
        case optMode opts of
          OptSearch s -> searchApp s
          OptTranslate t -> translateApp t
          OptRandom r -> randomApp r
  
      printGif :: Options -> Network.URI.URI -> Wreq.Response BSL.ByteString -> IO ()
      printGif opts uri r = do
        unless (optNoPreview opts) $ do
          render <- getImageRenderer
          BS8.putStrLn . render $ consoleImage True (r ^. Wreq.responseBody)
        putStrLn $ Network.URI.uriToString id uri ""
  
  translateApp :: T.Text -> Giphy.Giphy [Giphy.Gif]
  translateApp q = do
    resp <- Giphy.translate $ Giphy.Phrase q
    return . pure $ resp ^. Giphy.translateItem
  
  searchApp :: T.Text -> Giphy.Giphy [Giphy.Gif]
  searchApp q = do
    resp <- Giphy.search $ Giphy.Query q
    return $ resp ^. Giphy.searchItems
  
  randomApp :: Maybe T.Text -> Giphy.Giphy [Giphy.Gif]
  randomApp q = do
    resp <- Giphy.random $ Giphy.Tag <$> q
    return . pure $ resp ^. Giphy.randomGifItem
  