{-# LANGUAGE OverloadedStrings #-}

module HaskellWorks.Data.Uri.IO.File
  ( dirname
  , listMaybeDirectory
  , listFilesRecursiveWithPrefix
  , listDirectory
  , listFilesRecursive
  ) where

import Control.Monad.Except
import Control.Monad.IO.Unlift
import Data.Semigroup          ((<>))
import System.FilePath

import qualified Data.List                           as L
import qualified HaskellWorks.Control.Monad.Lazy     as IO
import qualified HaskellWorks.Data.Uri.Internal.List as L
import qualified System.Directory                    as IO

dirname :: FilePath -> FilePath
dirname filePath = case reverse (L.splitBy (== '/') ("$" <> filePath)) of
  []  -> filePath
  [_] -> filePath
  bs  -> drop 1 (L.intercalate "/" (reverse (drop 1 bs)))

listMaybeDirectory :: MonadIO m => FilePath -> m [FilePath]
listMaybeDirectory filepath = do
  exists <- liftIO $ IO.doesDirectoryExist filepath
  if exists
    then listDirectory filepath
    else return []

listDirectory :: MonadIO m => FilePath -> m [FilePath]
listDirectory = liftIO . IO.listDirectory

listFilesRecursive :: (MonadIO m, MonadUnliftIO m) => FilePath -> m [FilePath]
listFilesRecursive filePath = do
  ps <- listDirectory filePath
  qs <- fmap concat $ IO.interleaveSequenceM $ fmap (recurse filePath) ps
  let rs = if filePath /= "." then fmap (filePath </>) qs else qs
  return rs
  where go :: (MonadIO m, MonadUnliftIO m) => FilePath -> FilePath -> m [FilePath]
        go fp dir = do
          ps <- listDirectory fp
          qs <- fmap concat $ IO.interleaveSequenceM $ fmap (recurse fp) ps
          return (fmap (dir </>) qs)

        recurse :: (MonadIO m, MonadUnliftIO m) => FilePath -> FilePath -> m [FilePath]
        recurse fp p = do
          isDirectory <- liftIO $ IO.doesDirectoryExist (fp </> p)
          if isDirectory
            then case fp </> p of
              subPath -> if "./" `L.isPrefixOf` subPath
                then go (drop 2 subPath) p
                else go subPath p
            else return [p]

listFilesRecursiveWithPrefix :: (MonadIO m, MonadUnliftIO m) => FilePath -> m [FilePath]
listFilesRecursiveWithPrefix prefix = if '/' `elem` prefix
  then do
    fs <- listFilesRecursive (dirname prefix)
    return (filter (prefix `L.isPrefixOf`) fs)
  else do
    fs <- listFilesRecursive "."
    return (filter (prefix `L.isPrefixOf`) fs)