    {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}

    -----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Layout.CenterMainFluid
    -- Description :  Three column layout with master in center and unoccupied spaces reserved.
    -- Copyright   :  (c) 2023 Mahdi Seyedan
    -- License     :  BSD-style (see xmonad/LICENSE)
    --
    -- Maintainer  :  Mahdi Seyedan. <mahdisn78@gmail.com>
    -- Stability   :  unstable
    -- Portability :  unportable
    --
    -- A three column layout with main column in the center and
    -- two stack columns surrounding it. There will be always
    -- a pane in the center column and unoccupied spaces on the
    -- sides are reserved.
    -- It's best suited for ultrawide montiors, where a single
    -- stretched window might be annoying.
    -----------------------------------------------------------------------------

    module XMonad.Layout.CenterMainFluid
      ( -- * Usage
        -- $usage
        CenterMainFluid (..)
      ) where

    import XMonad
    import qualified XMonad.StackSet as W
    import Control.Monad (msum)

    -- $usage
    -- You can use this module by adding following in your @xmonad.hs@:
    --
    -- > import XMonad.Layout.CenterMainFluid
    --
    -- Then edit your @layoutHook@ by adding the CenterMainFluid layout:
    --
    -- > myLayoutHook = CenterMainFluid 1 (3/100) (70/100) ||| ...
    -- > main = xmonad def { layoutHook = myLayout }
    --
    -- The first argument specifies how many windows initially appear in the center
    -- column. The second argument specifies the amount to resize while resizing
    -- and the third argument specifies the initial size of the center column.
    --
    -- For more detailed instructions on editing the layoutHook see
    -- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial> and
    -- "XMonad.Doc.Extending#Editing_the_layout_hook".

    -- | Arguments are nmaster, delta, fraction. Supports 'Shrink', 'Expand' and
    -- 'IncMasterN'
    data CenterMainFluid a = CenterMainFluid
      { forall a. CenterMainFluid a -> Int
    cmfNMaster :: !Int             -- ^ The default number of windows in the center pane (default: 1)
      , forall a. CenterMainFluid a -> Rational
    cmfRatioIncrement :: !Rational -- ^ Percent of screen to increment by when resizing panes (default: 3/100)
      , forall a. CenterMainFluid a -> Rational
    cmfRatio :: !Rational          -- ^ Default proportion of screen occupied by the center pane (default: 70/100)
      }
      deriving (Int -> CenterMainFluid a -> ShowS
    [CenterMainFluid a] -> ShowS
    CenterMainFluid a -> String
    (Int -> CenterMainFluid a -> ShowS)
    -> (CenterMainFluid a -> String)
    -> ([CenterMainFluid a] -> ShowS)
    -> Show (CenterMainFluid a)
    forall a. Int -> CenterMainFluid a -> ShowS
    forall a. [CenterMainFluid a] -> ShowS
    forall a. CenterMainFluid a -> String
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: forall a. Int -> CenterMainFluid a -> ShowS
    showsPrec :: Int -> CenterMainFluid a -> ShowS
    $cshow :: forall a. CenterMainFluid a -> String
    show :: CenterMainFluid a -> String
    $cshowList :: forall a. [CenterMainFluid a] -> ShowS
    showList :: [CenterMainFluid a] -> ShowS
    Show,ReadPrec [CenterMainFluid a]
    ReadPrec (CenterMainFluid a)
    Int -> ReadS (CenterMainFluid a)
    ReadS [CenterMainFluid a]
    (Int -> ReadS (CenterMainFluid a))
    -> ReadS [CenterMainFluid a]
    -> ReadPrec (CenterMainFluid a)
    -> ReadPrec [CenterMainFluid a]
    -> Read (CenterMainFluid a)
    forall a. ReadPrec [CenterMainFluid a]
    forall a. ReadPrec (CenterMainFluid a)
    forall a. Int -> ReadS (CenterMainFluid a)
    forall a. ReadS [CenterMainFluid a]
    forall a.
    (Int -> ReadS a)
    -> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
    $creadsPrec :: forall a. Int -> ReadS (CenterMainFluid a)
    readsPrec :: Int -> ReadS (CenterMainFluid a)
    $creadList :: forall a. ReadS [CenterMainFluid a]
    readList :: ReadS [CenterMainFluid a]
    $creadPrec :: forall a. ReadPrec (CenterMainFluid a)
    readPrec :: ReadPrec (CenterMainFluid a)
    $creadListPrec :: forall a. ReadPrec [CenterMainFluid a]
    readListPrec :: ReadPrec [CenterMainFluid a]
    Read)

    instance LayoutClass CenterMainFluid a where

        pureLayout :: CenterMainFluid a -> Rectangle -> Stack a -> [(a, Rectangle)]
    pureLayout (CenterMainFluid Int
    nmaster Rational
    _ Rational
    frac) Rectangle
    r Stack a
    s
            | Rational
    frac Rational -> Rational -> Bool
    forall a. Eq a => a -> a -> Bool
    == Rational
    0 = Int -> [(a, Rectangle)] -> [(a, Rectangle)]
    forall a. Int -> [a] -> [a]
    drop Int
    nmaster [(a, Rectangle)]
    layout
            | Rational
    frac Rational -> Rational -> Bool
    forall a. Eq a => a -> a -> Bool
    == Rational
    1 = Int -> [(a, Rectangle)] -> [(a, Rectangle)]
    forall a. Int -> [a] -> [a]
    take Int
    nmaster [(a, Rectangle)]
    layout
            | Bool
    otherwise = [(a, Rectangle)]
    layout
          where layout :: [(a, Rectangle)]
    layout = [a] -> [Rectangle] -> [(a, Rectangle)]
    forall a b. [a] -> [b] -> [(a, b)]
    zip [a]
    ws [Rectangle]
    rs
                ws :: [a]
    ws = Stack a -> [a]
    forall a. Stack a -> [a]
    W.integrate Stack a
    s
                rs :: [Rectangle]
    rs = Rational -> Rectangle -> Int -> Int -> [Rectangle]
    tile3 Rational
    frac Rectangle
    r Int
    nmaster ([a] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [a]
    ws)

        pureMessage :: CenterMainFluid a -> SomeMessage -> Maybe (CenterMainFluid a)
    pureMessage (CenterMainFluid Int
    nmaster Rational
    delta Rational
    frac) SomeMessage
    m =
                [Maybe (CenterMainFluid a)] -> Maybe (CenterMainFluid a)
    forall (t :: * -> *) (m :: * -> *) a.
    (Foldable t, MonadPlus m) =>
    t (m a) -> m a
    msum [(Resize -> CenterMainFluid a)
    -> Maybe Resize -> Maybe (CenterMainFluid a)
    forall a b. (a -> b) -> Maybe a -> Maybe b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Resize -> CenterMainFluid a
    forall {a}. Resize -> CenterMainFluid a
    resize     (SomeMessage -> Maybe Resize
    forall m. Message m => SomeMessage -> Maybe m
    fromMessage SomeMessage
    m)
                     ,(IncMasterN -> CenterMainFluid a)
    -> Maybe IncMasterN -> Maybe (CenterMainFluid a)
    forall a b. (a -> b) -> Maybe a -> Maybe b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap IncMasterN -> CenterMainFluid a
    forall {a}. IncMasterN -> CenterMainFluid a
    incmastern (SomeMessage -> Maybe IncMasterN
    forall m. Message m => SomeMessage -> Maybe m
    fromMessage SomeMessage
    m)]

          where resize :: Resize -> CenterMainFluid a
    resize Resize
    Shrink             = Int -> Rational -> Rational -> CenterMainFluid a
    forall a. Int -> Rational -> Rational -> CenterMainFluid a
    CenterMainFluid Int
    nmaster Rational
    delta (Rational -> Rational -> Rational
    forall a. Ord a => a -> a -> a
    max Rational
    0 (Rational -> Rational) -> Rational -> Rational
    forall a b. (a -> b) -> a -> b
    $ Rational
    fracRational -> Rational -> Rational
    forall a. Num a => a -> a -> a
    -Rational
    delta)
                resize Resize
    Expand             = Int -> Rational -> Rational -> CenterMainFluid a
    forall a. Int -> Rational -> Rational -> CenterMainFluid a
    CenterMainFluid Int
    nmaster Rational
    delta (Rational -> Rational -> Rational
    forall a. Ord a => a -> a -> a
    min Rational
    1 (Rational -> Rational) -> Rational -> Rational
    forall a b. (a -> b) -> a -> b
    $ Rational
    fracRational -> Rational -> Rational
    forall a. Num a => a -> a -> a
    +Rational
    delta)
                incmastern :: IncMasterN -> CenterMainFluid a
    incmastern (IncMasterN Int
    d) = Int -> Rational -> Rational -> CenterMainFluid a
    forall a. Int -> Rational -> Rational -> CenterMainFluid a
    CenterMainFluid (Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    max Int
    0 (Int
    nmasterInt -> Int -> Int
    forall a. Num a => a -> a -> a
    +Int
    d)) Rational
    delta Rational
    frac

        description :: CenterMainFluid a -> String
    description CenterMainFluid a
    _ = String
    "CenterMainFluid"

    tile3 :: Rational -> Rectangle -> Int -> Int -> [Rectangle]
    tile3 :: Rational -> Rectangle -> Int -> Int -> [Rectangle]
    tile3 Rational
    f Rectangle
    r Int
    nmaster Int
    n
      | Int
    nmaster Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    0 Bool -> Bool -> Bool
    || Int
    n Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    nmaster = Int -> Rectangle -> [Rectangle]
    splitVertically Int
    n Rectangle
    middleR
      | Bool
    otherwise = [Rectangle]
    masters [Rectangle] -> [Rectangle] -> [Rectangle]
    forall a. [a] -> [a] -> [a]
    ++ [Rectangle]
    rights [Rectangle] -> [Rectangle] -> [Rectangle]
    forall a. [a] -> [a] -> [a]
    ++ [Rectangle]
    lefts
          where (Rectangle
    leftR, Rectangle
    middleR, Rectangle
    rightR) = Rational -> Rectangle -> (Rectangle, Rectangle, Rectangle)
    forall r.
    RealFrac r =>
    r -> Rectangle -> (Rectangle, Rectangle, Rectangle)
    split3HorizontallyBy Rational
    f Rectangle
    r
                (Int
    halfN, Int
    remaining) = (Int
    n Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    nmaster) Int -> Int -> (Int, Int)
    forall a. Integral a => a -> a -> (a, a)
    `divMod` Int
    2
                masters :: [Rectangle]
    masters = Int -> Rectangle -> [Rectangle]
    splitVertically Int
    nmaster Rectangle
    middleR
                lefts :: [Rectangle]
    lefts = Int -> Rectangle -> [Rectangle]
    splitVertically Int
    halfN Rectangle
    leftR
                rights :: [Rectangle]
    rights = Int -> Rectangle -> [Rectangle]
    splitVertically (Int
    halfN Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    remaining) Rectangle
    rightR

    -- | Divide the screen into three rectangles, using a rational to specify the ratio of center one
    split3HorizontallyBy :: RealFrac r => r -> Rectangle -> (Rectangle, Rectangle, Rectangle)
    split3HorizontallyBy :: forall r.
    RealFrac r =>
    r -> Rectangle -> (Rectangle, Rectangle, Rectangle)
    split3HorizontallyBy r
    f (Rectangle Position
    sx Position
    sy Dimension
    sw Dimension
    sh) =
      ( Position -> Position -> Dimension -> Dimension -> Rectangle
    Rectangle Position
    sx Position
    sy Dimension
    sidew Dimension
    sh
      , Position -> Position -> Dimension -> Dimension -> Rectangle
    Rectangle (Position
    sx Position -> Position -> Position
    forall a. Num a => a -> a -> a
    + Dimension -> Position
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Dimension
    sidew) Position
    sy Dimension
    middlew Dimension
    sh
      , Position -> Position -> Dimension -> Dimension -> Rectangle
    Rectangle (Position
    sx Position -> Position -> Position
    forall a. Num a => a -> a -> a
    + Dimension -> Position
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Dimension
    sidew Position -> Position -> Position
    forall a. Num a => a -> a -> a
    + Dimension -> Position
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Dimension
    middlew) Position
    sy Dimension
    sidew Dimension
    sh
      )
      where middlew :: Dimension
    middlew = r -> Dimension
    forall b. Integral b => r -> b
    forall a b. (RealFrac a, Integral b) => a -> b
    floor (r -> Dimension) -> r -> Dimension
    forall a b. (a -> b) -> a -> b
    $ Dimension -> r
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Dimension
    sw r -> r -> r
    forall a. Num a => a -> a -> a
    * r
    f
            sidew :: Dimension
    sidew = (Dimension
    sw Dimension -> Dimension -> Dimension
    forall a. Num a => a -> a -> a
    - Dimension -> Dimension
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Dimension
    middlew) Dimension -> Dimension -> Dimension
    forall a. Integral a => a -> a -> a
    `div` Dimension
    2