{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Highlight.Highlight.Monad
  ( module Highlight.Highlight.Monad
  , module Highlight.Common.Monad
  ) where

import Prelude ()
import Prelude.Compat

import Control.Lens (view)
import Control.Monad.Reader (MonadReader)
import Control.Monad.State (MonadState, get)
import Data.ByteString (ByteString)

import Highlight.Common.Error (HighlightErr(..))
import Highlight.Common.Monad
       (CommonHighlightM,
        FilenameHandlingFromFiles(NoFilename, PrintFilename), InputData,
        Output, compileHighlightRegexWithErr, createInputData,
        getInputFilenamesM, getRecursiveM, handleInputData,
        runCommonHighlightM, runOutputProducer)
import Highlight.Highlight.Options
       (ColorGrepFilenames, HasColorGrepFilenames(colorGrepFilenamesLens),
        Options(..))
import Highlight.Util (modify')

-- | The internal state that is used to figure out how to color filenames from
-- @grep@.
data FromGrepFilenameState = FromGrepFilenameState
  { fromGrepFilenameStatePrevFileNum :: {-# UNPACK #-} !Int
  , fromGrepFilenameStatePrevFilename :: !(Maybe ByteString)
  }

initFromGrepFilenameState :: FromGrepFilenameState
initFromGrepFilenameState =
  FromGrepFilenameState
  { fromGrepFilenameStatePrevFileNum = (-1)
  , fromGrepFilenameStatePrevFilename = Nothing
  }

-- | Call 'updateFilename' and return the new file number after doing the
-- update.
updateFilenameM :: MonadState FromGrepFilenameState m => ByteString -> m Int
updateFilenameM nextFilename = do
  modify' $ updateFilename nextFilename
  FromGrepFilenameState newFileNum _ <- get
  return newFileNum

-- | Update the file number in 'FromGrepFilenameState' if the 'ByteString'
-- filename passed in is different from that in 'FromGrepFilenameState'.
updateFilename :: ByteString -> FromGrepFilenameState -> FromGrepFilenameState
updateFilename nextFilename (FromGrepFilenameState prevFileNum prevFilename)
  | Just nextFilename == prevFilename =
    FromGrepFilenameState prevFileNum prevFilename
  | otherwise =
    FromGrepFilenameState (prevFileNum + 1) (Just nextFilename)

-------------------------
-- The Highlight Monad --
-------------------------

-- | 'HighlightM' is just 'CommonHighlightM' specialized for @highlight@.
type HighlightM = CommonHighlightM Options FromGrepFilenameState HighlightErr

runHighlightM :: Options -> HighlightM a -> IO (Either HighlightErr a)
runHighlightM opts = runCommonHighlightM opts initFromGrepFilenameState

----------------------------------
-- Get value of certain options --
----------------------------------

-- | Get the value of the 'ColorGrepFilenames' option.
getColorGrepFilenamesM
  :: (HasColorGrepFilenames r, MonadReader r m) => m ColorGrepFilenames
getColorGrepFilenamesM = view colorGrepFilenamesLens