{-# LANGUAGE DeriveAnyClass #-}
module Pier.Build.CFlags
    ( TransitiveDeps(..)
    , CFlags(..)
    , getCFlags
    , ghcDefines
    ) where

import Control.Applicative (liftA2)
import Control.Monad (guard)
import Data.Semigroup
import Data.Set (Set)
import Development.Shake
import Development.Shake.Classes
import Distribution.PackageDescription
import Distribution.System (buildOS, OS(OSX))
import Distribution.Text (display)
import Distribution.Types.PkgconfigDependency
import Distribution.Version (versionNumbers)
import GHC.Generics (Generic(..))

import qualified Data.Set as Set

import Pier.Build.Stackage
import Pier.Core.Artifact

data TransitiveDeps = TransitiveDeps
    { transitiveDBs :: Set Artifact
    , transitiveLibFiles :: Set Artifact
    , transitiveIncludeDirs :: Set Artifact
    , transitiveDataFiles :: Set Artifact
    } deriving (Show, Eq, Typeable, Generic, Hashable, Binary, NFData)

instance Semigroup TransitiveDeps

instance Monoid TransitiveDeps where
    mempty = TransitiveDeps Set.empty Set.empty Set.empty Set.empty
    TransitiveDeps dbs files is datas
        `mappend` TransitiveDeps dbs' files' is' datas'
        = TransitiveDeps (dbs <> dbs') (files <> files') (is <> is')
                (datas <> datas')

-- TODO: macros file also
data CFlags = CFlags
    { ccFlags :: [String]
    , cppFlags :: [String]
    , cIncludeDirs :: Set Artifact
    , linkFlags :: [String]
    , linkLibs :: [String]
    , macFrameworks :: [String]
    }

-- TODO: include macros file too
getCFlags :: TransitiveDeps -> Artifact -> BuildInfo -> Action CFlags
getCFlags deps pkgDir bi = do
    pkgConfFlags <- mconcat <$> mapM getPkgConfFlags (pkgconfigDepends bi)
    return CFlags
            { ccFlags = ccOptions bi ++ fst pkgConfFlags
            , cppFlags = cppOptions bi
            , cIncludeDirs =
                    Set.fromList (map (pkgDir />) $ includeDirs bi)
                    <> transitiveIncludeDirs deps
            , linkFlags = ldOptions bi ++ snd pkgConfFlags
            , linkLibs = extraLibs bi
            , macFrameworks = guard (buildOS == OSX)
                                >> frameworks bi
            }

-- TODO: handle version numbers too
getPkgConfFlags :: PkgconfigDependency -> Action ([String], [String])
getPkgConfFlags (PkgconfigDependency name _) = liftA2 (,)
    (runPkgConfig [display name, "--cflags"])
    (runPkgConfig [display name, "--libs"])
  where
    runPkgConfig = fmap words . runCommandStdout . prog "pkg-config"


-- | Definitions that GHC provides by default
ghcDefines :: InstalledGhc -> [String]
ghcDefines ghc = ["-D__GLASGOW_HASKELL__=" ++
                    cppVersion (ghcInstalledVersion ghc)]
  where
    cppVersion v = case versionNumbers v of
        (v1:v2:_) -> show v1 ++ if v2 < 10 then '0':show v2 else show v2
        _ -> error $ "cppVersion: " ++ display v