-- | Serve an appropriate response for a folder request.
serveFolder :: StaticSettings -> Pieces -> W.Request -> Folder -> IO StaticResponse
serveFolder ss@StaticSettings {..} pieces req folder@Folder {..} =
    -- first check if there is an index file in this folder
    case getFirst $ mconcat $ map (findIndex $ rights folderContents) ssIndices of
        Just index ->
            let pieces' = setLast pieces index in
            case () of
              () | ssRedirectToIndex -> return $ Redirect pieces' Nothing
                 | Just path <- addTrailingSlash req ->
                    return $ RawRedirect path
                 | otherwise ->
                    -- start the checking process over, with a new set
                    checkPieces ss pieces' req
        Nothing ->
            case ssListing of
                Just _ | Just path <- addTrailingSlash req ->
                    return $ RawRedirect path
                Just listing -> do
                    -- directory listings turned on, display it
                    builder <- listing pieces folder
                    return $ WaiResponse $ W.responseBuilder H.status200
                        [ ("Content-Type", "text/html; charset=utf-8")
                        ] builder
                Nothing -> return $ WaiResponse $ W.responseLBS H.status403
                    [ ("Content-Type", "text/plain")
                    ] "Directory listings disabled"
  where
    setLast :: Pieces -> Piece -> Pieces
    setLast [] x = [x]
    setLast [t] x
        | fromPiece t == "" = [x]
    setLast (a:b) x = a : setLast b x

    addTrailingSlash :: W.Request -> Maybe ByteString
    addTrailingSlash req
        | S8.null rp = Just "/"
        | S8.last rp == '/' = Nothing
        | otherwise = Just $ S8.snoc rp '/'
      where
        rp = W.rawPathInfo req

    noTrailingSlash :: Pieces -> Bool
    noTrailingSlash [] = False
    noTrailingSlash [x] = fromPiece x /= ""
    noTrailingSlash (_:xs) = noTrailingSlash xs

    findIndex :: [File] -> Piece -> First Piece
    findIndex files index
        | index `elem` map fileName files = First $ Just index
        | otherwise = First Nothing
serveFolder :: StaticSettings -> Pieces -> W.Request -> Folder -> IO StaticResponse
serveFolder ss@StaticSettings {..} pieces req folder@Folder {..} =
    -- first check if there is an index file in this folder
    case getFirst $ mconcat $ map (findIndex $ rights folderContents) ssIndices of
        Just index ->
            let pieces' = setLast pieces index in
            case () of
              () | ssRedirectToIndex -> return $ Redirect pieces' Nothing
                 | Just path <- addTrailingSlash req ->
                    return $ RawRedirect path
                 | otherwise ->
                    -- start the checking process over, with a new set
                    checkPieces ss pieces' req
        Nothing ->
            case ssListing of
                Just _ | Just path <- addTrailingSlash req ->
                    return $ RawRedirect path
                Just listing -> do
                    -- directory listings turned on, display it
                    builder <- listing pieces folder
                    return $ WaiResponse $ W.responseBuilder H.status200
                        [ ("Content-Type", "text/html; charset=utf-8")
                        ] builder
                Nothing -> return $ WaiResponse $ W.responseLBS H.status403
                    [ ("Content-Type", "text/plain")
                    ] "Directory listings disabled"
  where
    setLast :: Pieces -> Piece -> Pieces
    setLast [] x = [x]
    setLast [t] x
        | fromPiece t == "" = [x]
    setLast (a:b) x = a : setLast b x

    addTrailingSlash :: W.Request -> Maybe ByteString
    addTrailingSlash req
        | S8.null rp = Just "/"
        | S8.last rp == '/' = Nothing
        | otherwise = Just $ S8.snoc rp '/'
      where
        rp = W.rawPathInfo req

    noTrailingSlash :: Pieces -> Bool
    noTrailingSlash [] = False
    noTrailingSlash [x] = fromPiece x /= ""
    noTrailingSlash (_:xs) = noTrailingSlash xs

    findIndex :: [File] -> Piece -> First Piece
    findIndex files index
        | index `elem` map fileName files = First $ Just index
        | otherwise = First Nothing
serveFolder ss@StaticSettings {..} pieces req folder@Folder {..} =
    -- first check if there is an index file in this folder
    case getFirst $ mconcat $ map (findIndex $ rights folderContents) ssIndices of
        Just index ->
            let pieces' = setLast pieces index in
            case () of
              () | ssRedirectToIndex -> return $ Redirect pieces' Nothing
                 | Just path <- addTrailingSlash req ->
                    return $ RawRedirect path
                 | otherwise ->
                    -- start the checking process over, with a new set
                    checkPieces ss pieces' req
        Nothing ->
            case ssListing of
                Just _ | Just path <- addTrailingSlash req ->
                    return $ RawRedirect path
                Just listing -> do
                    -- directory listings turned on, display it
                    builder <- listing pieces folder
                    return $ WaiResponse $ W.responseBuilder H.status200
                        [ ("Content-Type", "text/html; charset=utf-8")
                        ] builder
                Nothing -> return $ WaiResponse $ W.responseLBS H.status403
                    [ ("Content-Type", "text/plain")
                    ] "Directory listings disabled"
  where
    setLast :: Pieces -> Piece -> Pieces
    setLast [] x = [x]
    setLast [t] x
        | fromPiece t == "" = [x]
    setLast (a:b) x = a : setLast b x

    addTrailingSlash :: W.Request -> Maybe ByteString
    addTrailingSlash req
        | S8.null rp = Just "/"
        | S8.last rp == '/' = Nothing
        | otherwise = Just $ S8.snoc rp '/'
      where
        rp = W.rawPathInfo req

    noTrailingSlash :: Pieces -> Bool
    noTrailingSlash [] = False
    noTrailingSlash [x] = fromPiece x /= ""
    noTrailingSlash (_:xs) = noTrailingSlash xs

    findIndex :: [File] -> Piece -> First Piece
    findIndex files index
        | index `elem` map fileName files = First $ Just index
        | otherwise = First Nothing