-- | Returns the new InstalledMap and all of the locally registered packages.
getInstalled :: (M env m, PackageInstallInfo pii)
             => EnvOverride
             -> GetInstalledOpts
             -> Map PackageName pii -- ^ does not contain any installed information
             -> m ( InstalledMap
                  , [DumpPackage () ()] -- globally installed
                  , Map GhcPkgId PackageIdentifier -- locally installed
                  )
getInstalled menv opts sourceMap = do
    snapDBPath <- packageDatabaseDeps
    localDBPath <- packageDatabaseLocal
    extraDBPaths <- packageDatabaseExtra

    bconfig <- asks getBuildConfig

    mcache <-
        if getInstalledProfiling opts || getInstalledHaddock opts
            then liftM Just $ loadInstalledCache $ configInstalledCache bconfig
            else return Nothing

    let loadDatabase' = loadDatabase menv opts mcache sourceMap

    (installedLibs0, globalInstalled) <- loadDatabase' Nothing []
    (installedLibs1, _extraInstalled) <-
      (foldM (\lhs' pkgdb -> do
        lhs'' <- loadDatabase' (Just (ExtraGlobal, pkgdb)) (fst lhs')
        return lhs'') ((installedLibs0, globalInstalled)) extraDBPaths)
    (installedLibs2, _snapInstalled) <-
        loadDatabase' (Just (InstalledTo Snap, snapDBPath)) installedLibs1
    (installedLibs3, localInstalled) <-
        loadDatabase' (Just (InstalledTo Local, localDBPath)) installedLibs2
    let installedLibs = M.fromList $ map lhPair installedLibs3

    case mcache of
        Nothing -> return ()
        Just pcache -> saveInstalledCache (configInstalledCache bconfig) pcache

    -- Add in the executables that are installed, making sure to only trust a
    -- listed installation under the right circumstances (see below)
    let exesToSM loc = Map.unions . map (exeToSM loc)
        exeToSM loc (PackageIdentifier name version) =
            case Map.lookup name sourceMap of
                -- Doesn't conflict with anything, so that's OK
                Nothing -> m
                Just pii
                    -- Not the version we want, ignore it
                    | version /= piiVersion pii || loc /= piiLocation pii -> Map.empty

                    | otherwise -> m
          where
            m = Map.singleton name (version, loc, Executable $ PackageIdentifier name version)
    exesSnap <- getInstalledExes Snap
    exesLocal <- getInstalledExes Local
    let installedMap = Map.unions
            [ exesToSM Local exesLocal
            , exesToSM Snap exesSnap
            , installedLibs
            ]

    return ( installedMap
           , globalInstalled
           , Map.fromList $ map (dpGhcPkgId &&& dpPackageIdent) localInstalled
           )

-- | Outputs both the modified InstalledMap and the Set of all installed packages in this database
--
-- The goal is to ascertain that the dependencies for a package are present,
-- that it has profiling if necessary, and that it matches the version and
-- location needed by the SourceMap
getInstalled :: (M env m, PackageInstallInfo pii)
             => EnvOverride
             -> GetInstalledOpts
             -> Map PackageName pii -- ^ does not contain any installed information
             -> m ( InstalledMap
                  , [DumpPackage () ()] -- globally installed
                  , Map GhcPkgId PackageIdentifier -- locally installed
                  )
getInstalled menv opts sourceMap = do
    snapDBPath <- packageDatabaseDeps
    localDBPath <- packageDatabaseLocal
    extraDBPaths <- packageDatabaseExtra

    bconfig <- asks getBuildConfig

    mcache <-
        if getInstalledProfiling opts || getInstalledHaddock opts
            then liftM Just $ loadInstalledCache $ configInstalledCache bconfig
            else return Nothing

    let loadDatabase' = loadDatabase menv opts mcache sourceMap

    (installedLibs0, globalInstalled) <- loadDatabase' Nothing []
    (installedLibs1, _extraInstalled) <-
      (foldM (\lhs' pkgdb -> do
        lhs'' <- loadDatabase' (Just (ExtraGlobal, pkgdb)) (fst lhs')
        return lhs'') ((installedLibs0, globalInstalled)) extraDBPaths)
    (installedLibs2, _snapInstalled) <-
        loadDatabase' (Just (InstalledTo Snap, snapDBPath)) installedLibs1
    (installedLibs3, localInstalled) <-
        loadDatabase' (Just (InstalledTo Local, localDBPath)) installedLibs2
    let installedLibs = M.fromList $ map lhPair installedLibs3

    case mcache of
        Nothing -> return ()
        Just pcache -> saveInstalledCache (configInstalledCache bconfig) pcache

    -- Add in the executables that are installed, making sure to only trust a
    -- listed installation under the right circumstances (see below)
    let exesToSM loc = Map.unions . map (exeToSM loc)
        exeToSM loc (PackageIdentifier name version) =
            case Map.lookup name sourceMap of
                -- Doesn't conflict with anything, so that's OK
                Nothing -> m
                Just pii
                    -- Not the version we want, ignore it
                    | version /= piiVersion pii || loc /= piiLocation pii -> Map.empty

                    | otherwise -> m
          where
            m = Map.singleton name (version, loc, Executable $ PackageIdentifier name version)
    exesSnap <- getInstalledExes Snap
    exesLocal <- getInstalledExes Local
    let installedMap = Map.unions
            [ exesToSM Local exesLocal
            , exesToSM Snap exesSnap
            , installedLibs
            ]

    return ( installedMap
           , globalInstalled
           , Map.fromList $ map (dpGhcPkgId &&& dpPackageIdent) localInstalled
           )

-- | Outputs both the modified InstalledMap and the Set of all installed packages in this database
--
-- The goal is to ascertain that the dependencies for a package are present,
-- that it has profiling if necessary, and that it matches the version and
-- location needed by the SourceMap
getInstalled menv opts sourceMap = do
    snapDBPath <- packageDatabaseDeps
    localDBPath <- packageDatabaseLocal
    extraDBPaths <- packageDatabaseExtra

    bconfig <- asks getBuildConfig

    mcache <-
        if getInstalledProfiling opts || getInstalledHaddock opts
            then liftM Just $ loadInstalledCache $ configInstalledCache bconfig
            else return Nothing

    let loadDatabase' = loadDatabase menv opts mcache sourceMap

    (installedLibs0, globalInstalled) <- loadDatabase' Nothing []
    (installedLibs1, _extraInstalled) <-
      (foldM (\lhs' pkgdb -> do
        lhs'' <- loadDatabase' (Just (ExtraGlobal, pkgdb)) (fst lhs')
        return lhs'') ((installedLibs0, globalInstalled)) extraDBPaths)
    (installedLibs2, _snapInstalled) <-
        loadDatabase' (Just (InstalledTo Snap, snapDBPath)) installedLibs1
    (installedLibs3, localInstalled) <-
        loadDatabase' (Just (InstalledTo Local, localDBPath)) installedLibs2
    let installedLibs = M.fromList $ map lhPair installedLibs3

    case mcache of
        Nothing -> return ()
        Just pcache -> saveInstalledCache (configInstalledCache bconfig) pcache

    -- Add in the executables that are installed, making sure to only trust a
    -- listed installation under the right circumstances (see below)
    let exesToSM loc = Map.unions . map (exeToSM loc)
        exeToSM loc (PackageIdentifier name version) =
            case Map.lookup name sourceMap of
                -- Doesn't conflict with anything, so that's OK
                Nothing -> m
                Just pii
                    -- Not the version we want, ignore it
                    | version /= piiVersion pii || loc /= piiLocation pii -> Map.empty

                    | otherwise -> m
          where
            m = Map.singleton name (version, loc, Executable $ PackageIdentifier name version)
    exesSnap <- getInstalledExes Snap
    exesLocal <- getInstalledExes Local
    let installedMap = Map.unions
            [ exesToSM Local exesLocal
            , exesToSM Snap exesSnap
            , installedLibs
            ]

    return ( installedMap
           , globalInstalled
           , Map.fromList $ map (dpGhcPkgId &&& dpPackageIdent) localInstalled
           )

-- | Outputs both the modified InstalledMap and the Set of all installed packages in this database
--
-- The goal is to ascertain that the dependencies for a package are present,
-- that it has profiling if necessary, and that it matches the version and
-- location needed by the SourceMap