module Storage.Hashed.AnchoredPath
    ( Name(..), AnchoredPath(..), anchoredRoot, appendPath, anchorPath
    , isPrefix, parent, parents, catPaths, flatten, makeName, appendToName
    
    , floatBS, floatPath, replacePrefixPath ) where
import qualified Data.ByteString.Char8 as BS
import Data.List( isPrefixOf, inits )
import System.FilePath( (</>), splitDirectories, normalise, dropTrailingPathSeparator )
newtype Name = Name BS.ByteString  deriving (Eq, Show, Ord)
newtype AnchoredPath = AnchoredPath [Name] deriving (Eq, Show, Ord)
isPrefix :: AnchoredPath -> AnchoredPath -> Bool
(AnchoredPath a) `isPrefix` (AnchoredPath b) = a `isPrefixOf` b
appendPath :: AnchoredPath -> Name -> AnchoredPath
appendPath (AnchoredPath p) n =
    case n of
      (Name s) | s == BS.empty -> AnchoredPath p
               | s == BS.pack "." -> AnchoredPath p
               | otherwise -> AnchoredPath $ p ++ [n]
catPaths :: AnchoredPath -> AnchoredPath -> AnchoredPath
catPaths (AnchoredPath p) (AnchoredPath n) = AnchoredPath $ p ++ n
parent :: AnchoredPath -> AnchoredPath
parent (AnchoredPath x) = AnchoredPath (init x)
parents :: AnchoredPath -> [AnchoredPath]
parents (AnchoredPath x) = map AnchoredPath . init . inits $ x
anchorPath :: FilePath -> AnchoredPath -> FilePath
anchorPath dir p = dir </> BS.unpack (flatten p)
floatBS :: BS.ByteString -> AnchoredPath
floatBS = AnchoredPath . map Name . takeWhile (not . BS.null) . BS.split '/'
flatten :: AnchoredPath -> BS.ByteString
flatten (AnchoredPath []) = BS.singleton '.'
flatten (AnchoredPath p) = BS.intercalate (BS.singleton '/')
                                           [ n | (Name n) <- p ]
makeName :: String -> Name
makeName ".." = error ".. is not a valid AnchoredPath component name"
makeName n | '/' `elem` n = error "/ may not occur in a valid AnchoredPath component name"
           | otherwise = Name $ BS.pack n
floatPath :: FilePath -> AnchoredPath
floatPath = make . splitDirectories . normalise . dropTrailingPathSeparator
  where make ["."] = AnchoredPath []
        make x = AnchoredPath $ map (Name . BS.pack) x
anchoredRoot :: AnchoredPath
anchoredRoot = AnchoredPath []
replacePrefixPath :: AnchoredPath -> AnchoredPath -> AnchoredPath -> AnchoredPath
replacePrefixPath (AnchoredPath []) b c = catPaths b c
replacePrefixPath (AnchoredPath (r:p)) b (AnchoredPath (r':p'))
    | r == r' = replacePrefixPath (AnchoredPath p) b (AnchoredPath p')
    | otherwise = AnchoredPath []
replacePrefixPath _ _ _ = AnchoredPath []
appendToName :: AnchoredPath -> String -> AnchoredPath
appendToName (AnchoredPath p) s = AnchoredPath (init p++[Name finalname])
    where suffix = BS.pack s
          finalname | suffix `elem` (BS.tails lastname) = lastname
                    | otherwise = BS.append lastname suffix
          lastname = case last p of
                        Name name -> name