-- File created: 2008-10-10 13:40:35

module System.FilePath.Glob.Utils where

import Data.List         ((\\), tails)
import qualified Data.Set as Set
import System.Directory  (doesDirectoryExist, getDirectoryContents)
import System.FilePath   ((</>), joinPath, splitPath)
import System.IO.Unsafe  (unsafeInterleaveIO)

inRange :: Ord a => (a,a) -> a -> Bool
inRange (a,b) c = c >= a && c <= b

-- returns Just (a range which covers both given ranges) or Nothing if they are
-- disjoint.
--
-- Assumes that the ranges are in the correct order, i.e. (fst x < snd x).
overlap :: Ord a => (a,a) -> (a,a) -> Maybe (a,a)
overlap (a,b) (c,d) =
   if b >= c
      then if b >= d
              then if a <= c
                      then Just (a,b)
                      else Just (c,b)
              else if a <= c
                      then Just (a,d)
                      else Just (c,d)
      else Nothing

addToRange :: (Ord a, Enum a) => (a,a) -> a -> Maybe (a,a)
addToRange (a,b) c
   | inRange (a,b) c = Just (a,b)
   | c == pred a     = Just (c,b)
   | c == succ b     = Just (a,c)
   | otherwise       = Nothing

-- fst of result is in reverse order so that:
--
-- If x = fst (increasingSeq (a:xs)), then
-- x == reverse [a .. head x]
increasingSeq :: (Eq a, Enum a) => [a] -> ([a],[a])
increasingSeq []     = ([],[])
increasingSeq (x:xs) = go [x] xs
 where
   go is       []     = (is,[])
   go is@(i:_) (y:ys) =
      if y == succ i
         then go (y:is) ys
         else (is, y:ys)
   go _ _ = error "Glob.increasingSeq :: internal error"

isLeft :: Either a b -> Bool
isLeft (Left _) = True
isLeft _        = False

fromLeft :: Either a b -> a
fromLeft (Left x) = x
fromLeft _        = error "fromLeft :: Right"

dropLeadingZeroes :: String -> String
dropLeadingZeroes s =
   let x = dropWhile (=='0') s
    in if null x then "0" else x

pathParts :: FilePath -> [FilePath]
pathParts = map joinPath . tails . splitPath

getRecursiveContents :: FilePath -> IO [FilePath]
getRecursiveContents dir = flip catch (const $ return []) $ do
   raw <- getDirectoryContents dir

   let entries    = raw \\ [".",".."]
       absEntries =
          if dir == "."
             then entries
             else map (dir </>) entries

   (dirs,files) <- partitionM doesDirectoryExist absEntries

   subs <- unsafeInterleaveIO . mapM getRecursiveContents $ dirs

   return$ dir : files ++ concat subs

partitionM :: (Monad m) => (a -> m Bool) -> [a] -> m ([a], [a])
partitionM _ []     = return ([],[])
partitionM p (x:xs) = do
   ~(ts,fs) <- partitionM p xs
   b <- p x
   return $ if b
               then (x:ts, fs)
               else (ts, x:fs)

nubOrd :: Ord a => [a] -> [a]
nubOrd = go Set.empty
 where
   go _ [] = []
   go set (x:xs) =
      if Set.member x set
         then go set xs
         else x : go (Set.insert x set) xs