{-# LANGUAGE DeriveGeneric #-}
module Vgrep.Environment.Config.Monoid
  ( ConfigMonoid (..)
  , ColorsMonoid (..)
  , KeybindingsMonoid (..)
  ) where

import Data.Monoid
import Generics.Deriving.Monoid (mappenddefault, memptydefault)
import GHC.Generics
import Graphics.Vty.Attributes  (Attr)

import Vgrep.KeybindingMap (KeybindingMap (..))

-- $setup
-- >>> import Data.Map.Strict
-- >>> import Vgrep.Command
-- >>> import qualified Vgrep.Key as Key

-- | A 'Monoid' for reading partial configs. The 'ConfigMonoid' can be converted
-- to an actual 'Vgrep.Environment.Config.Config' using
-- 'Vgrep.Environment.Config.fromConfigMonoid'.
--
-- The Monoid consists mostly of 'First a' values, so the most important config
-- (the one that overrides all the others) should be read first.
data ConfigMonoid = ConfigMonoid
    { _mcolors      :: ColorsMonoid
    , _mtabstop     :: First Int
    , _meditor      :: First String
    , _mkeybindings :: KeybindingsMonoid
    } deriving (Eq, Show, Generic)

instance Monoid ConfigMonoid where
    mempty  = memptydefault
    mappend = mappenddefault


-- | A 'Monoid' for reading partial 'Vgrep.Environment.Config.Colors'
-- configurations.
--
-- Note that the attributes are not merged, but overridden:
--
-- >>> import Graphics.Vty.Attributes
-- >>> let leftStyle  = defAttr `withStyle` standout
-- >>> let rightStyle = defAttr `withForeColor` black
-- >>> let l = mempty { _mnormal = First (Just leftStyle)}
-- >>> let r = mempty { _mnormal = First (Just rightStyle)}
-- >>> _mnormal (l <> r) == First (Just (leftStyle <> rightStyle))
-- False
-- >>> _mnormal (l <> r) == First (Just leftStyle)
-- True
data ColorsMonoid = ColorsMonoid
    { _mlineNumbers   :: First Attr
    , _mlineNumbersHl :: First Attr
    , _mnormal        :: First Attr
    , _mnormalHl      :: First Attr
    , _mfileHeaders   :: First Attr
    , _mselected      :: First Attr
    } deriving (Eq, Show, Generic)

instance Monoid ColorsMonoid where
    mempty = memptydefault
    mappend = mappenddefault


-- | A 'Monoid' for reading a partial 'Vgrep.Environment.Config.Keybindings'
-- configuration.
--
-- Mappings are combined using left-biased 'Data.Map.Strict.union':
--
-- >>> let l = Just (KeybindingMap (fromList [(Key.Chord mempty Key.Down, ResultsDown), (Key.Chord mempty Key.Up, ResultsUp)]))
-- >>> let r = Just (KeybindingMap (fromList [(Key.Chord mempty Key.Down, PagerDown)]))
-- >>> l <> r
-- Just (KeybindingMap {unKeybindingMap = fromList [(Chord (fromList []) Up,ResultsUp),(Chord (fromList []) Down,ResultsDown)]})
-- >>> r <> l
-- Just (KeybindingMap {unKeybindingMap = fromList [(Chord (fromList []) Up,ResultsUp),(Chord (fromList []) Down,PagerDown)]})
--
-- In particular, @'Just' ('Data.Map.Strict.fromList' [])@ (declaring an empty
-- list of mappings) and @'Nothing'@ (not declaring anything) are equivalent,
-- given that there are already default mappings:
--
-- >>> l <> Just (KeybindingMap (fromList [])) == l <> Nothing
-- True
--
-- This means that new keybindings override the previous ones if they collide,
-- otherwise they are simply added. To remove a keybinding, it has to be mapped
-- to 'Unset' explicitly.
data KeybindingsMonoid = KeybindingsMonoid
    { _mresultsKeybindings :: Maybe KeybindingMap
    , _mpagerKeybindings   :: Maybe KeybindingMap
    , _mglobalKeybindings  :: Maybe KeybindingMap
    } deriving (Eq, Show, Generic)

instance Monoid KeybindingsMonoid where
    mempty = memptydefault
    mappend = mappenddefault