{-#LANGUAGE OverloadedStrings #-}
{-#LANGUAGE ScopedTypeVariables #-}
module Data.EmbedVersion
where

import Distribution.Version
import Distribution.Verbosity
import Distribution.Package
import Distribution.PackageDescription
import Distribution.PackageDescription.Parsec
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import Data.List (intercalate, isPrefixOf)
import System.Process
import System.Exit
import qualified Data.Text as Text
import Data.Text (Text)
import Data.Char (isSpace)
import Control.Exception


getPackageVersion :: FilePath -> IO Version
getPackageVersion cabalFileName = do
    gpd <- readGenericPackageDescription silent cabalFileName
    return . pkgVersion . package. packageDescription $ gpd

getPackageVersionStr :: FilePath -> IO String
getPackageVersionStr = fmap formatPackageVersion . getPackageVersion

runGit :: [String] -> IO String
runGit args = do
    (exitCode, stdout, stderr) <- readProcessWithExitCode "git" args ""
    case exitCode of
        ExitSuccess -> return stdout
        e -> fail $ "git:" ++ stderr

data GitVersion = GitTag String String
                | GitCommit String
                | Unversioned

getGitVersionStr :: IO GitVersion
getGitVersionStr = do
    currentCommit <- runGit ["log", "-1", "--pretty=tformat:%H %d"]
    let (commitHash, refInfo) = break isSpace currentCommit
    let refStrs = map Text.strip
                . Text.splitOn ","
                . Text.dropAround (`elem` ("()" :: [Char]))
                . Text.strip
                . Text.pack
                $ refInfo
    return $ case filter ("tag: " `Text.isPrefixOf`) refStrs of
            (tagRef:_) -> GitTag (Text.unpack $ Text.drop 5 tagRef) commitHash
            [] -> GitCommit commitHash

haveGitModifications :: IO Bool
haveGitModifications = do
    output <- runGit ["status", "--porcelain"]
    let outLines = filter (not . null) . lines $ output
    return . not . null $ outLines

formatPackageVersion :: Version -> String
formatPackageVersion =
    intercalate "." . map show . versionNumbers

embedPackageVersionStr :: FilePath -> Q Exp
embedPackageVersionStr fp = do
    gitVersion <- runIO $ getGitVersionStr `catch` \(err :: IOError) -> return Unversioned
    cabalVersion <- runIO (getPackageVersionStr fp)

    let version = case gitVersion of
            GitCommit c -> cabalVersion ++ "+git-" ++ c
            GitTag t c ->
                if (cabalVersion ++ "-") `isPrefixOf` t || cabalVersion == t
                    then t
                    else cabalVersion ++ " (" ++ t ++ ")"
            Unversioned -> cabalVersion
    modificationsStr <- runIO $ do
        modifications <- haveGitModifications `catch` \(err :: IOError) -> return False
        if modifications
            then return "+modifications"
            else return ""
    stringE $ version ++ modificationsStr
