-- | Given a directory, populate it with all necessary files to run @jupyter kernelspec install@.
--
-- Currently created files include:
--  * @kernel.js@: (optional) Javascript to include in the notebook frontend.
--  * @logo-64x64.png@: (optional) Small logo PNG to include in the notebook frontend UI.
--  * @kernel.json@: (required) JSON file containing kernel invocation command and other metadata.
--
-- The passed in directory is created and should not exist; if it already exists, it will be
-- deleted.
prepareKernelspecDirectory :: Kernelspec -> FilePath -> IO ()
prepareKernelspecDirectory kernelspec dir = do
  -- Make sure the directory doesn't already exist. If we didn't delete the directory, then later
  -- kernelspec installs may inherit files created by previous kernelspec installs.
  exists <- doesDirectoryExist dir
  when exists $ removeDirectoryRecursive dir

  createDirectoryIfMissing True dir
  copyKernelspecFiles kernelspec
  generateKernelJSON kernelspec

  where
    -- Copy files indicated by the Kernelspec data type into the directory.
    copyKernelspecFiles :: Kernelspec -> IO ()
    copyKernelspecFiles Kernelspec { .. } = do
      whenJust kernelspecJsFile $ \file -> copyFile file $ dir ++ "/kernel.js"
      whenJust kernelspecLogoFile $ \file -> copyFile file $ dir ++ "/logo-64x64.png"

    -- Generate the kernel.json data structure from the Kernelspec datatype.
    generateKernelJSON :: Kernelspec -> IO ()
    generateKernelJSON Kernelspec { .. } = do
      exePath <- getExecutablePath
      withFile (dir ++ "/kernel.json") WriteMode $
        flip LBS.hPutStr $
          encode $
            object
              [ "argv" .= kernelspecCommand exePath "{connection_file}"
              , "display_name" .= kernelspecDisplayName
              , "language" .= kernelspecLanguage
              , "env" .= kernelspecEnv
              ]

    whenJust :: Maybe a -> (a -> IO ()) -> IO ()
    whenJust Nothing _ = return ()
    whenJust (Just a) f = f a

-- | Install a kernelspec using @jupyter kernelspec install@.
--
-- Throws a 'JupyterKernelspecException' on failure.
prepareKernelspecDirectory :: Kernelspec -> FilePath -> IO ()
prepareKernelspecDirectory kernelspec dir = do
  -- Make sure the directory doesn't already exist. If we didn't delete the directory, then later
  -- kernelspec installs may inherit files created by previous kernelspec installs.
  exists <- doesDirectoryExist dir
  when exists $ removeDirectoryRecursive dir

  createDirectoryIfMissing True dir
  copyKernelspecFiles kernelspec
  generateKernelJSON kernelspec

  where
    -- Copy files indicated by the Kernelspec data type into the directory.
    copyKernelspecFiles :: Kernelspec -> IO ()
    copyKernelspecFiles Kernelspec { .. } = do
      whenJust kernelspecJsFile $ \file -> copyFile file $ dir ++ "/kernel.js"
      whenJust kernelspecLogoFile $ \file -> copyFile file $ dir ++ "/logo-64x64.png"

    -- Generate the kernel.json data structure from the Kernelspec datatype.
    generateKernelJSON :: Kernelspec -> IO ()
    generateKernelJSON Kernelspec { .. } = do
      exePath <- getExecutablePath
      withFile (dir ++ "/kernel.json") WriteMode $
        flip LBS.hPutStr $
          encode $
            object
              [ "argv" .= kernelspecCommand exePath "{connection_file}"
              , "display_name" .= kernelspecDisplayName
              , "language" .= kernelspecLanguage
              , "env" .= kernelspecEnv
              ]

    whenJust :: Maybe a -> (a -> IO ()) -> IO ()
    whenJust Nothing _ = return ()
    whenJust (Just a) f = f a

-- | Install a kernelspec using @jupyter kernelspec install@.
--
-- Throws a 'JupyterKernelspecException' on failure.
prepareKernelspecDirectory kernelspec dir = do
  -- Make sure the directory doesn't already exist. If we didn't delete the directory, then later
  -- kernelspec installs may inherit files created by previous kernelspec installs.
  exists <- doesDirectoryExist dir
  when exists $ removeDirectoryRecursive dir

  createDirectoryIfMissing True dir
  copyKernelspecFiles kernelspec
  generateKernelJSON kernelspec

  where
    -- Copy files indicated by the Kernelspec data type into the directory.
    copyKernelspecFiles :: Kernelspec -> IO ()
    copyKernelspecFiles Kernelspec { .. } = do
      whenJust kernelspecJsFile $ \file -> copyFile file $ dir ++ "/kernel.js"
      whenJust kernelspecLogoFile $ \file -> copyFile file $ dir ++ "/logo-64x64.png"

    -- Generate the kernel.json data structure from the Kernelspec datatype.
    generateKernelJSON :: Kernelspec -> IO ()
    generateKernelJSON Kernelspec { .. } = do
      exePath <- getExecutablePath
      withFile (dir ++ "/kernel.json") WriteMode $
        flip LBS.hPutStr $
          encode $
            object
              [ "argv" .= kernelspecCommand exePath "{connection_file}"
              , "display_name" .= kernelspecDisplayName
              , "language" .= kernelspecLanguage
              , "env" .= kernelspecEnv
              ]

    whenJust :: Maybe a -> (a -> IO ()) -> IO ()
    whenJust Nothing _ = return ()
    whenJust (Just a) f = f a

-- | Install a kernelspec using @jupyter kernelspec install@.
--
-- Throws a 'JupyterKernelspecException' on failure.