content: {-# LANGUAGE DeriveGeneric #-}
  {-# LANGUAGE StandaloneDeriving #-}
  
  module Codex.Internal where
  
  import Data.Char (isSpace)
  import Data.Yaml
  import Data.Maybe (mapMaybe)
  import Data.Version (versionBranch, Version, parseVersion)
  import Distribution.Package
  import Distribution.Text
  import GHC.Generics
  import System.FilePath
  import System.Process (shell, readCreateProcess)
  import Text.ParserCombinators.ReadP (readP_to_S)
  
  import qualified Data.List as L
  
  defaultStackOpts :: FilePath
  defaultStackOpts = ""
  
  defaultTagsFileName :: FilePath
  defaultTagsFileName = "codex.tags"
  
  data Builder = Cabal | Stack String
  
  data Codex = Codex
    { currentProjectIncluded :: Bool
    , hackagePath :: FilePath
    , stackOpts :: String
    , tagsCmd :: String
    , tagsFileHeader :: Bool
    , tagsFileSorted :: Bool
    , tagsFileName :: FilePath }
      deriving Show
  
  deriving instance Generic Codex
  instance ToJSON Codex
  instance FromJSON Codex
  
  hackagePathOf :: Builder -> Codex -> FilePath
  hackagePathOf Cabal     cx = hackagePath cx
  hackagePathOf (Stack _) cx = hackagePath cx </> "packages"
  
  packagePath :: FilePath -> PackageIdentifier -> FilePath
  packagePath root i = root </> relativePath i where
    relativePath _ = name </> version where
      name = display $ pkgName i
      version = display $ pkgVersion i
  
  packageArchive :: FilePath -> PackageIdentifier -> FilePath
  packageArchive root i = packagePath root i </> name where
    name = concat [display $ pkgName i, "-", display $ pkgVersion i, ".tar.gz"]
  
  packageSources :: FilePath -> PackageIdentifier -> FilePath
  packageSources root i = packagePath root i </> name where
    name = concat [display $ pkgName i, "-", display $ pkgVersion i]
  
  packageTags :: FilePath -> PackageIdentifier -> FilePath
  packageTags root i = packagePath root i </> "tags"
  
  packageUrl :: PackageIdentifier -> String
  packageUrl i = concat ["http://hackage.haskell.org/package/", path] where
    path = concat [name, "/", name, ".tar.gz"]
    name = concat [display $ pkgName i, "-", display $ pkgVersion i]
  
  removePrefix :: String -> String -> Maybe String
  removePrefix prefix str =
    if prefix `L.isPrefixOf` trim str
      then Just $ trim $ L.drop (length prefix) $ trim str
      else Nothing
   where
    trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace
  
  readStackPath :: String -> String -> IO String
  readStackPath opts id' = do
    let cmd = concat ["stack ", opts, " path --", id']
    s <- readCreateProcess (shell cmd) ""
    return $ init s
  
  stackListDependencies :: String -> String -> IO [PackageIdentifier]
  stackListDependencies opts pname = do
    version <- readStackVersion
    let
      cmd =
        case versionBranch version of
          a : b : _
            | a <= 1
            , b <= 7
            -> concat ["stack ", opts, " list-dependencies", pname]
          _
            -> concat ["stack ", opts, " ls dependencies ", pname]
    s <- readCreateProcess (shell cmd) ""
    return $ mapMaybe parsePackageIdentifier $ lines s
    where
      parsePackageIdentifier line =
        let line' = map (\c -> if c == ' ' then '-' else c)
                        line
         in  simpleParse line'
  
  readStackVersion :: IO Version
  readStackVersion = do
    s <- readCreateProcess (shell "stack --version") ""
    let
      versionText =
        if ',' `elem` s then
        takeWhile (/= ',') (drop (length "Version ") s)
        else takeWhile (/= ' ') s
      parsed =
        readP_to_S parseVersion versionText
    case parsed of
      (v, _) : _ ->
        pure v
      _ ->
        error $ "Failed to parse stack version. Output: " ++ s
  