{-|
Module      : Devel.Config
Description : To have wai-devel depend on it's environment a lot less.
Copyright   : (c) 2015 Njagi Mwaniki
License     : MIT
Maintainer  : njagi@urbanslug.com
Stability   : experimental
Portability : POSIX

Currently we'll query for this information from the stack binary.

Ideal case it to use the stack library to figure out depends and stuff.
Depending on the stack library causes a breakage in the dependecy tree.
As of now I can't find a single function (or set of functions) that fetches this information from stack.
Closest thing is in the stack Main module.
        
Will be rewritten to depend on the stack library.
-}
{-# LANGUAGE ScopedTypeVariables #-}

module Devel.Config 
( setConfig
, getPath
, getPkgDb
) where

import System.Process (readProcessWithExitCode)
import System.Environment (unsetEnv, setEnv, lookupEnv)
import Control.Exception (catch)

import System.FilePath.Posix (pathSeparator)
import System.Directory (getCurrentDirectory, getHomeDirectory, doesDirectoryExist)
import System.Info (arch, compilerName, os)

import Devel.Types

setConfig :: IO ()
setConfig = do
  (path, pkgDb) <- getConfig
  _ <- unsetEnv "PATH"
  _ <- unsetEnv "GHC_PACKAGE_PATH"
  _ <- setEnv "PATH" path
  setEnv "GHC_PACKAGE_PATH" pkgDb
  

getConfig :: IO Config
getConfig = do
  -- If stack isn't installed use cabal-install.
  catch getStackConfig getCabalConfig
  where 
    getStackConfig :: IO Config
    getStackConfig = do 
      (_, stdout, _) <- readProcessWithExitCode "stack" ["path"] ""
      parseConfig stdout


    parseConfig :: String -> IO Config
    parseConfig stdout = do
      let outputList = lines stdout
          tupleList = map (span (/=':') ) outputList
          path = concatMap getPath tupleList
          pkgDb = concatMap getPkgDb tupleList
      return (path, pkgDb)

getCabalConfig :: IOError -> IO Config
getCabalConfig _ = do
  mPath <- lookupEnv "PATH"
  mPkgDb <- lookupEnv "GHC_PACKAGE_PATH"
  
  ghcVersion <- catch getGhcVersion getGhcVersionError
  
  cwd <- getCurrentDirectory
  homeDir <- getHomeDirectory
  isSandBoxed <- doesDirectoryExist ".cabal-sandbox"
  
  let compilerVersion' = last $ words ghcVersion
      forSandboxPath = arch ++ "-" ++ os ++ "-" ++ compilerName ++ "-" ++ compilerVersion'
      forUserPath    = arch ++ "-" ++ os ++ "-" ++ compilerVersion'
      forGlobalPath  = compilerName ++ "-" ++ compilerVersion'


      sandboxPkgDb = if isSandBoxed
                        then cwd ++ (pathSeparator : ".cabal-sandbox") 
                             ++ (pathSeparator : forSandboxPath) ++ "-packages.conf.d:"
                        else "" -- We're not using a sandbox.

      userPkgDb = homeDir ++ (pathSeparator: ".ghc") ++ (pathSeparator:forUserPath) 
                  ++ (pathSeparator:"package.conf.d:")
      globalPkgDb = "/usr/lib/" ++ forGlobalPath ++"/package.conf.d:"

      pkgDb' = sandboxPkgDb ++ userPkgDb ++ globalPkgDb

      path = case mPath of
                   Just p -> p
                   Nothing -> fail "The environment variable PATH isn't set."
      pkgDb = case mPkgDb of
                   Just db -> db
                   Nothing -> pkgDb'

  return (path, pkgDb)

getGhcVersion :: IO String
getGhcVersion = do
  (_, stdout, _) <- readProcessWithExitCode "ghc" ["--version"] ""
  return stdout

-- Give proper fail information.
getGhcVersionError :: IOError -> IO String
getGhcVersionError _ = do
  fail $ "Is GHC not in your PATH? "
         ++ "Because wai-devel can't get the version number from: "
         ++ "`ghc --version`"

getPath :: (String, String) -> String
getPath (key,value)
        | key == "bin-path" = dropWhile (==':') $ filter (/=' ') value
        | otherwise = ""

getPkgDb :: (String, String) -> String
getPkgDb (key,value)
          | key == "ghc-package-path" = dropWhile (==':') $ filter (/=' ') value
          | otherwise = ""