content: {-# LANGUAGE RecordWildCards #-}
  {-# LANGUAGE TemplateHaskell #-}
  module Vgrep.Environment.Config where
  
  import Control.Lens.Compat
  import Control.Monad.IO.Class
  import Data.Maybe
  import Data.Monoid
  import Graphics.Vty.Attributes
      ( Attr
      , blue
      , bold
      , defAttr
      , green
      , standout
      , withBackColor
      , withForeColor
      , withStyle
      )
  
  import           Vgrep.Command
  import           Vgrep.Environment.Config.Monoid
  import           Vgrep.Environment.Config.Sources
  import qualified Vgrep.Key                        as Key
  import           Vgrep.KeybindingMap              (KeybindingMap (..))
  import qualified Vgrep.KeybindingMap              as KeybindingMap
  
  
  --------------------------------------------------------------------------
  -- * Types
  --------------------------------------------------------------------------
  
  data Config = Config
      { _colors      :: Colors
      -- ^ Color configuration
  
      , _tabstop     :: Int
      -- ^ Tabstop width (default: 8)
  
      , _editor      :: String
      -- ^ Executable for @e@ key (default: environment variable @$EDITOR@,
      -- or @vi@ if @$EDITOR@ is not set)
  
      , _keybindings :: Keybindings
  
      } deriving (Eq, Show)
  
  data Colors = Colors
      { _lineNumbers   :: Attr
      -- ^ Line numbers (default: blue)
  
      , _lineNumbersHl :: Attr
      -- ^ Highlighted line numbers (default: bold blue)
  
      , _normal        :: Attr
      -- ^ Normal text (default: terminal default)
  
      , _normalHl      :: Attr
      -- ^ Highlighted text (default: bold)
  
      , _fileHeaders   :: Attr
      -- ^ File names in results view (default: terminal default color on
      -- green background)
  
      , _selected      :: Attr
      -- ^ Selected entry (default: terminal default, inverted)
  
      } deriving (Eq, Show)
  
  data Keybindings = Keybindings
      { _resultsKeybindings :: KeybindingMap
      -- ^ Keybindings in effect when results list is focused.
  
      , _pagerKeybindings   :: KeybindingMap
      -- ^ Keybindings in effect when pager is focused.
  
      , _globalKeybindings  :: KeybindingMap
      -- ^ Global keybindings are in effect both for pager and results list, but
      -- can be overridden by either one.
  
      } deriving (Eq, Show)
  
  
  --------------------------------------------------------------------------
  -- * Auto-generated Lenses
  --------------------------------------------------------------------------
  
  makeLenses ''Config
  makeLenses ''Colors
  makeLenses ''Keybindings
  
  
  --------------------------------------------------------------------------
  -- * Read Config from Monoid
  --------------------------------------------------------------------------
  
  -- | Convert a 'ConfigMonoid' to a 'Config'. Missing (@'mempty'@) values in the
  -- 'ConfigMonoid' are supplied from the 'defaultConfig'.
  fromConfigMonoid :: ConfigMonoid -> Config
  fromConfigMonoid ConfigMonoid{..} = Config
      { _colors  = fromColorsMonoid _mcolors
      , _tabstop = fromFirst (_tabstop defaultConfig) _mtabstop
      , _editor  = fromFirst (_editor  defaultConfig) _meditor
      , _keybindings = fromKeybindingsMonoid _mkeybindings }
  
  -- | Convert a 'ColorsMonoid' to a 'Colors' config.
  fromColorsMonoid :: ColorsMonoid -> Colors
  fromColorsMonoid ColorsMonoid{..} = Colors
      { _lineNumbers   = fromFirst (_lineNumbers   defaultColors) _mlineNumbers
      , _lineNumbersHl = fromFirst (_lineNumbersHl defaultColors) _mlineNumbersHl
      , _normal        = fromFirst (_normal        defaultColors) _mnormal
      , _normalHl      = fromFirst (_normalHl      defaultColors) _mnormalHl
      , _fileHeaders   = fromFirst (_fileHeaders   defaultColors) _mfileHeaders
      , _selected      = fromFirst (_selected      defaultColors) _mselected }
  
  fromFirst :: a -> First a -> a
  fromFirst a = fromMaybe a . getFirst
  
  fromKeybindingsMonoid :: KeybindingsMonoid -> Keybindings
  fromKeybindingsMonoid KeybindingsMonoid{..} = Keybindings
      { _resultsKeybindings = fromMaybe mempty _mresultsKeybindings <> _resultsKeybindings defaultKeybindings
      , _pagerKeybindings   = fromMaybe mempty _mpagerKeybindings   <> _pagerKeybindings   defaultKeybindings
      , _globalKeybindings  = fromMaybe mempty _mglobalKeybindings  <> _globalKeybindings  defaultKeybindings }
  
  
  --------------------------------------------------------------------------
  -- * Default Config
  --------------------------------------------------------------------------
  
  defaultConfig :: Config
  defaultConfig = Config
      { _colors = defaultColors
      , _tabstop = 8
      , _editor = "vi"
      , _keybindings = defaultKeybindings }
  
  defaultColors :: Colors
  defaultColors = Colors
      { _lineNumbers   = defAttr `withForeColor` blue
      , _lineNumbersHl = defAttr `withForeColor` blue
                                 `withStyle` bold
      , _normal        = defAttr
      , _normalHl      = defAttr `withStyle` bold
      , _fileHeaders   = defAttr `withBackColor` green
      , _selected      = defAttr `withStyle` standout }
  
  defaultKeybindings :: Keybindings
  defaultKeybindings = Keybindings
      { _resultsKeybindings = KeybindingMap.fromList
          [ (Key.key Key.Up,          ResultsUp)
          , (Key.key Key.Down,        ResultsDown)
          , (Key.key Key.PageUp,      ResultsPageUp)
          , (Key.key Key.PageDown,    ResultsPageDown)
          , (Key.key Key.Enter,       PagerGotoResult)
          , (Key.key (Key.Char 'k'),  ResultsUp)
          , (Key.key (Key.Char 'j'),  ResultsDown)
          , (Key.key (Key.Char 'b') `Key.withModifier` Key.Ctrl, ResultsPageUp)
          , (Key.key (Key.Char 'f') `Key.withModifier` Key.Ctrl, ResultsPageDown)
          , (Key.key (Key.Char 'f'),  DisplayResultsOnly)
          , (Key.key Key.Tab,         SplitFocusPager) ]
      , _pagerKeybindings = KeybindingMap.fromList
          [ (Key.key Key.Up,          PagerUp)
          , (Key.key Key.Down,        PagerDown)
          , (Key.key Key.PageUp,      PagerPageUp)
          , (Key.key Key.PageDown,    PagerPageDown)
          , (Key.key Key.Left,        PagerScrollLeft)
          , (Key.key Key.Right,       PagerScrollRight)
          , (Key.key (Key.Char 'k'),  PagerUp)
          , (Key.key (Key.Char 'j'),  PagerDown)
          , (Key.key (Key.Char 'h'),  PagerScrollLeft)
          , (Key.key (Key.Char 'l'),  PagerScrollRight)
          , (Key.key (Key.Char 'u') `Key.withModifier` Key.Ctrl, PagerHalfPageUp)
          , (Key.key (Key.Char 'd') `Key.withModifier` Key.Ctrl, PagerHalfPageDown)
          , (Key.key (Key.Char 'b') `Key.withModifier` Key.Ctrl, PagerPageUp)
          , (Key.key (Key.Char 'f') `Key.withModifier` Key.Ctrl, PagerPageDown)
          , (Key.key (Key.Char 'f'),  DisplayPagerOnly)
          , (Key.key Key.Tab,         SplitFocusResults)
          , (Key.key (Key.Char 'q'),  DisplayResultsOnly) ]
      , _globalKeybindings = KeybindingMap.fromList
          [ (Key.key (Key.Char 'e'),  OpenFileInEditor)
          , (Key.key (Key.Char 'q'),  Exit) ]
      }
  
  
  --------------------------------------------------------------------------
  -- * Config Loader
  --------------------------------------------------------------------------
  
  -- | Gathers 'ConfigMonoid's from various sources and builds a 'Config'
  -- based on the 'defaultConfig':
  --
  -- * Config from environment variables
  -- * The configuration specified in the config file
  -- * External config, e.g. from command line
  --
  -- where the latter ones override the earlier ones.
  loadConfig
      :: MonadIO io
      => ConfigMonoid -- ^ External config from command line
      -> io Config
  loadConfig configFromArgs = do
      configs <- sequence
          [ pure configFromArgs
          , configFromFile
          , editorConfigFromEnv ]
      pure (fromConfigMonoid (mconcat configs))
  