{-# LANGUAGE CPP             #-}
{-# LANGUAGE TemplateHaskell #-}

module Development.IDE.Graph.Internal.Paths (getDataFile) where

import           Paths_hls_graph

#ifndef FILE_EMBED
import           Control.Exception  (SomeException (SomeException), catch)
import           Control.Monad      (filterM)
import           System.Directory   (doesFileExist, getCurrentDirectory)
import           System.Environment (getExecutablePath)
import           System.FilePath    (takeDirectory, (</>))
import           System.IO.Unsafe   (unsafePerformIO)
#endif

#ifdef FILE_EMBED
import qualified Data.ByteString    as BS
import qualified Data.ByteString    as LBS
import           Data.FileEmbed

initDataDirectory :: IO ()
initDataDirectory = pure ()

htmlDataFiles :: [(FilePath, BS.ByteString)]
htmlDataFiles =
  [ ("profile.html",  $(embedFile "html/profile.html"))
  , ("progress.html", $(embedFile "html/progress.html"))
  , ("shake.js",      $(embedFile "html/shake.js"))
  ]

readDataFileHTML :: FilePath -> IO LBS.ByteString
readDataFileHTML file = do
    case lookup file htmlDataFiles of
      Nothing -> fail $ "Could not find data file " ++ file ++ " in embedded data files!"
      Just x  -> pure (LBS.fromStrict x)

manualDirData :: [(FilePath, BS.ByteString)]
manualDirData = $(embedDir "docs/manual")

hasManualData :: IO Bool
hasManualData = pure True

copyManualData :: FilePath -> IO ()
copyManualData dest = do
    createDirectoryRecursive dest
    forM_ manualDirData $ \(file, bs) -> do
        BS.writeFile (dest </> file) bs

#else
-- We want getDataFileName to be relative to the current directory on program startup,
-- even if we issue a change directory command. Therefore, first call caches, future ones read.

{-# NOINLINE dataDirs #-}
dataDirs :: [String]
dataDirs :: [String]
dataDirs = IO [String] -> [String]
forall a. IO a -> a
unsafePerformIO (IO [String] -> [String]) -> IO [String] -> [String]
forall a b. (a -> b) -> a -> b
$ do
    String
datdir <- IO String
getDataDir
    String
exedir <- String -> String
takeDirectory (String -> String) -> IO String -> IO String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO String
getExecutablePath IO String -> (SomeException -> IO String) -> IO String
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
`catch` \SomeException{} -> String -> IO String
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
""
    String
curdir <- IO String
getCurrentDirectory
    [String] -> IO [String]
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([String] -> IO [String]) -> [String] -> IO [String]
forall a b. (a -> b) -> a -> b
$ [String
datdir] [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
exedir | String
exedir String -> String -> Bool
forall a. Eq a => a -> a -> Bool
/= String
""] [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
curdir]


getDataFile :: FilePath -> IO FilePath
getDataFile :: String -> IO String
getDataFile String
file = do
    let poss :: [String]
poss = (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String -> String -> String
</> String
file) [String]
dataDirs
    [String]
res <- (String -> IO Bool) -> [String] -> IO [String]
forall (m :: * -> *) a.
Applicative m =>
(a -> m Bool) -> [a] -> m [a]
filterM String -> IO Bool
doesFileExist [String]
poss
    case [String]
res of
        [] -> String -> IO String
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> IO String) -> String -> IO String
forall a b. (a -> b) -> a -> b
$ [String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ (String
"Could not find data file " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
file String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
", looked in:") String -> [String] -> [String]
forall a. a -> [a] -> [a]
: (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String
"  " String -> String -> String
forall a. [a] -> [a] -> [a]
++) [String]
poss
        String
x:[String]
_ -> String -> IO String
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
x

#endif