module Text.LaTeX.LambdaTeX.Selection.Internal where

import Data.List (isPrefixOf)

import Text.LaTeX.LambdaTeX.Selection.Types
import Text.LaTeX.LambdaTeX.Types

-- | Whether a part is selected by a given list of selectors.
--
-- A part is selected if:
--
-- All parts are selected or
-- It it is matched and not ignored.
selects :: Part -> [Selector] -> Bool
selects (Part parts) selectors = go parts selectors False
  where
    go :: [Text] -> [Selector] -> Bool -> Bool
    go _ [] b = b
    go ps (All:ss) _ = go ps ss True
    go ps (Match s:ss) b =
        if ps `isPrefixOf` s || s `isPrefixOf` ps
            then go ps ss True
            else go ps ss b
    go ps (Ignore s:ss) True =
        if s `isPrefixOf` ps
            then go ps ss False
            else go ps ss True
    go ps (Ignore _:ss) False = go ps ss False

-- | Whether the current part is selected to be generated
isSelected :: Monad m => ΛTeXT m Bool
isSelected = do
    part <- currentPart
    sels <- λasks configSelection
    return $ selects part sels

-- * Current part Helpers
-- | Returns the current part from the ΛTeX state
currentPart :: Monad m => ΛTeXT m Part
currentPart = λgets stateCurrentPart

-- | Execute the following function with a given sub-part name
inPart :: Monad m => Text -> ΛTeXT m () -> ΛTeXT m ()
inPart partname func = do
    pushCurrentPart partname
    func
    popCurrentPart

-- | Push a new part name onto the current part stack
--
-- WARNING: This should never be used outside of 'inPart'
pushCurrentPart :: Monad m => Text -> ΛTeXT m ()
pushCurrentPart partname =
    λmodify
        (\s -> s {stateCurrentPart = pushPart (stateCurrentPart s) partname})

-- | Pop a part name off the current part stack
--
-- WARNING: This should never be used outside of 'inPart'
popCurrentPart :: Monad m => ΛTeXT m ()
popCurrentPart =
    λmodify (\s -> s {stateCurrentPart = popPart $ stateCurrentPart s})

-- * Text Helpers
-- | Split a string on a dot.
-- This can be useful to convert from a 'Selector's textual representation back to a selector
--
-- > split = splitOn '.'
split :: String -> [String]
split = splitOn '.'

-- | Split a string on a specific character
splitOn :: Char -> String -> [String]
splitOn c str = go str []
  where
    go :: String -> String -> [String]
    go [] s = [s]
    go (sc:ss) acc
        | sc == c = acc : go ss []
        | otherwise = go ss (acc ++ [sc])