module Tinc.Facts where

import           Data.List
import           Control.Monad
import           Data.Maybe
import           System.Directory
import           System.Environment
import           System.FilePath
import           Data.Function

import           Tinc.GhcInfo
import           Tinc.SourceDependency
import           Tinc.Types

data NixCache
type Plugins = [Plugin]
type Plugin = (String, FilePath)

data Facts = Facts {
  factsCache :: Path CacheDir
, factsGitCache :: Path GitCache
, factsSourceDependencyCache :: Path SourceDependencyCache
, factsNixCache :: Path NixCache
, factsUseNix :: Bool
, factsNixResolver :: Maybe String
, factsPlugins :: Plugins
, factsGhcInfo :: GhcInfo
} deriving (Eq, Show)

tincEnvVar :: String
tincEnvVar = "TINC_USE_NIX"

useNix :: FilePath -> IO Bool
useNix executablePath = do
  maybe (isInNixStore executablePath) (`notElem` ["no", "0"]) <$> lookupEnv tincEnvVar

isInNixStore :: FilePath -> Bool
isInNixStore = ("/nix/" `isPrefixOf`)

getNixResolver :: IO (Maybe String)
getNixResolver = lookupEnv "TINC_NIX_RESOLVER"

discoverFacts :: FilePath -> IO Facts
discoverFacts executablePath = getGhcInfo >>= discoverFacts_impl executablePath

discoverFacts_impl :: FilePath -> GhcInfo -> IO Facts
discoverFacts_impl executablePath ghcInfo = do
  home <- getHomeDirectory
  useNix_ <- useNix executablePath
  nixResolver <- getNixResolver
  let pluginsDir :: FilePath
      pluginsDir = home </> ".tinc" </> "plugins"

      cacheDir :: Path CacheDir
      cacheDir = Path (home </> ".tinc" </> "cache" </> ghcFlavor ghcInfo)

      gitCache :: Path GitCache
      gitCache = Path (home </> ".tinc" </> "cache" </> "git")

      sourceDependencyCache :: Path SourceDependencyCache
      sourceDependencyCache = Path (home </> ".tinc" </> "cache" </> "add-source")

      nixCache :: Path NixCache
      nixCache = Path (home </> ".tinc" </> "cache" </> "nix")

  createDirectoryIfMissing True (path cacheDir)
  createDirectoryIfMissing True (path nixCache)
  createDirectoryIfMissing True pluginsDir
  plugins <- listAllPlugins pluginsDir
  if useNix_
     then setEnv tincEnvVar "yes"
     else setEnv tincEnvVar "no"
  return Facts {
    factsCache = cacheDir
  , factsGitCache = gitCache
  , factsSourceDependencyCache = sourceDependencyCache
  , factsNixCache = nixCache
  , factsUseNix = useNix_
  , factsNixResolver = nixResolver
  , factsPlugins = plugins
  , factsGhcInfo = ghcInfo
  }

listAllPlugins :: FilePath -> IO Plugins
listAllPlugins pluginsDir = do
  plugins <- listPlugins pluginsDir
  pathPlugins <- getSearchPath >>= listPathPlugins
  return (pathPlugins ++ plugins)

listPlugins :: FilePath -> IO Plugins
listPlugins pluginsDir = do
  exists <- doesDirectoryExist pluginsDir
  if exists
    then do
      files <- mapMaybe (stripPrefix "tinc-") <$> getDirectoryContents pluginsDir
      let f name = (name, pluginsDir </> "tinc-" ++ name)
      filterM isExecutable (map f files)
    else return []

isExecutable :: Plugin -> IO Bool
isExecutable = fmap executable . getPermissions . snd

listPathPlugins :: [FilePath] -> IO Plugins
listPathPlugins = fmap (nubBy ((==) `on` fst) . concat) . mapM listPlugins

module Tinc.Facts where

import           Data.List
import           Control.Monad
import           Data.Maybe
import           System.Directory
import           System.Environment
import           System.FilePath
import           Data.Function

import           Tinc.GhcInfo
import           Tinc.SourceDependency
import           Tinc.Types

data NixCache
type Plugins = [Plugin]
type Plugin = (String, FilePath)

data Facts = Facts {
  factsCache :: Path CacheDir
, factsGitCache :: Path GitCache
, factsSourceDependencyCache :: Path SourceDependencyCache
, factsNixCache :: Path NixCache
, factsUseNix :: Bool
, factsNixResolver :: Maybe String
, factsPlugins :: Plugins
, factsGhcInfo :: GhcInfo
} deriving (Eq, Show)

tincEnvVar :: String
tincEnvVar = "TINC_USE_NIX"

useNix :: FilePath -> IO Bool
useNix executablePath = do
  maybe (isInNixStore executablePath) (`notElem` ["no", "0"]) <$> lookupEnv tincEnvVar

isInNixStore :: FilePath -> Bool
isInNixStore = ("/nix/" `isPrefixOf`)

getNixResolver :: IO (Maybe String)
getNixResolver = lookupEnv "TINC_NIX_RESOLVER"

discoverFacts :: FilePath -> IO Facts
discoverFacts executablePath = getGhcInfo >>= discoverFacts_impl executablePath

discoverFacts_impl :: FilePath -> GhcInfo -> IO Facts
discoverFacts_impl executablePath ghcInfo = do
  home <- getHomeDirectory
  useNix_ <- useNix executablePath
  nixResolver <- getNixResolver
  let pluginsDir :: FilePath
      pluginsDir = home </> ".tinc" </> "plugins"

      cacheDir :: Path CacheDir
      cacheDir = Path (home </> ".tinc" </> "cache" </> ghcFlavor ghcInfo)

      gitCache :: Path GitCache
      gitCache = Path (home </> ".tinc" </> "cache" </> "git")

      sourceDependencyCache :: Path SourceDependencyCache
      sourceDependencyCache = Path (home </> ".tinc" </> "cache" </> "add-source")

      nixCache :: Path NixCache
      nixCache = Path (home </> ".tinc" </> "cache" </> "nix")

  createDirectoryIfMissing True (path cacheDir)
  createDirectoryIfMissing True (path nixCache)
  createDirectoryIfMissing True pluginsDir
  plugins <- listAllPlugins pluginsDir
  if useNix_
     then setEnv tincEnvVar "yes"
     else setEnv tincEnvVar "no"
  return Facts {
    factsCache = cacheDir
  , factsGitCache = gitCache
  , factsSourceDependencyCache = sourceDependencyCache
  , factsNixCache = nixCache
  , factsUseNix = useNix_
  , factsNixResolver = nixResolver
  , factsPlugins = plugins
  , factsGhcInfo = ghcInfo
  }

listAllPlugins :: FilePath -> IO Plugins
listAllPlugins pluginsDir = do
  plugins <- listPlugins pluginsDir
  pathPlugins <- getSearchPath >>= listPathPlugins
  return (pathPlugins ++ plugins)

listPlugins :: FilePath -> IO Plugins
listPlugins pluginsDir = do
  exists <- doesDirectoryExist pluginsDir
  if exists
    then do
      files <- mapMaybe (stripPrefix "tinc-") <$> getDirectoryContents pluginsDir
      let f name = (name, pluginsDir </> "tinc-" ++ name)
      filterM isExecutable (map f files)
    else return []

isExecutable :: Plugin -> IO Bool
isExecutable = fmap executable . getPermissions . snd

listPathPlugins :: [FilePath] -> IO Plugins
listPathPlugins = fmap (nubBy ((==) `on` fst) . concat) . mapM listPlugins
