content: {- TooManyCells.Program.Utility
  Gregory W. Schwartz
  
  Utility functions for the command line program.
  -}
  
  {-# LANGUAGE BangPatterns      #-}
  
  module TooManyCells.Program.Utility where
  
  -- Remote
  import Control.Exception (SomeException (..), try, evaluate)
  import Control.Monad (guard)
  import Control.Monad.Trans.Maybe (runMaybeT, MaybeT (..))
  import Data.Bool (bool)
  import Data.List (isInfixOf)
  import Data.Maybe (fromMaybe)
  import Text.Read (readMaybe)
  import qualified System.Directory as FP
  import qualified System.FilePath as FP
  import qualified Turtle as TU
  
  -- Local
  import TooManyCells.File.Types
  import TooManyCells.Matrix.Types
  import TooManyCells.Program.Options
  
  -- | Read or return an error.
  readOrErr :: (Read a) => String -> String -> a
  readOrErr err = fromMaybe (error err) . readMaybe
  
  -- | Normalization defaults.
  getNormalization :: Subcommand -> [NormType] -> [NormType]
  getNormalization (MakeTreeCommand opts) xs =
    if null xs then [TfIdfNorm] else xs
  getNormalization (InteractiveCommand opts) xs =
    if null xs then [TfIdfNorm] else xs
  getNormalization _ xs = if null xs then [NoneNorm] else xs
  
  -- | Get the file type of an input matrix. Returns either a left file (i.e. CSV)
  -- or a right matrix market.
  getMatrixFileType :: FilePath -> IO (Either MatrixFileType MatrixFileType)
  getMatrixFileType path = do
    fileExist      <- FP.doesFileExist path
    directoryExist <- FP.doesDirectoryExist path
    compressedFileExist <- FP.doesFileExist $ path FP.</> "matrix.mtx.gz"
  
    let fragmentsFile = (\ (x, y) -> isInfixOf "fragments" x && y == ".tsv.gz")
                      . FP.splitExtensions
                      . FP.takeFileName
                      $ path
        matrixFile
          | fileExist && (FP.takeExtension path == ".bdg") = Left . BedGraph $ path
          | fileExist && (FP.takeExtension path == ".bw") = Left . BigWig $ path
          | fileExist && not fragmentsFile = Left . DecompressedMatrix . MatrixFile $ path
          | fileExist && fragmentsFile = Left . CompressedFragments . FragmentsFile $ path
          | directoryExist && not compressedFileExist = Right . DecompressedMatrix . MatrixFile $ path FP.</> "matrix.mtx"
          | directoryExist && compressedFileExist = Right . CompressedMatrix . MatrixFile $ path FP.</> "matrix.mtx.gz"
          | directoryExist = error "Cannot determine matrix pointed to, are there too many matrices here?"
          | otherwise = error "\nMatrix path does not exist."
  
    return matrixFile
  
  -- | Check if file exists and is not empty. Currently may have a handle closing
  -- issue, do not use for now.
  nonEmptyFileExists :: TU.FilePath -> IO Bool
  nonEmptyFileExists path = either (const' False) (/= TU.B 0)
                        <$> (try $ TU.du path :: IO (Either SomeException TU.Size))
    where
      const' !x !y = x
  