{-# OPTIONS -cpp #-}

module Scion.Hoogle.Util
( findHoogleBinPath
) where

import Data.List (find)
import Distribution.InstalledPackageInfo
import Distribution.Package
import Scion.Packages
import System.FilePath
import System.Directory (doesFileExist)

#ifdef mingw32_HOST_OS
import System.Directory (getAppUserDataDirectory)
#endif

-- Functions for finding Hoogle in the system

findHoogleBinPath :: IO (Maybe String)
findHoogleBinPath = findPathsAndCheck placesToSearch
                    where placesToSearch = [ findHoogleBinInLibrary getHoogleBinPath1
                                           , findHoogleBinInLibrary getHoogleBinPath2
#ifdef mingw32_HOST_OS
                                           , getHoogleBinPath3
#endif
                                           ]

findPathsAndCheck :: [IO (Maybe String)] -> IO (Maybe String)
findPathsAndCheck []     = return Nothing
findPathsAndCheck (f:fs) = do r <- findPathAndCheck f
                              case r of
                                Nothing -> findPathsAndCheck fs
                                _       -> return r

findPathAndCheck :: IO (Maybe String) -> IO (Maybe String)
findPathAndCheck f = do p <- f
                        case p of
                          Nothing   -> return Nothing
                          Just path -> do exists <- doesFileExist path
                                          if exists
                                             then return (Just path)
                                             else return Nothing

findHoogleBinInLibrary :: (String -> String) -> IO (Maybe String)
findHoogleBinInLibrary f = do minfo <- findHoogleInfo
                              case minfo of
                                Nothing   -> return Nothing
                                Just info -> let [libDir] = libraryDirs info
                                             in  return $ Just (f libDir)

findHoogleInfo :: IO (Maybe InstalledPackageInfo)
findHoogleInfo = do infos' <- getPkgInfos
                    let infos = removeSmallVersions $ concat $ map snd infos'
                    return $ find (\m -> (pkgName (sourcePackageId m)) == PackageName "hoogle") infos

removeSmallVersions :: [InstalledPackageInfo] -> [InstalledPackageInfo]
removeSmallVersions pids = filter
  (not . (\InstalledPackageInfo { sourcePackageId = (PackageIdentifier name version) } -> 
             any (\InstalledPackageInfo { sourcePackageId = (PackageIdentifier name' version') } ->
                     name' == name && version' > version)
                 pids))
  pids

getHoogleBinPath1 :: String -> String
getHoogleBinPath1 path = let (_:(_:(_:rest))) = reverse $ splitDirectories path
                         in  (joinPath $ reverse ("bin":rest)) </> "hoogle" <.> exeExtension

getHoogleBinPath2 :: String -> String
getHoogleBinPath2 path = let (_:(_:rest)) = reverse $ splitDirectories path
                         in  (joinPath $ reverse ("bin":rest)) </> "hoogle" <.> exeExtension

#ifdef mingw32_HOST_OS
getHoogleBinPath3 :: IO (Maybe String)
getHoogleBinPath3 = do cabalDir <- getAppUserDataDirectory "cabal"
                       return $ Just (cabalDir </> "bin" </> "hoogle" <.> exeExtension)
#endif

exeExtension :: String
#ifdef mingw32_HOST_OS
exeExtension = "exe"
#else
exeExtension = ""
#endif

-- This part is commented out because Cabal does not work
-- well linking with the Hoogle library.
-- Instead, `hoogle` is called directly and the results
-- are parsed and converted into database items.

{-
-- |Loads the Hoogle search database into memory.
--  If no database is found, an empty one is returned.
getHoogleDatabases :: IO H.Database
getHoogleDatabases = do path <- findHoogleDatabasesPath
                        case path of
                          Nothing -> return mempty
                          Just p  -> do files <- getDirectoryContents p
                                        hooFiles <- filterM (\f -> do exists <- doesFileExist f
                                                                      let isHoo = takeExtension f == ".hoo"
                                                                      return $ exists && isHoo)
                                                            files
                                        dbs <- mapM H.loadDatabase hooFiles
                                        return $ mconcat dbs

findHoogleDatabasesPath :: IO (Maybe String)
findHoogleDatabasesPath = do minfo <- findHoogleInfo
                             case minfo of
                               Nothing   -> return Nothing
                               Just info -> let [libDir] = libraryDirs info
                                            in  return $ Just (getDatabasesDir libDir)

getDatabasesDir :: String -> String
getDatabasesDir path = let (_:(hoogleV:(_:rest))) = reverse $ splitDirectories path
                       in  (joinPath $ reverse (hoogleV:("share":rest))) </> "databases"
-}