    -- |
    -- Module      : Foundation.VFS.Path
    -- License     : BSD-style
    -- Maintainer  : foundation
    -- Stability   : experimental
    -- Portability : portable
    --

    {-# LANGUAGE FlexibleContexts #-}

    module Foundation.VFS.Path
        (
          -- * Path class
          Path(..)

        , parent
        , filename
        , prefix
        , suffix
        ) where

    import Basement.Compat.Base

    -- $setup
    -- >>> import Basement.Compat.Base
    -- >>> import Foundation.VFS.FilePath
    -- >>> import Foundation.VFS.Path

    -- | Path type class
    --
    -- defines the Path associated types and basic functions to implement related
    -- to the path manipulation
    --
    -- # TODO, add missing enhancement:
    --
    -- @
    -- splitExtension :: PathEnt path -> (PathEnt path, PathEnt path)
    -- addExtension  :: PathEnt path -> PathEnt path -> PathEnt path
    -- (<.>) :: path -> PathEnt path -> path
    -- (-<.>) :: path -> PathEnt path -> path
    -- @
    --
    class Path path where
        -- | the associated PathEntity of the given `path`
        -- this type is the minimal element contained in the Path
        -- a Path is not a collection but it is possible to see this
        -- associated type equivalent to the `Foundation.Collection.Element` type family
        type PathEnt path

        -- | the associated prefix of the given `path`
        --
        -- in the case of a `Foundation.VFS.FilePath`, it is a void (i.e. `()`)
        -- in the case of a `Foundation.VFS.URI`, it is the schema, host, port...
        type PathPrefix path

        -- | the associated suffix of the given path
        --
        -- in the case of the `Foundation.VFS.FilePath`, it is a void (i.e. `()`)
        -- in the case of the `Foundation.VFS.URI`, it is a the query, the fragment
        type PathSuffix path

        -- | join a path entity to a given path
        (</>) :: path -> PathEnt path -> path

        -- | split the path into the associated elements
        splitPath :: path -> ( PathPrefix path
                             , [PathEnt path]
                             , PathSuffix path
                             )

        -- | build the path from the associated elements
        buildPath :: ( PathPrefix path
                     , [PathEnt path]
                     , PathSuffix path
                     )
                  -> path

    -- | parent is only going to drop the filename.
    --
    -- if you actually want to reference to the parent directory, simply uses:
    --
    -- @
    -- parent "." /= "." </> ".."
    -- @
    --
    -- >>> parent ("foo.hs" :: FilePath)
    -- .
    --
    -- >>> parent ("foo/bar/baz.hs" :: FilePath)
    -- foo/bar
    parent :: Path path => path -> path
    parent path = buildPath (p, init ps, s)
      where
        (p, ps, s) = splitPath path

    -- | get the filename of the given path
    --
    -- If there is no filename, you will receive the 'mempty' of the 'PathEnt'
    --
    -- >>> filename ("foo.hs" :: FilePath)
    -- foo.hs
    --
    -- >>> filename ("foo/bar/baz.hs" :: FilePath)
    -- baz.hs
    filename :: (Path path, Monoid (PathEnt path)) => path -> PathEnt path
    filename path = case ps of
        [] -> mempty
        _  -> last ps
      where
        (_, ps , _) = splitPath path

    -- TODO: this might be better in Sequential ?
    init :: [a] -> [a]
    init [] = []
    init [_] = []
    init (x:xs) = x : init xs

    -- TODO: this might be better in Sequential ?
    last :: [a] -> a
    last [] = undefined
    last [x] = x
    last (_:xs) = last xs

    -- | get the path prefix information
    --
    -- >>> prefix ("/home/tab" :: FilePath)
    -- Absolute
    --
    -- >>> prefix ("home/tab" :: FilePath)
    -- Relative
    --
    -- or for URI (TODO, not yet accurate)
    --
    -- @
    -- prefix "http://github.com/vincenthz/hs-foundation?w=1"
    --    == URISchema http Nothing Nothing "github.com" Nothing
    -- @
    prefix :: Path path => path -> PathPrefix path
    prefix p = pre
      where
        (pre, _, _) = splitPath p

    -- | get the path suffix information
    --
    -- >>> suffix ("/home/tab" :: FilePath)
    -- ()
    --
    -- or for URI (TODO, not yet accurate)
    --
    -- @
    -- suffix "http://github.com/vincenthz/hs-foundation?w=1"
    --    == URISuffix (["w", "1"], Nothing)
    -- @
    suffix :: Path path => path -> PathSuffix path
    suffix p = suf
      where
        (_, _, suf) = splitPath p