module HsDev.Inspect.Order (
        orderBy, order
        ) where

import Control.Lens
import Data.Maybe
import Data.String
import qualified Data.Map as M
import qualified Data.Set as S
import qualified Language.Haskell.Exts as H

import Data.Deps
import HsDev.Inspect
import HsDev.Symbols.Types
import System.Directory.Paths

-- | Order source files so that dependencies goes first and we are able to resolve symbols and set fixities
orderBy :: (a -> Maybe Preloaded) -> [a] -> Either (DepsError Path) [a]
orderBy fn ps = do
        order' <- linearize pdeps
        return $ mapMaybe (`M.lookup` pm) order'
        where
                pdeps = mconcat $ mapMaybe (fmap getDeps . fn) ps
                pm = M.fromList [(pfile, p) | p <- ps, pfile <- fn p ^.. _Just . preloadedId . moduleLocation . moduleFile]
                files = S.fromList $ map fn ps ^.. each . _Just . preloadedId . moduleLocation . moduleFile
                getDeps :: Preloaded -> Deps Path
                getDeps p = deps mfile [ifile | ifile <- ifiles, S.member ifile files] where
                        H.Module _ _ _ idecls _ = _preloadedModule p
                        imods = [fromString iname | H.ModuleName _ iname <- map H.importModule idecls]
                        mfile = _preloadedId p ^?! moduleLocation . moduleFile
                        projRoot = _preloadedId p ^? moduleLocation . moduleProject . _Just . projectPath
                        mroot = fromMaybe
                                (sourceModuleRoot (view moduleName $ _preloadedId p) mfile)
                                projRoot
                        dirs = do
                                proj <- _preloadedId p ^.. moduleLocation . moduleProject . _Just
                                i <- fileTargets proj mfile
                                view infoSourceDirs i
                        ifiles = [normPath (joinPaths [mroot, dir, importPath imod]) | imod <- imods, dir <- if null dirs then [fromFilePath "."] else dirs]

order :: [Preloaded] -> Either (DepsError Path) [Preloaded]
order = orderBy Just