module Tintin.Capabilities.Filesystem
  ( Filesystem
  , Capability
  , Path(..)
  , Extension(..)

  , local
  , deleteIfExists
  , list
  , currentDirectory
  , readFile
  , makeDirectory
  , writeFile
  , doesExist

  , getPathsWith
  )
where

import Tintin.Core hiding (list, local, readFile, writeFile)
import qualified Tintin.Core as Core
import Tintin.Capabilities

require Data.Text


data Filesystem

newtype Path      = Path Text deriving ( Show, Ord, Eq )
newtype Extension = Extension Text


data Capability = Capability
  { _deleteIfExists   :: Path -> IO ()
  , _list             :: Path -> IO [Path]
  , _currentDirectory :: IO Path
  , _readFile         :: Path -> IO Text
  , _makeDirectory    :: Path -> IO ()
  , _writeFile        :: Path -> Text -> IO ()
  , _doesExist        :: Path -> IO Bool
  }


local :: Capability
local =
  Capability {..}
 where
  _deleteIfExists (Path p) = do
    let pathToDelete = toString p
    pathExists <- doesDirectoryExist pathToDelete
    when pathExists (removeDirectoryRecursive pathToDelete)

  _list (Path p) = do
    files <- listDirectory (toString p)
    return $ sort (Path . toText <$> files)

  _currentDirectory =
    getCurrentDirectory
    & fmap toText
    & fmap Path

  _readFile (Path p) = Core.readFile (toString p)

  _makeDirectory (Path p) = createDirectoryIfMissing True (toString p)

  _writeFile (Path p) = Core.writeFile (toString p)

  _doesExist (Path p) = doesPathExist (toString p)


deleteIfExists :: Has Capability eff
               => Path
               -> Effectful eff ()
deleteIfExists = liftCapability _deleteIfExists


list :: Has Capability eff
     => Path
     -> Effectful eff [Path]
list = liftCapability _list


currentDirectory :: Has Capability eff
                 => Effectful eff Path
currentDirectory = liftCapability _currentDirectory


readFile :: Has Capability eff
         => Path
         -> Effectful eff Text
readFile = liftCapability _readFile


makeDirectory :: Has Capability eff
              => Path
              -> Effectful eff ()
makeDirectory = liftCapability _makeDirectory


writeFile :: Has Capability eff
          => Path
          -> Text
          -> Effectful eff ()
writeFile = liftCapability _writeFile


doesExist :: Has Capability eff
          => Path
          -> Effectful eff Bool
doesExist = liftCapability _doesExist


getPathsWith :: Extension -> [Path] -> [Path]
getPathsWith (Extension e) =
  filter (\(Path fn) -> e `Text.isSuffixOf` fn)