module Text.Layout.Table.Spec.OccSpec
    ( OccSpec
    , predOccSpec
    , splitAtOcc
    , predicate
    ) where

import Control.Arrow

-- | Specifies an occurence of a letter.
data OccSpec = OccSpec (Char -> Bool) Int

predicate :: OccSpec -> (Char -> Bool)
predicate :: OccSpec -> Char -> Bool
predicate (OccSpec Char -> Bool
p Int
_) = Char -> Bool
p

-- | Construct an occurence specification by using a predicate.
predOccSpec :: (Char -> Bool) -> OccSpec
predOccSpec :: (Char -> Bool) -> OccSpec
predOccSpec Char -> Bool
p = (Char -> Bool) -> Int -> OccSpec
OccSpec Char -> Bool
p Int
0

-- | Use an occurence specification to split a 'String'.
splitAtOcc :: OccSpec -> String -> (String, String)
splitAtOcc :: OccSpec -> String -> (String, String)
splitAtOcc (OccSpec Char -> Bool
p Int
occ) = (String -> String) -> (String, String) -> (String, String)
forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (b, d) (c, d)
first String -> String
forall a. [a] -> [a]
reverse ((String, String) -> (String, String))
-> (String -> (String, String)) -> String -> (String, String)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> String -> String -> (String, String)
go Int
0 []
  where
    go :: Int -> String -> String -> (String, String)
go Int
n String
ls String
xs = case String
xs of
        []      -> (String
ls, [])
        Char
x : String
xs' -> if Char -> Bool
p Char
x
                   then if Int
n Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
occ
                        then (String
ls, String
xs)
                        else Int -> String -> String -> (String, String)
go (Int -> Int
forall a. Enum a => a -> a
succ Int
n) (Char
x Char -> String -> String
forall a. a -> [a] -> [a]
: String
ls) String
xs'
                   else Int -> String -> String -> (String, String)
go Int
n (Char
x Char -> String -> String
forall a. a -> [a] -> [a]
: String
ls) String
xs'