content: --
  -- Copyright 2016, NICTA
  --
  -- This software may be distributed and modified according to the terms of
  -- the GNU General Public License version 2. Note that NO WARRANTY is provided.
  -- See "LICENSE_GPLv2.txt" for details.
  --
  -- @TAG(NICTA_GPL)
  --
  
  {-# LANGUAGE CPP #-}
  #if __GLASGOW_HASKELL__ < 709
  import Control.Applicative ((<$>))
  #endif
  import Control.Arrow (first)
  import Control.Exception (SomeException, catch)
  
  import Distribution.PackageDescription
  import Distribution.Simple
  #if MIN_VERSION_Cabal (2,0,0)
  import Distribution.Simple.BuildPaths (autogenPackageModulesDir)
  #else
  import Distribution.Simple.BuildPaths (autogenModulesDir)
  #endif
  import Distribution.Simple.LocalBuildInfo as L
  import qualified Distribution.Simple.Setup as S
  import Distribution.Simple.Utils
  import Distribution.PackageDescription
  import Distribution.Verbosity (Verbosity)
  
  import System.Directory(removeFile, doesDirectoryExist)
  import System.Exit
  import System.FilePath ((</>), (<.>), takeExtension)
  import qualified System.FilePath.Posix as Px
  import System.Process (readProcess)
  
  -- Flags
  isRelease :: S.ConfigFlags -> Bool
  isRelease flags =
  #if MIN_VERSION_Cabal (2,2,0)
    case lookup (mkFlagName "release") (unFlagAssignment $ S.configConfigurationsFlags flags) of
  #elif MIN_VERSION_Cabal (2,0,0)
    case lookup (mkFlagName "release") (S.configConfigurationsFlags flags) of
  #else
    case lookup (FlagName "release") (S.configConfigurationsFlags flags) of
  #endif
      Just x  -> x
      Nothing -> False
  
  -- Git Hash
  gitHash :: IO String
  gitHash = do h <- Control.Exception.catch (readProcess "git" ["rev-parse", "--short=10", "HEAD"] "")
                 (\e -> let e' = (e :: SomeException) in return "devel")
               return $ takeWhile (/= '\n') h
  
  -- Version Module
  generateVersionModule verbosity dir release flags = do
    hash <- gitHash
    let versionModulePath = dir </> "Version_cogent" <.> "hs"
    putStrLn $ "Generating " ++ versionModulePath ++
      if release then " for release" else " for dev " ++ hash ++ "\n" ++
        "(Configured with flags: " ++ flagsInfo ++ ")"
    createDirectoryIfMissingVerbose verbosity True dir
    
  #if MIN_VERSION_Cabal (2,0,0)
    rewriteFileEx verbosity versionModulePath (versionModuleContents hash)
  #else
    rewriteFile versionModulePath (versionModuleContents hash)
  #endif
  
    where 
      flagsInfo = unwords (flip map flags (\(f,a) -> (if a then "+" else "-") ++ f))
      versionModuleContents h = "module Version_cogent where\n\n" ++
            "gitHash :: String\n" ++
              (if release
                 then "gitHash = \"\"\n"
                 else "gitHash = \"" ++ h ++ "\"\n") ++
              "configFlags :: String" ++ "\n" ++
              "configFlags = \"Built with flags: " ++ flagsInfo ++ "\""
  
  
  -- Configure
  cogentConfigure :: Args -> S.ConfigFlags -> PackageDescription -> LocalBuildInfo -> IO ()
  cogentConfigure _ flags _ lbi = do
  #if MIN_VERSION_Cabal (2,0,0)
    generateVersionModule verbosity (autogenPackageModulesDir lbi) (isRelease (configFlags lbi)) flagAssignment
  #else
    generateVersionModule verbosity (autogenModulesDir lbi) (isRelease (configFlags lbi)) flagAssignment
  #endif
    where
      verbosity = S.fromFlag $ S.configVerbosity flags
      version = pkgVersion . package $ localPkgDescr lbi
  #if MIN_VERSION_Cabal (2,2,0)
      flagAssignment = map (first unFlagName) $ unFlagAssignment $ S.configConfigurationsFlags flags
  #else 
      flagAssignment = map (first unFlagName) $ S.configConfigurationsFlags flags
  #endif
  
  -- Copy
  copyGumHdrs :: Verbosity -> PackageDescription -> LocalBuildInfo -> CopyDest -> IO ()
  copyGumHdrs verbosity pkg lbi copy = do
    let hdrsdest = datadir (L.absoluteInstallDirs pkg lbi copy) </> "include"
    putStrLn $ "Installing Gum headers in `" ++ hdrsdest ++ "'..."
    createDirectoryIfMissingVerbose verbosity True hdrsdest
    errorMsg <- installDirectoryContents verbosity "lib" hdrsdest
    case errorMsg of 
      Nothing -> return ()
      Just x  -> do putStrLn x; putStrLn "Are you using cabal2.4?"
      where
        -- This function is copied from Cabal-1.24.2.0 and modified
        installDirectoryContents :: Verbosity -> FilePath -> FilePath -> IO (Maybe String)
        installDirectoryContents verbosity srcDir destDir = do
          info verbosity ("copy directory '" ++ srcDir ++ "' to '" ++ destDir ++ "'...")
          b <- doesDirectoryExist srcDir
          b' <- doesDirectoryExist destDir
          if not b then do
            return $ Just $ "Error: Source directory '" ++ srcDir ++ "' not found"
          else do
            if not b' then do
              return $ Just $ "Error: Destination directory '" ++ destDir ++ "' not found"
            else do
              srcFiles <- getDirectoryContentsRecursive srcDir
              let srcFiles' = filter ((`elem` [".ac", ".ah", ".h", ".cogent", ".c"]) . takeExtension) srcFiles
              installOrdinaryFiles verbosity destDir [ (srcDir, f) | f <- srcFiles' ]
              return Nothing
                 
  
  installManPage :: Verbosity -> PackageDescription -> LocalBuildInfo -> CopyDest -> IO ()
  installManPage verbosity pkg lbi copy = do
    let mandest = mandir (L.absoluteInstallDirs pkg lbi copy) ++ "/man1"
    putStrLn $ "Installing man page to `" ++ mandest ++ "'..."
    r <- rawSystemExitCode verbosity "perl" ["scripts/man-gen.pl", mandest, buildDir lbi </> "cogent/cogent"]
    case r of
      ExitSuccess -> return ()
      ExitFailure n -> putStrLn $ "Installing man page failed: exit code " ++ show n
  
  
  -- Test
  -- cabal has two unrelated "dataDir" variables.
  -- We need to use the one in the install directory where cogent is installed.
  fixPkg pkg target = pkg { dataDir = target }
  
  -- "Args" argument of testHooks have been added in cabal 1.22.0
  #if __GLASGOW_HASKELL__ < 710
  originalTestHook _ = testHook simpleUserHooks
  #else
  originalTestHook = testHook simpleUserHooks
  #endif
  
  cogentTestHook args pkg lbi hooks flags = do
    let target = datadir $ L.absoluteInstallDirs pkg lbi NoCopyDest
    originalTestHook args (fixPkg pkg target) lbi hooks flags
  
  -- -----
  -- Main
  -- -----
  main = defaultMainWithHooks $ autoconfUserHooks
    { postConf = cogentConfigure
    -- TODO:
    -- , postBuild = \_ flags pkg lbi -> do
    --     copyGumHdrs 
    --     buildManPage 
    , postCopy = \_ flags pkg lbi -> do
        copyGumHdrs (S.fromFlag $ S.copyVerbosity flags) pkg lbi (S.fromFlag $ S.copyDest flags)
        installManPage (S.fromFlag $ S.copyVerbosity flags) pkg lbi (S.fromFlag $ S.copyDest flags)
    -- No idea why `postInst' is needed, as it doesn't seem to execute.
    -- Looking at other packages, they mostly do the same.
    -- Googled for quite a while and found some questions but no answers. / zilinc
    , postInst = \_ flags pkg lbi -> do
        installManPage (S.fromFlag $ S.installVerbosity flags) pkg lbi NoCopyDest
  #if __GLASGOW_HASKELL__ < 710
    , testHook = cogentTestHook ()
  #else
    , testHook = cogentTestHook
  #endif
    }
  