module Byg.Functions.Paths
  ( joinPaths
  , fileComponents
  , hasExtension
  , listDirectory
  , isDirectory
  , makeDir
  , copyFile
  , copyTo
  ) where

import Byg.Types (IsFunctionIO(..), Token(..))
import Byg.DependencyGenerator (DepGenM, TokenableTo(..), onToken, onTupleToken, toTupleToken,
                            runFunctionIO, runFunctionIO_, untupleSndDepGenM)
import Byg.DependencyRunner (extractSndTokenAsList)

import Data.Char (toLower)
import Control.Monad (when)
import qualified System.Directory as SD

joinPaths :: (TokenableTo FilePath a, TokenableTo FilePath b) => a -> b -> DepGenM (Token FilePath)
joinPaths = onTupleToken $ \s0 s1 -> s0 ++ "/" ++ s1

fileComponents :: TokenableTo FilePath a => a -> DepGenM (Token (String, String))
fileComponents = onToken $ \s ->
  let (lastRev, firstRev) = span (/= '.') $ reverse s
      (base, ext) = case firstRev of
        _ : firstRev' -> (reverse firstRev', reverse lastRev)
        [] -> (reverse lastRev, "")
  in (base, ext)

hasExtension :: (TokenableTo [String] a, TokenableTo FilePath b) => a -> b -> DepGenM (Token Bool)
hasExtension exts filename = do
  ext <- onToken (map toLower) =<< untupleSndDepGenM =<< fileComponents filename
  onTupleToken elem ext exts


data ListDirectory = ListDirectory deriving Show
instance IsFunctionIO ListDirectory FilePath [FilePath] where
  evalFunctionIO ListDirectory s = SD.listDirectory s
  functionIOReads ListDirectory s = [s]
  functionIOWrites ListDirectory = const (ListToken [])
  functionIOWritesAny ListDirectory = False  -- old: force triggering

listDirectory :: TokenableTo FilePath a => a -> DepGenM (Token [FilePath])
listDirectory a = runFunctionIO ListDirectory =<< toToken a


data IsDirectory = IsDirectory deriving Show
instance IsFunctionIO IsDirectory FilePath Bool where
  evalFunctionIO IsDirectory s = SD.doesDirectoryExist s
  functionIOReads IsDirectory s = [s]
  functionIOWrites IsDirectory = const (ListToken [])
  functionIOWritesAny IsDirectory = False

isDirectory :: TokenableTo FilePath a => a -> DepGenM (Token Bool)
isDirectory a = runFunctionIO IsDirectory =<< toToken a


data MakeDir = MakeDir deriving Show
instance IsFunctionIO MakeDir FilePath () where
  evalFunctionIO MakeDir s = do
    exists <- SD.doesPathExist s
    when (not exists) $ SD.createDirectory s
  functionIOReads MakeDir = const []
  functionIOWrites MakeDir s = ListToken [s]


  -- Old: Don't consider a created
                                            -- directory "written", as there is
                                            -- no extra information than its name
                                            -- and presence.
  functionIOWritesAny MakeDir = True

makeDir :: TokenableTo FilePath a => a -> DepGenM ()
makeDir a = runFunctionIO_ MakeDir =<< toToken a


data CopyFile = CopyFile deriving Show
instance IsFunctionIO CopyFile (FilePath, FilePath) () where
  evalFunctionIO CopyFile (source, target) =
    SD.copyFile source target
  functionIOReads CopyFile (i, _) = [i]
  functionIOWrites CopyFile = extractSndTokenAsList
  functionIOWritesAny CopyFile = True

copyFile :: (TokenableTo FilePath a, TokenableTo FilePath b) => a -> b -> DepGenM ()
copyFile a b = runFunctionIO_ CopyFile =<< toTupleToken a b

copyTo :: (TokenableTo FilePath a, TokenableTo FilePath b) => a -> b -> DepGenM ()
copyTo path targetDir = do
  pathToken <- toToken path
  copyFile pathToken =<< joinPaths targetDir pathToken

