{-# LANGUAGE CPP #-}
-- | FileNames contain facilities for manipulating filenames
-- in a hopefully OS-independent manner.
module Util.FileNames(
   fileSep, -- :: Char
            -- file separator
   topDir,  -- :: String
            -- what we call the top directory.
   thisDir,  -- :: String
            -- what we call the current directory.
   trimDir, -- :: String -> String
            -- trim file separator from end of name if there.
            -- (intended for directories)
   splitName,
            -- :: String -> (String,String)
            -- Returns the directory and file part of a name.
   combineNames,
            -- :: String -> String -> String
            -- combines a directory and file name.
   breakName,
            -- :: String -> [String]
            -- breakName splits local file name completely into
            -- a sequence of file names, with the top directory
            -- first.  (If the first character is the file separator
            -- the first list element is the empty string.)
   unbreakName,
            -- :: [String] -> String
            -- unbreakName inverts breakName

   splitExtension,
            -- :: String -> Maybe (String,String)
            -- Remove the (last) extension part from a file name, returning
            -- the two parts.  For example "foo.bar" should go to (foo,bar).
   unsplitExtension,
            -- :: String -> String -> String
            -- reverse unsplitExtension.

   recordSep,
            -- :: String
            -- separator for between records.

   ) where

#ifdef WINDOWS
fileSep = '\\'
recordSep = "\r\n"
#else
fileSep = '/'
recordSep = "\n"
#endif

fileSep :: Char
recordSep :: String

topDir :: String
topDir = [fileSep]

thisDir :: String
thisDir = "."

trimDir :: String -> String
trimDir [] = []
trimDir (name@[c])
   | c==fileSep = []
   | True = name
trimDir (first:rest) = first:trimDir rest

splitName :: String -> (String,String)
splitName filePath0 =
   let
      filePath1 = trimDir filePath0
   in
      case splitName1 filePath1 of
         Nothing -> (thisDir,filePath1)
         Just ("",filePath2) -> (topDir,filePath2)
         Just simple -> simple

splitName1 :: String -> Maybe(String,String)
splitName1 [] = Nothing
splitName1 (first:remainder) =
   case splitName1 remainder of
      Nothing
         | first == fileSep -> Just([],remainder)
         | True -> Nothing
      Just (dir,name) -> Just (first:dir,name)

combineNames :: String -> String -> String
combineNames dir file = dir ++ (fileSep:file)

breakName :: String -> [String]
breakName [] = [[]]
breakName (first : rest)
   | (first == fileSep) = "":breakName rest
   | True =
      case breakName rest of
         firstName : restNames -> (first:firstName) : restNames
         [] -> error "breakName"

unbreakName :: [String] -> String
unbreakName [] = ""
unbreakName parts = foldr1 combineNames parts


splitExtension :: String -> Maybe (String,String)
splitExtension str = case splitExtension0 str of
      Just (ne @ (name,ext)) | not (null name) && not (null ext) -> Just ne
      _ -> Nothing
   where
      splitExtension0 [] = Nothing
      splitExtension0 (c:cs) = case splitExtension0 cs of
         Just (name0,ext) -> Just (c:name0,ext)
         Nothing -> if c == '.' then Just ("",cs) else Nothing

unsplitExtension :: String -> String -> String
unsplitExtension name ext = name ++ "." ++ ext