module Distribution.Simple.JHC (
        configure, getInstalledPackages,
        buildLib, buildExe,
        installLib, installExe
 ) where
import Distribution.PackageDescription as PD
       ( PackageDescription(..), BuildInfo(..), Executable(..)
       , Library(..), libModules, hcOptions, usedExtensions )
import Distribution.InstalledPackageInfo
         ( emptyInstalledPackageInfo, )
import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo
import Distribution.Simple.PackageIndex (PackageIndex)
import qualified Distribution.Simple.PackageIndex as PackageIndex
import Distribution.Simple.LocalBuildInfo
         ( LocalBuildInfo(..), ComponentLocalBuildInfo(..) )
import Distribution.Simple.BuildPaths
                                ( autogenModulesDir, exeExtension )
import Distribution.Simple.Compiler
         ( CompilerFlavor(..), CompilerId(..), Compiler(..)
         , PackageDBStack, Flag, languageToFlags, extensionsToFlags )
import Language.Haskell.Extension
         ( Language(Haskell98), Extension(..), KnownExtension(..))
import Distribution.Simple.Program
         ( ConfiguredProgram(..), jhcProgram, ProgramConfiguration
         , userMaybeSpecifyPath, requireProgramVersion, lookupProgram
         , rawSystemProgram, rawSystemProgramStdoutConf )
import Distribution.Version
         ( Version(..), orLaterVersion )
import Distribution.Package
         ( Package(..), InstalledPackageId(InstalledPackageId),
           pkgName, pkgVersion, )
import Distribution.Simple.Utils
        ( createDirectoryIfMissingVerbose, writeFileAtomic
        , installOrdinaryFile, installExecutableFile
        , intercalate )
import System.FilePath          ( (</>) )
import Distribution.Verbosity
import Distribution.Text
         ( Text(parse), display )
import Distribution.Compat.ReadP
    ( readP_to_S, string, skipSpaces )
import Distribution.System ( Platform )
import Data.List                ( nub )
import Data.Char                ( isSpace )
import Data.Maybe               ( fromMaybe )
import qualified Data.ByteString.Lazy.Char8 as BS.Char8
configure :: Verbosity -> Maybe FilePath -> Maybe FilePath
          -> ProgramConfiguration -> IO (Compiler, Maybe Platform, ProgramConfiguration)
configure verbosity hcPath _hcPkgPath conf = do
  (jhcProg, _, conf') <- requireProgramVersion verbosity
                           jhcProgram (orLaterVersion (Version [0,7,2] []))
                           (userMaybeSpecifyPath "jhc" hcPath conf)
  let Just version = programVersion jhcProg
      comp = Compiler {
        compilerId             = CompilerId JHC version,
        compilerLanguages      = jhcLanguages,
        compilerExtensions     = jhcLanguageExtensions
      }
      compPlatform = Nothing
  return (comp, compPlatform, conf')
jhcLanguages :: [(Language, Flag)]
jhcLanguages = [(Haskell98, "")]
jhcLanguageExtensions :: [(Extension, Flag)]
jhcLanguageExtensions =
    [(EnableExtension  TypeSynonymInstances       , "")
    ,(DisableExtension TypeSynonymInstances       , "")
    ,(EnableExtension  ForeignFunctionInterface   , "")
    ,(DisableExtension ForeignFunctionInterface   , "")
    ,(EnableExtension  ImplicitPrelude            , "") 
    ,(DisableExtension ImplicitPrelude            , "--noprelude")
    ,(EnableExtension  CPP                        , "-fcpp")
    ,(DisableExtension CPP                        , "-fno-cpp")
    ]
getInstalledPackages :: Verbosity -> PackageDBStack -> ProgramConfiguration
                    -> IO PackageIndex
getInstalledPackages verbosity _packageDBs conf = do
   
   
   
   str <- rawSystemProgramStdoutConf verbosity jhcProgram conf ["--list-libraries"]
   let pCheck :: [(a, String)] -> [a]
       pCheck rs = [ r | (r,s) <- rs, all isSpace s ]
   let parseLine ln =
          pCheck (readP_to_S
             (skipSpaces >> string "Name:" >> skipSpaces >> parse) ln)
   return $
      PackageIndex.fromList $
      map (\p -> emptyInstalledPackageInfo {
                    InstalledPackageInfo.installedPackageId =
                       InstalledPackageId (display p),
                    InstalledPackageInfo.sourcePackageId = p
                 }) $
      concatMap parseLine $
      lines str
buildLib :: Verbosity -> PackageDescription -> LocalBuildInfo
                      -> Library            -> ComponentLocalBuildInfo -> IO ()
buildLib verbosity pkg_descr lbi lib clbi = do
  let Just jhcProg = lookupProgram jhcProgram (withPrograms lbi)
  let libBi = libBuildInfo lib
  let args  = constructJHCCmdLine lbi libBi clbi (buildDir lbi) verbosity
  let pkgid = display (packageId pkg_descr)
      pfile = buildDir lbi </> "jhc-pkg.conf"
      hlfile= buildDir lbi </> (pkgid ++ ".hl")
  writeFileAtomic pfile . BS.Char8.pack $ jhcPkgConf pkg_descr
  rawSystemProgram verbosity jhcProg $
     ["--build-hl="++pfile, "-o", hlfile] ++
     args ++ map display (libModules lib)
buildExe :: Verbosity -> PackageDescription -> LocalBuildInfo
                      -> Executable         -> ComponentLocalBuildInfo -> IO ()
buildExe verbosity _pkg_descr lbi exe clbi = do
  let Just jhcProg = lookupProgram jhcProgram (withPrograms lbi)
  let exeBi = buildInfo exe
  let out   = buildDir lbi </> exeName exe
  let args  = constructJHCCmdLine lbi exeBi clbi (buildDir lbi) verbosity
  rawSystemProgram verbosity jhcProg (["-o",out] ++ args ++ [modulePath exe])
constructJHCCmdLine :: LocalBuildInfo -> BuildInfo -> ComponentLocalBuildInfo
                    -> FilePath -> Verbosity -> [String]
constructJHCCmdLine lbi bi clbi _odir verbosity =
        (if verbosity >= deafening then ["-v"] else [])
     ++ hcOptions JHC bi
     ++ languageToFlags (compiler lbi) (defaultLanguage bi)
     ++ extensionsToFlags (compiler lbi) (usedExtensions bi)
     ++ ["--noauto","-i-"]
     ++ concat [["-i", l] | l <- nub (hsSourceDirs bi)]
     ++ ["-i", autogenModulesDir lbi]
     ++ ["-optc" ++ opt | opt <- PD.ccOptions bi]
     
     
     
     ++ (concat [ ["-p", display (pkgName pkgid)]
                | (_, pkgid) <- componentPackageDeps clbi ])
jhcPkgConf :: PackageDescription -> String
jhcPkgConf pd =
  let sline name sel = name ++ ": "++sel pd
      lib = fromMaybe (error "no library available") . library
      comma = intercalate "," . map display
  in unlines [sline "name" (display . pkgName . packageId)
             ,sline "version" (display . pkgVersion . packageId)
             ,sline "exposed-modules" (comma . PD.exposedModules . lib)
             ,sline "hidden-modules" (comma . otherModules . libBuildInfo . lib)
             ]
installLib :: Verbosity -> FilePath -> FilePath -> PackageDescription -> Library -> IO ()
installLib verb dest build_dir pkg_descr _ = do
    let p = display (packageId pkg_descr)++".hl"
    createDirectoryIfMissingVerbose verb True dest
    installOrdinaryFile verb (build_dir </> p) (dest </> p)
installExe :: Verbosity -> FilePath -> FilePath -> (FilePath,FilePath) -> PackageDescription -> Executable -> IO ()
installExe verb dest build_dir (progprefix,progsuffix) _ exe = do
    let exe_name = exeName exe
        src = exe_name </> exeExtension
        out   = (progprefix ++ exe_name ++ progsuffix) </> exeExtension
    createDirectoryIfMissingVerbose verb True dest
    installExecutableFile verb (build_dir </> src) (dest </> out)