module Re (
    matchOnce,
    matchAll,
    matchCount,
    matchTest,
    replace,
    replaceMap
) where

import qualified Text.Regex.Base as B
    ( RegexMaker(..)
    , RegexLike(..)
    , RegexContext(..)
    , defaultExecOpt
    , MatchText
    , MatchArray
    )
import Text.Regex.Posix as P
    ( Regex
    , compNewline
    , compIgnoreCase
    , compExtended
    )
import Data.Array ((!), indices, bounds, elems)

makeRegex :: String -> Regex
makeRegex = B.makeRegex

matchOnce :: String -> String -> Maybe (String, B.MatchText String, String)
matchOnce r s = B.matchOnceText (makeRegex r) s

matchAll :: String -> String -> [((String, Int), [String])]
matchAll r s = do
    let matches = B.matchAllText (makeRegex r) s
    map (\m -> ((substr m, count m), values m)) matches
    where
        values m = map (\n -> fst (m!n)) (drop 1 $ indices m)
        count m = snd $ bounds m
        substr m = fst (m!0)

matchCount :: String -> String -> Int
matchCount r s = B.matchCount (makeRegex r) s

matchTest :: String -> String -> Bool
matchTest r s = B.matchTest (makeRegex r) s

replace :: String -> String -> String -> String
replace r s m = replaceMap r s (func m)
    where
        func tmpl ps =
            compile "\\\\([0-9])" tmpl ps

compile :: String -> String -> [String] -> String
compile r s m = replaceMap r s (func m)
    where
        func ms ps = ms !! (read $ (ps!!1) :: Int)

replaceMap :: String -> String -> ([String] -> String) -> String
replaceMap r s f = do
    let matches = B.matchAllText (makeRegex r) s
    let splited = _split 0 s (map (\m -> m!0) matches)
    concat $ _replacer splited matches f
    where
        _replacer [] _ _ = []
        _replacer s [] _ = s
        _replacer ss ms f = do
            let ps = map (\m -> fst m) $ elems (head ms)
            [head ss] ++ [f ps] ++ (_replacer (tail ss) (tail ms) f)

        _split _ s [] = [s]
        _split i s m  = do
            let (offset, len) = snd (head m)
            let pre = take (offset - i) s
            let left = drop (offset + len - i) s
            pre : ( _split (offset + len) left (tail m) )