{-# LANGUAGE OverloadedStrings #-}

-- |
-- Module      : $header$
-- Copyright   : (c) Laurent P René de Cotret, 2019 - present
-- License     : GNU GPL, version 2 or above
-- Maintainer  : laurent.decotret@outlook.com
-- Stability   : internal
-- Portability : portable
--
-- Specification of renderers.
module Text.Pandoc.Filter.Plot.Renderers
  ( renderer,
    preambleSelector,
    parseExtraAttrs,
    executable,
    availableToolkits,
    availableToolkitsM,
    unavailableToolkits,
    unavailableToolkitsM,
    supportedSaveFormats,
    OutputSpec (..),
    Executable (..),
    Renderer (..),
  )
where

import Control.Concurrent.Async.Lifted (forConcurrently)
import Control.Monad.Reader (local)
import Data.Functor ((<&>))
import Data.List ((\\))
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import Data.Maybe (catMaybes, isJust)
import Data.Text (Text, pack)
import System.Directory (findExecutable)
import System.Exit (ExitCode (..))
import Text.Pandoc.Filter.Plot.Monad
import Text.Pandoc.Filter.Plot.Monad.Logging
  ( Logger (lVerbosity),
  )
import Text.Pandoc.Filter.Plot.Renderers.Bokeh
  ( bokeh,
    bokehSupportedSaveFormats,
  )
import Text.Pandoc.Filter.Plot.Renderers.D2
  ( d2,
    d2SupportedSaveFormats,
  )
import Text.Pandoc.Filter.Plot.Renderers.GGPlot2
  ( ggplot2,
    ggplot2SupportedSaveFormats,
  )
import Text.Pandoc.Filter.Plot.Renderers.GNUPlot
  ( gnuplot,
    gnuplotSupportedSaveFormats,
  )
import Text.Pandoc.Filter.Plot.Renderers.Graphviz
  ( graphviz,
    graphvizSupportedSaveFormats,
  )
import Text.Pandoc.Filter.Plot.Renderers.Mathematica
  ( mathematica,
    mathematicaSupportedSaveFormats,
  )
import Text.Pandoc.Filter.Plot.Renderers.Matlab
  ( matlab,
    matlabSupportedSaveFormats,
  )
import Text.Pandoc.Filter.Plot.Renderers.Matplotlib
  ( matplotlib,
    matplotlibSupportedSaveFormats,
  )
import Text.Pandoc.Filter.Plot.Renderers.Octave
  ( octave,
    octaveSupportedSaveFormats,
  )
import Text.Pandoc.Filter.Plot.Renderers.PlantUML
  ( plantuml,
    plantumlSupportedSaveFormats,
  )
import Text.Pandoc.Filter.Plot.Renderers.PlotlyPython
  ( plotlyPython,
    plotlyPythonSupportedSaveFormats,
  )
import Text.Pandoc.Filter.Plot.Renderers.PlotlyR
  ( plotlyR,
    plotlyRSupportedSaveFormats,
  )
import Text.Pandoc.Filter.Plot.Renderers.Plotsjl
  ( plotsjl,
    plotsjlSupportedSaveFormats,
  )
import Text.Pandoc.Filter.Plot.Renderers.SageMath
  ( sagemath,
    sagemathSupportedSaveFormats,
  )
import Text.Pandoc.Filter.Plot.Renderers.Asymptote
  ( asymptote,
    asymptoteSupportedSaveFormats,
  )
import Text.Pandoc.Filter.Plot.Renderers.Mermaid
  ( mermaid,
    mermaidSupportedSaveFormats,
  )
-- | Get the renderer associated with a toolkit.
-- If the renderer has not been used before,
-- initialize it and store where it is. It will be re-used.
renderer :: Toolkit -> PlotM Renderer
renderer Matplotlib = matplotlib
renderer PlotlyPython = plotlyPython
renderer PlotlyR = plotlyR
renderer Matlab = matlab
renderer Mathematica = mathematica
renderer Octave = octave
renderer GGPlot2 = ggplot2
renderer GNUPlot = gnuplot
renderer Graphviz = graphviz
renderer Bokeh = bokeh
renderer Plotsjl = plotsjl
renderer PlantUML = plantuml
renderer SageMath = sagemath
renderer D2 = d2
renderer Asymptote = asymptote
renderer Mermaid = mermaid

-- | Save formats supported by this renderer.
supportedSaveFormats :: Toolkit -> [SaveFormat]
supportedSaveFormats Matplotlib = matplotlibSupportedSaveFormats
supportedSaveFormats PlotlyPython = plotlyPythonSupportedSaveFormats
supportedSaveFormats PlotlyR = plotlyRSupportedSaveFormats
supportedSaveFormats Matlab = matlabSupportedSaveFormats
supportedSaveFormats Mathematica = mathematicaSupportedSaveFormats
supportedSaveFormats Octave = octaveSupportedSaveFormats
supportedSaveFormats GGPlot2 = ggplot2SupportedSaveFormats
supportedSaveFormats GNUPlot = gnuplotSupportedSaveFormats
supportedSaveFormats Graphviz = graphvizSupportedSaveFormats
supportedSaveFormats Bokeh = bokehSupportedSaveFormats
supportedSaveFormats Plotsjl = plotsjlSupportedSaveFormats
supportedSaveFormats PlantUML = plantumlSupportedSaveFormats
supportedSaveFormats SageMath = sagemathSupportedSaveFormats
supportedSaveFormats D2 = d2SupportedSaveFormats
supportedSaveFormats Asymptote = asymptoteSupportedSaveFormats
supportedSaveFormats Mermaid = mermaidSupportedSaveFormats

-- | The function that maps from configuration to the preamble.
preambleSelector :: Toolkit -> (Configuration -> Script)
preambleSelector Matplotlib = matplotlibPreamble
preambleSelector PlotlyPython = plotlyPythonPreamble
preambleSelector PlotlyR = plotlyRPreamble
preambleSelector Matlab = matlabPreamble
preambleSelector Mathematica = mathematicaPreamble
preambleSelector Octave = octavePreamble
preambleSelector GGPlot2 = ggplot2Preamble
preambleSelector GNUPlot = gnuplotPreamble
preambleSelector Graphviz = graphvizPreamble
preambleSelector Bokeh = bokehPreamble
preambleSelector Plotsjl = plotsjlPreamble
preambleSelector PlantUML = plantumlPreamble
preambleSelector SageMath = sagemathPreamble
preambleSelector D2 = d2Preamble
preambleSelector Asymptote = asyPreamble
preambleSelector Mermaid = mermaidPreamble

-- | Parse code block headers for extra attributes that are specific
-- to this renderer. By default, no extra attributes are parsed.
parseExtraAttrs :: Toolkit -> Map Text Text -> Map Text Text
parseExtraAttrs Matplotlib =
  M.filterWithKey
    ( \k _ ->
        k
          `elem` [ pack $ show MatplotlibTightBBoxK,
                   pack $ show MatplotlibTransparentK
                 ]
    )
parseExtraAttrs _ = return mempty

-- | List of toolkits available on this machine.
-- The executables to look for are taken from the configuration.
availableToolkits :: Configuration -> IO [Toolkit]
availableToolkits conf = runPlotM Nothing conf availableToolkitsM

-- | List of toolkits not available on this machine.
-- The executables to look for are taken from the configur
unavailableToolkits :: Configuration -> IO [Toolkit]
unavailableToolkits conf = runPlotM Nothing conf unavailableToolkitsM

-- | Monadic version of @availableToolkits@.
--
-- Note that logging is disabled
availableToolkitsM :: PlotM [Toolkit]
availableToolkitsM = asNonStrictAndSilent $ do
  mtks <- forConcurrently toolkits $ \tk -> do
    r <- renderer tk
    exe <- executable tk
    a <- isAvailable exe (rendererAvailability r)
    if a
      then return $ Just tk
      else return Nothing
  return $ catMaybes mtks
  where
    asNonStrictAndSilent = local (\(RuntimeEnv f c l d s) -> RuntimeEnv f (c {strictMode = False}) (l {lVerbosity = Silent}) d s)

    -- \| Check that the supplied command results in
    -- an exit code of 0 (i.e. no errors)
    commandSuccess :: Text -> PlotM Bool
    commandSuccess s = do
      cwd <- asks envCWD
      (ec, _) <- runCommand cwd s
      debug $ mconcat ["Command ", s, " resulted in ", pack $ show ec]
      return $ ec == ExitSuccess

    isAvailable :: Executable -> AvailabilityCheck -> PlotM Bool
    isAvailable exe (CommandSuccess f) = commandSuccess (f exe)
    isAvailable exe ExecutableExists = liftIO $ findExecutable (pathToExe exe) <&> isJust

-- | Monadic version of @unavailableToolkits@
unavailableToolkitsM :: PlotM [Toolkit]
unavailableToolkitsM = (\\) toolkits <$> availableToolkitsM
