{-# LANGUAGE UndecidableInstances, TypeOperators, FlexibleContexts, MultiParamTypeClasses, FlexibleInstances, TypeFamilies, ScopedTypeVariables #-}
module Data.Monoid.Generator.Combinators
    ( module Data.Monoid.Generator
    , traverse_
    , for_
    , mapM_
    , forM_
    , foldMap'
    , concatMap
    , and
    , or
    , any
    , all
    , sum
    , product
    , elem
    , notElem
    , filter
    , find
    ) where

import Prelude hiding (mapM_, any, elem, filter, concatMap, and, or, all, sum, product, notElem)
import Data.Monoid.Generator
import Data.Monoid.Applicative
import Data.Monoid.Monad
import Data.Monoid.Monad.Identity hiding (mapM_, forM_)

traverse_ :: (Generator c, Applicative f) => (Elem c -> f b) -> c -> f ()
traverse_ f = getTraversal . mapReduce f
    
for_ :: (Generator c, Applicative f) => c -> (Elem c -> f b) -> f ()
for_ = flip traverse_

mapM_ :: (Generator c, Monad m) => (Elem c -> m b) -> c -> m ()
mapM_ f = getAction . mapReduce f

forM_ :: (Generator c, Monad m) => c -> (Elem c -> m b) -> m ()
forM_ = flip mapM_

foldMap' :: (Monoid m, Generator c) => (Elem c -> m) -> c -> m
foldMap' f = runIdentity . mapReduce f

concatMap :: Generator c => (Elem c -> [b]) -> c -> [b]
concatMap = foldMap'

and :: (Generator c, Elem c ~ Bool) => c -> Bool
and = getAll . reduce

or :: (Generator c, Elem c ~ Bool) => c -> Bool
or = getAny . reduce

any :: Generator c => (Elem c -> Bool) -> c -> Bool
any f = getAny . mapReduce f

all :: Generator c => (Elem c -> Bool) -> c -> Bool
all f = getAll . mapReduce f

sum :: (Generator c, Num (Elem c)) => c -> Elem c
sum = getSum . reduce

product :: (Generator c, Num (Elem c)) => c -> Elem c
product = getProduct . reduce

elem :: (Generator c, Eq (Elem c)) => Elem c -> c -> Bool
elem = any . (==)

notElem :: (Generator c, Eq (Elem c)) => Elem c -> c -> Bool
notElem x = not . elem x

filter :: (Generator c, Elem c `Reducer` m) => (Elem c -> Bool) -> c -> m
filter p = foldMap' f where
    f x | p x = unit x
        | otherwise = mempty

find :: Generator c => (Elem c -> Bool) -> c -> Maybe (Elem c)
find p = getFirst . filter p