content: module Variants where
  
  import Base
  import Data.Colour
  import Data.Colour.Palette.BrewerSet
  import Development.Shake hiding ((<//>))
  import Juvix.Prelude
  import Juvix.Prelude.Env
  import System.Process
  
  allVariantsExcept :: [Lang] -> [VariantId] -> [Variant]
  allVariantsExcept ls vs =
    filter
      (\v -> (v ^. variantLanguage) `notElem` ls)
      (map getVariant (filter (`notElem` vs) allElements))
  
  allVariants :: [Variant]
  allVariants = map getVariant allElements
  
  defaultVariants :: [Variant]
  defaultVariants = allVariantsExcept [] [CoreEval]
  
  data VariantId
    = OcamlExe
    | OcamlByte
    | HaskellExe
    | HaskellStrict
    | JuvixExe
    | JuvixWasm
    | ClangExe
    | ClangWasm
    | RuntimeExe
    | RuntimeWasm
    | CoreEval
    deriving stock (Bounded, Enum, Eq, Ord)
  
  getVariantIx :: VariantId -> Int
  getVariantIx = fromEnum
  
  -- | Note that only 12 colors are available
  getVariantColor :: VariantId -> Colour Double
  getVariantColor v
    | i < 12 = brewerSet Paired 12 !! i
    | otherwise = error "not enough colors. Please extend the palette"
    where
      i :: Int
      i = getVariantIx v
  
  getVariant :: VariantId -> Variant
  getVariant = \case
    OcamlExe -> ocamlExe
    OcamlByte -> ocamlByteExe
    HaskellExe -> haskellExe
    HaskellStrict -> haskellStrictExe
    JuvixExe -> juvixExe
    JuvixWasm -> juvixWasm
    ClangExe -> clangExe
    ClangWasm -> clangWasm
    RuntimeExe -> runtimeExe
    RuntimeWasm -> runtimeWasm
    CoreEval -> coreEval
  
  defaultExt :: [String]
  defaultExt = [".exe"]
  
  runWasm :: Path Abs File -> IO ()
  runWasm p = void (readProcess "wasmer" [toFilePath p] "")
  
  runExe :: Path Abs File -> IO ()
  runExe p = void (readProcess (toFilePath p) [] "")
  
  outOptions :: BuildArgs -> [String] -> [String]
  outOptions args ext = ["-o", toFilePath (binFile args ext)]
  
  commonOptions :: BuildArgs -> [String] -> [String]
  commonOptions args ext = toFilePath (args ^. buildSrc) : outOptions args ext
  
  ocamlExe :: Variant
  ocamlExe =
    Variant
      { _variantTitle = Nothing,
        _variantLanguage = Ocaml,
        _variantExtensions = ext,
        _variantColor = getVariantColor OcamlExe,
        _variantRun = runExe,
        _variantBuild = \args ->
          command_ [] "ocamlopt" ("-O2" : commonOptions args ext)
      }
    where
      ext :: [String]
      ext = defaultExt
  
  ocamlByteExe :: Variant
  ocamlByteExe =
    Variant
      { _variantTitle = Just "byte",
        _variantLanguage = Ocaml,
        _variantExtensions = ext,
        _variantColor = getVariantColor OcamlByte,
        _variantRun = runExe,
        _variantBuild = \args ->
          command_ [] "ocamlc" (commonOptions args ext)
      }
    where
      ext :: [String]
      ext = ".byte" : defaultExt
  
  haskellCommon :: [String]
  haskellCommon = ["-O2", "-no-keep-hi-files", "-no-keep-o-files"]
  
  haskellExe :: Variant
  haskellExe =
    Variant
      { _variantTitle = Nothing,
        _variantLanguage = Haskell,
        _variantExtensions = ext,
        _variantColor = getVariantColor HaskellExe,
        _variantRun = runExe,
        _variantBuild = \args ->
          command_ [] "ghc" (haskellCommon ++ commonOptions args ext)
      }
    where
      ext :: [String]
      ext = defaultExt
  
  haskellStrictExe :: Variant
  haskellStrictExe =
    Variant
      { _variantTitle = Just "strict",
        _variantLanguage = Haskell,
        _variantExtensions = ext,
        _variantColor = getVariantColor HaskellStrict,
        _variantRun = runExe,
        _variantBuild = \args ->
          command_ [] "ghc" (haskellCommon ++ ["-XStrict"] ++ commonOptions args ext)
      }
    where
      ext :: [String]
      ext = ".strict" : defaultExt
  
  juvixExe :: Variant
  juvixExe =
    Variant
      { _variantTitle = Nothing,
        _variantLanguage = Juvix,
        _variantExtensions = ext,
        _variantColor = getVariantColor JuvixExe,
        _variantRun = runExe,
        _variantBuild = \args ->
          command_ [] "juvix" (juvixCommon ++ ["native"] ++ commonOptions args ext)
      }
    where
      ext :: [String]
      ext = defaultExt
  
  juvixCommon :: [String]
  juvixCommon = ["compile"]
  
  juvixWasm :: Variant
  juvixWasm =
    Variant
      { _variantTitle = Just "wasm",
        _variantLanguage = Juvix,
        _variantExtensions = ext,
        _variantColor = getVariantColor JuvixWasm,
        _variantRun = runWasm,
        _variantBuild = \args ->
          command_ [] "juvix" (juvixCommon ++ ["wasi"] ++ commonOptions args ext)
      }
    where
      ext :: [String]
      ext = [".wasm"]
  
  runtimeCommon :: [String]
  runtimeCommon = ["dev", "runtime", "compile"]
  
  runtimeExe :: Variant
  runtimeExe =
    Variant
      { _variantTitle = Nothing,
        _variantLanguage = Runtime,
        _variantExtensions = ext,
        _variantColor = getVariantColor RuntimeExe,
        _variantRun = runExe,
        _variantBuild = \args ->
          command_ [] "juvix" (runtimeCommon ++ commonOptions args ext)
      }
    where
      ext :: [String]
      ext = defaultExt
  
  runtimeWasm :: Variant
  runtimeWasm =
    Variant
      { _variantTitle = Just "wasm",
        _variantLanguage = Runtime,
        _variantExtensions = ext,
        _variantColor = getVariantColor RuntimeWasm,
        _variantRun = runWasm,
        _variantBuild = \args ->
          command_ [] "juvix" (runtimeCommon ++ ["--target=wasi"] ++ commonOptions args ext)
      }
    where
      ext :: [String]
      ext = [".wasm"]
  
  clangExe :: Variant
  clangExe =
    Variant
      { _variantTitle = Nothing,
        _variantLanguage = C,
        _variantExtensions = ext,
        _variantColor = getVariantColor ClangExe,
        _variantRun = runExe,
        _variantBuild = \args ->
          command_ [] "clang" ("-O3" : commonOptions args ext)
      }
    where
      ext :: [String]
      ext = defaultExt
  
  clangWasm :: Variant
  clangWasm =
    Variant
      { _variantTitle = Just "wasm",
        _variantLanguage = C,
        _variantExtensions = ext,
        _variantColor = getVariantColor ClangWasm,
        _variantRun = runWasm,
        _variantBuild = \args -> do
          wasipath <- getWasiSysrootPathStr
          command_ [] "clang" (["-Os", "-nodefaultlibs", "--sysroot", wasipath, "-lc", "--target=wasm32-wasi"] ++ commonOptions args ext)
      }
    where
      ext :: [String]
      ext = [".wasm"]
  
  coreEval :: Variant
  coreEval =
    Variant
      { _variantTitle = Nothing,
        _variantLanguage = Core,
        _variantExtensions = ext,
        _variantColor = getVariantColor CoreEval,
        _variantRun = \src -> void (readProcess "juvix" ["dev", "core", "eval", toFilePath src] ""),
        _variantBuild = \args ->
          command_ [] "cp" (map toFilePath [args ^. buildSrc, binFile args ext])
      }
    where
      ext :: [String]
      ext = [langExtension Core]
  