content: module Package.C.Build.Tree ( buildByName
                              ) where
  
  import           Control.Recursion
  import           CPkgPrelude
  import           Data.Containers.ListUtils (nubOrd)
  import           Data.List                 (isInfixOf)
  import           Package.C.Build
  import           Package.C.Monad
  import           Package.C.PackageSet
  import           Package.C.Type
  import           Package.C.Type.Tree
  import           System.Directory          (doesDirectoryExist)
  import           System.FilePath           ((</>))
  
  data BuildDirs = BuildDirs { libraries :: [FilePath]
                             , share     :: [FilePath]
                             , include   :: [FilePath]
                             , binaries  :: [FilePath]
                             }
  
  getAll :: [BuildDirs] -> BuildDirs
  getAll bds =
      let go f = concat (f <$> bds)
      in BuildDirs (go libraries) (go share) (go include) (go binaries)
  
  -- in order to prevent the "vanilla" libffi from preceding the *cross* libffi,
  -- we filter out any directory that doesn't contain the target triple. this
  -- causes further bugs and it's slow
  --
  -- Really we should allow *all* libdirs for Python/Perl here, since they won't
  -- (hopefully) pollute the pkg-config path...
  immoralFilter :: Maybe TargetTriple -> [FilePath] -> [FilePath]
  immoralFilter Nothing fps = fps
  immoralFilter (Just tgt') fps =
      let infixDir = show tgt'
      in filter (\fp -> infixDir `isInfixOf` fp || "meson" `isInfixOf` fp || "XML-Parser" `isInfixOf` fp || "python3" `isInfixOf` fp) fps -- FIXME: more principled approach
  
  -- filter out stuff from the path
  filterCross :: Maybe TargetTriple -> [FilePath] -> [FilePath]
  filterCross Nothing = id
  filterCross (Just tgt') =
      let infixDir = show tgt'
      in filter (\fp -> not (infixDir `isInfixOf` fp) || "ncurses" `isInfixOf` fp)
  
  buildWithContext :: DepTree CPkg
                   -> Maybe TargetTriple
                   -> Bool -- ^ Should we build static libraries?
                   -> Bool -- ^ Install globally
                   -> PkgM ()
  buildWithContext cTree host sta glob = zygoM' dirAlg buildAlg cTree
  
      where buildAlg :: DepTreeF CPkg (BuildDirs, ()) -> PkgM ()
            buildAlg (DepNodeF c usr preBds) =
              buildCPkg c host sta glob usr ds (immoralFilter host ls) is (filterCross host bs)
                  where (BuildDirs ls ds is bs) = getAll (fst <$> preBds)
            buildAlg (BldDepNodeF c preBds) =
              buildCPkg c Nothing False False False ds ls is bs -- don't use static libraries for build dependencies
              -- also don't install them globally
              -- build dependencies are not manual!
                  where (BuildDirs ls ds is bs) = getAll (fst <$> preBds)
  
            mkBuildDirs :: MonadIO m => FilePath -> BuildDirs -> m BuildDirs
            mkBuildDirs pkgDir (BuildDirs ls ds is bs) = do
              let linkDir = pkgDir </> "lib"
                  linkDir64 = pkgDir </> "lib64"
                  includeDir = pkgDir </> "include"
                  dataDir = pkgDir </> "share"
                  binDir = pkgDir </> "bin"
  
              binExists <- liftIO (doesDirectoryExist binDir)
              let bins = if binExists
                  then binDir : bs
                  else bs
  
              shareExists <- liftIO (doesDirectoryExist dataDir)
              let shares = if shareExists
                  then dataDir : ds
                  else ds
  
              linkExists <- liftIO (doesDirectoryExist linkDir)
              link64Exists <- liftIO (doesDirectoryExist linkDir64)
  
              let linkAppend = if linkExists
                  then (linkDir :)
                  else id
              let link64Append = if link64Exists
                  then (linkDir64 :)
                  else id
  
              let links = link64Append (linkAppend ls)
  
              includeExists <- liftIO (doesDirectoryExist includeDir)
              let includes = if includeExists
                  then includeDir : is
                  else is
  
              pure (BuildDirs (nubOrd links) (nubOrd shares) (nubOrd includes) (nubOrd bins))
  
            dirAlg :: DepTreeF CPkg BuildDirs -> PkgM BuildDirs
            dirAlg (DepNodeF c _ bds) = do
  
              let bldDirs@(BuildDirs ls ds is bs) = getAll bds
  
              buildVars <- getVars host sta ds (immoralFilter host ls) is (filterCross host bs)
  
              pkgDir <- cPkgToDir c host glob buildVars
  
              mkBuildDirs pkgDir bldDirs
  
            dirAlg (BldDepNodeF c bds) = do
  
              let bldDirs@(BuildDirs ls ds is bs) = getAll bds
  
              buildVars <- getVars Nothing False ds ls is bs
  
              pkgDir <- cPkgToDir c Nothing False buildVars
  
              mkBuildDirs pkgDir bldDirs
  
  -- TODO: should this parse a string into a TargetTriple instead?
  -- | Manually install a package
  buildByName :: PackId -> Maybe TargetTriple -> Maybe String -> Bool -> Bool -> PkgM ()
  buildByName pkId host pkSet sta glob = do
      allPkgs <- liftIO (pkgsM pkId pkSet)
      buildWithContext allPkgs host sta glob
  