module Pipes.Conf where
import           Control.Applicative
import           Control.Monad
import           Control.Monad.M
import           Control.Monad.Trans.Either
import qualified Data.List as L
import           Data.Maybe
import           Data.String.Util
import           Distribution.InstalledPackageInfo as DIP
import qualified Filesystem.Path.CurrentOS as P
import qualified Module as Ghc
import           Package.Conf
import qualified PackageConfig as Ghc
import           Pipes
import qualified System.Directory as D

checkPath :: Bool -> P.FilePath -> String -> IO (Maybe String) 
checkPath dir path name = do 
  exists <- predicate . P.encodeString $ path 
  return $ if exists then Nothing else Just $ "missing: " ++ name ++ ' ':msg'
  where 
    (predicate, msg') =
      if dir then (D.doesDirectoryExist, "dir") else (D.doesFileExist, "file")

confError :: FilePath -> String -> M r
confError path fatal = 
  err $ preposition "parse errors" "in" "conf file" path [fatal] 
    
fromParseResults :: FilePath -> ParseResult InstalledPackageInfo -> M Conf
fromParseResults conf (ParseFailed _) = 
  confError conf "package conf file parse failed"
fromParseResults conf (ParseOk cabalWarnings fields)
  | null (haddockHTMLs fields)      =
    confError conf "failed to extract html field from conf file"
  | null (haddockInterfaces fields) = 
    confError conf "failed to extract haddock interface field from conf file"
  | otherwise = do
    unless (L.null cabalWarnings) $
      liftIO . putStr $
        "parse of:\n " ++ conf ++ "\n completed with warnings: " 
        ++ L.intercalate "\n" (map show cabalWarnings) 
    lift . right $
      Conf 
        -- We're not using Cabal's type information beyond just
        -- extracting package data. Ghc types are used for the rest.
        (Ghc.mkPackageId . sourcePackageId $ fields)
        -- TODO Respect multiple interfaces, however 
        -- this is not the common consensus for use of haddock interfaces. 
        (P.decodeString (head $ haddockInterfaces fields))
        (P.decodeString $ head (haddockHTMLs fields) ++ "/")
        (DIP.exposed fields)

diagnosePaths :: Conf -> M [String]
diagnosePaths conf = do
  (html_doc_warnings, interface_warnings) <- liftIO $  
    liftM2 (,)
      (checkPath True (htmlDir conf) "html doc")
      (checkPath False (interfaceFile conf) "haddock interface")
  return $ catMaybes [interface_warnings, html_doc_warnings] 

pipe_Conf :: PipeM FilePath Conf ()
pipe_Conf = forever $ do
  pkg_db_conf <- await
  -- Retrieve the package conf file from the package db
  parse_results <- liftIO $ DIP.parseInstalledPackageInfo <$> Prelude.readFile pkg_db_conf
  c <- lift $ fromParseResults pkg_db_conf parse_results

  errors <- lift $ diagnosePaths c
  if L.null errors then
    yield c
  else 
    lift $
      do msg "\n"
         warning $ "failed to process: " ++ Ghc.packageIdString (pkg c)
         warning $ preposition "path errors" "in" "pkg conf file" pkg_db_conf errors
         msg "\n" 
