    -- | This module provides a type and functions for handling focus rings
    -- of values.
    --
    -- This interface is experimental.
    module Brick.Focus
      ( FocusRing
      , focusRing
      , focusNext
      , focusPrev
      , focusGetCurrent
      , focusSetCurrent
      , focusRingCursor
      , withFocusRing
      , focusRingModify
      )
    where

    import Lens.Micro ((^.))
    import Data.Maybe (listToMaybe)
    import qualified Data.CircularList as C

    import Brick.Types
    import Brick.Widgets.Core (Named(..))

    -- | A focus ring containing a sequence of resource names to focus and a
    -- currently-focused name.
    newtype FocusRing n = FocusRing (C.CList n)

    -- | Construct a focus ring from the list of resource names.
    focusRing :: [n] -> FocusRing n
    focusRing = FocusRing . C.fromList

    -- | Advance focus to the next value in the ring.
    focusNext :: FocusRing n -> FocusRing n
    focusNext r@(FocusRing l)
        | C.isEmpty l = r
        | otherwise = FocusRing $ C.rotR l

    -- | Advance focus to the previous value in the ring.
    focusPrev :: FocusRing n -> FocusRing n
    focusPrev r@(FocusRing l)
        | C.isEmpty l = r
        | otherwise = FocusRing $ C.rotL l

    -- | This function is a convenience function to look up a widget state
    -- value's resource name in a focus ring and set its focus setting
    -- according to the focus ring's state. This function determines whether
    -- a given widget state value is the focus of the ring and passes the
    -- resulting boolean to a rendering function, along with the state value
    -- (a), to produce whatever comes next (b).
    --
    -- Focus-aware widgets have rendering functions that should be
    -- usable with this combinator; see 'Brick.Widgets.List.List' and
    -- 'Brick.Widgets.Edit.Edit'.
    withFocusRing :: (Eq n, Named a n)
                  => FocusRing n
                  -- ^ The focus ring to use as the source of focus state.
                  -> (Bool -> a -> b)
                  -- ^ A function that takes a value and its focus state.
                  -> a
                  -- ^ The wiget state value that we need to check for focus.
                  -> b
                  -- ^ The rest of the computation.
    withFocusRing ring f a = f (focusGetCurrent ring == Just (getName a)) a

    -- | Get the currently-focused resource name from the ring. If the ring
    -- is emtpy, return 'Nothing'.
    focusGetCurrent :: FocusRing n -> Maybe n
    focusGetCurrent (FocusRing l) = C.focus l

    -- | Set the currently-focused resource name in the ring, provided the
    -- name is in the ring. Otherwise return the ring unmodified.
    focusSetCurrent :: (Eq n) => n -> FocusRing n -> FocusRing n
    focusSetCurrent n r@(FocusRing l) =
        case C.rotateTo n l of
            Nothing -> r
            Just l' -> FocusRing l'

    -- | Modify the internal circular list structure of a focus ring
    -- directly. This function permits modification of the circular list
    -- using the rich Data.CircularList API.
    focusRingModify :: (C.CList n -> C.CList n) -> FocusRing n -> FocusRing n
    focusRingModify f (FocusRing l) = FocusRing $ f l

    -- | Cursor selection convenience function for use as an
    -- 'Brick.Main.appChooseCursor' value.
    focusRingCursor :: (Eq n)
                    => (a -> FocusRing n)
                    -- ^ The function used to get the focus ring out of your
                    -- application state.
                    -> a
                    -- ^ Your application state.
                    -> [CursorLocation n]
                    -- ^ The list of available cursor positions.
                    -> Maybe (CursorLocation n)
                    -- ^ The cursor position, if any, that matches the
                    -- resource name currently focused by the 'FocusRing'.
    focusRingCursor getRing st ls =
        listToMaybe $ filter isCurrent ls
        where
            isCurrent cl = cl^.cursorLocationNameL ==
                           (focusGetCurrent $ getRing st)