{-# LANGUAGE CPP #-}
module Jenga.Cabal
  ( dependencyName
  , readPackageDependencies
  ) where

-- You would thing that since the Cabal file exposes its cabal parser you would
-- think it would be a simple matter to extract the list of dependencies.
-- Unfortunately its much more work than it should be. See:
-- https://hackage.haskell.org/package/Cabal-1.24.2.0/docs/Distribution-PackageDescription.html#v:buildDepends

import qualified Data.Map.Strict as DM
import           Data.Text (Text)
import qualified Data.Text as T

import           Distribution.Package (Dependency (..), PackageIdentifier (..), unPackageName)
import           Distribution.PackageDescription
                    ( Benchmark, CondTree (..), ConfVar, Executable, GenericPackageDescription (..)
                    , PackageDescription (..), Library, TestSuite
                    )
#if MIN_VERSION_Cabal (2,0,0)
import           Distribution.PackageDescription.Parse (readGenericPackageDescription)
#else
import           Distribution.PackageDescription.Parse (readPackageDescription)
import           Distribution.Verbosity (Verbosity)
#endif
import           Distribution.Verbosity (normal)

readPackageDependencies :: FilePath -> IO [Dependency]
readPackageDependencies fpath = do
  genpkg <- readGenericPackageDescription normal fpath
  pure
    $ sortNubByName
    $ filterPackageName (package $ packageDescription genpkg)
    $ extractLibraryDeps (condLibrary genpkg)
        ++ extractExecutableDeps (condExecutables genpkg)
        ++ extractTestSuiteDeps (condTestSuites genpkg)
        ++ extractBenchmarkDeps (condBenchmarks genpkg)


sortNubByName :: [Dependency] -> [Dependency]
sortNubByName = fmap toDep . DM.toList . DM.fromList . fmap fromDep
  where
    fromDep (Dependency n v) = (n, v)
    toDep (n, v) = Dependency n v

filterPackageName :: PackageIdentifier -> [Dependency] -> [Dependency]
filterPackageName (PackageIdentifier pname _) =
  filter (\dep -> pname /= packageName dep )
  where
    packageName (Dependency pn _) = pn

dependencyName :: Dependency -> Text
dependencyName (Dependency name _) = T.pack $ unPackageName name


extractLibraryDeps :: Maybe (CondTree ConfVar [Dependency] Library) -> [Dependency]
extractLibraryDeps Nothing = []
extractLibraryDeps (Just x) = condTreeConstraints x

extractExecutableDeps :: [(a, CondTree ConfVar [Dependency] Executable)] -> [Dependency]
extractExecutableDeps = concatMap (condTreeConstraints . snd)

extractTestSuiteDeps :: [(a, CondTree ConfVar [Dependency] TestSuite)] -> [Dependency]
extractTestSuiteDeps = concatMap (condTreeConstraints . snd)

extractBenchmarkDeps :: [(a, CondTree ConfVar [Dependency] Benchmark)] -> [Dependency]
extractBenchmarkDeps = concatMap (condTreeConstraints . snd)

#if MIN_VERSION_Cabal (2,0,0)
#else
readGenericPackageDescription :: Verbosity -> FilePath -> IO GenericPackageDescription
readGenericPackageDescription = readPackageDescription
#endif