-- |
-- Module      : Misc utilities useful in working with events
-- Copyright   : (c) Target RWE 2023
-- License     : BSD3
-- Maintainer  : bbrown@targetrwe.com 
--               ljackman@targetrwe.com 
--               dpritchard@targetrwe.com

module EventDataTheory.Utilities
  ( Predicate(..)
  {-
  RE: haddock message:
    80% (  4 /  5) in 'EventDataTheory.Utilities'
    Missing documentation for:
      Predicate
  See: https://github.com/haskell/haddock/issues/958
  -}
  , containsTag
  , findOccurrenceOfEvent
  , firstOccurrenceOfTag
  , lastOccurrenceOfTag
  , splitByTags
  , filterEvents
  , tallyEvents
  ) where

import           Data.Foldable              (length, toList)
import           Data.Functor.Contravariant (Predicate (..))
import           Data.Semigroup
import           EventDataTheory.Core
import           IntervalAlgebra
import           Safe                       (headMay, lastMay)
import qualified Witherable                 as W

{-|
Creates a predicate to check that an 'Event' contains
any of a given list of tags.
-}
containsTag :: (Ord t) => [t] -> Predicate (Event t m a)
containsTag :: forall t m a. Ord t => [t] -> Predicate (Event t m a)
containsTag [t]
tList = forall a. (a -> Bool) -> Predicate a
Predicate (forall a t. HasTag a t => a -> [t] -> Bool
`hasAnyTag` [t]
tList)

{-|
Filter a container of events by a predicate.
-}
filterEvents
  :: (W.Filterable f)
  => Predicate (Event t m a)
  -> f (Event t m a)
  -> f (Event t m a)
filterEvents :: forall (f :: * -> *) t m a.
Filterable f =>
Predicate (Event t m a) -> f (Event t m a) -> f (Event t m a)
filterEvents Predicate (Event t m a)
p = forall (f :: * -> *) a. Filterable f => (a -> Bool) -> f a -> f a
W.filter (forall a. Predicate a -> a -> Bool
getPredicate Predicate (Event t m a)
p)

{-|
Filter a container of 'Event's
to a single @'Maybe' 'Event'@,
based on a provided function,
with the provided tag set.

For example,
see 'firstOccurrenceOfTag' and
'lastOccurrenceOfTag'.
-}
findOccurrenceOfEvent
  :: (W.Filterable f)
  => (f (Event t m a) -> Maybe (Event t m a)) -- ^ function used to select a single event after the container is filtered
  -> Predicate (Event t m a) -- ^ predicate by which to filter
  -> f (Event t m a) -- ^ a container of events
  -> Maybe (Event t m a)
findOccurrenceOfEvent :: forall (f :: * -> *) t m a.
Filterable f =>
(f (Event t m a) -> Maybe (Event t m a))
-> Predicate (Event t m a)
-> f (Event t m a)
-> Maybe (Event t m a)
findOccurrenceOfEvent f (Event t m a) -> Maybe (Event t m a)
f Predicate (Event t m a)
p = f (Event t m a) -> Maybe (Event t m a)
f forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) t m a.
Filterable f =>
Predicate (Event t m a) -> f (Event t m a) -> f (Event t m a)
filterEvents Predicate (Event t m a)
p

{-|
Finds the *first* occurrence of an 'Event' with at least one of the tags
if one exists.
Assumes the input events are appropriately sorted.
-}
firstOccurrenceOfTag
  :: (W.Witherable f, Ord t) => [t] -> f (Event t m a) -> Maybe (Event t m a)
firstOccurrenceOfTag :: forall (f :: * -> *) t m a.
(Witherable f, Ord t) =>
[t] -> f (Event t m a) -> Maybe (Event t m a)
firstOccurrenceOfTag [t]
x =
  forall (f :: * -> *) t m a.
Filterable f =>
(f (Event t m a) -> Maybe (Event t m a))
-> Predicate (Event t m a)
-> f (Event t m a)
-> Maybe (Event t m a)
findOccurrenceOfEvent (forall a. [a] -> Maybe a
headMay forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) a. Foldable t => t a -> [a]
toList) (forall t m a. Ord t => [t] -> Predicate (Event t m a)
containsTag [t]
x)

{-|
Finds the *last* occurrence of an 'Event' with at least one of the tags
if one exists.
Assumes the input events list are appropriately sorted.
-}
lastOccurrenceOfTag
  :: (W.Witherable f, Ord t) => [t] -> f (Event t m a) -> Maybe (Event t m a)
lastOccurrenceOfTag :: forall (f :: * -> *) t m a.
(Witherable f, Ord t) =>
[t] -> f (Event t m a) -> Maybe (Event t m a)
lastOccurrenceOfTag [t]
x =
  forall (f :: * -> *) t m a.
Filterable f =>
(f (Event t m a) -> Maybe (Event t m a))
-> Predicate (Event t m a)
-> f (Event t m a)
-> Maybe (Event t m a)
findOccurrenceOfEvent (forall a. [a] -> Maybe a
lastMay forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) a. Foldable t => t a -> [a]
toList) (forall t m a. Ord t => [t] -> Predicate (Event t m a)
containsTag [t]
x)

{-|
Split a container of @'Event'@s
into a pair of @'Event'@s.
The first element contains
events have any of the tags in the first argument,
similarly for the second element.

Note that one or both of the resulting containers
may be empty.
-}
splitByTags
  :: (W.Filterable f, Ord t)
  => [t]
  -> [t]
  -> f (Event t m a)
  -> (f (Event t m a), f (Event t m a))
splitByTags :: forall (f :: * -> *) t m a.
(Filterable f, Ord t) =>
[t] -> [t] -> f (Event t m a) -> (f (Event t m a), f (Event t m a))
splitByTags [t]
t1 [t]
t2 f (Event t m a)
es =
  (forall (f :: * -> *) t m a.
Filterable f =>
Predicate (Event t m a) -> f (Event t m a) -> f (Event t m a)
filterEvents (forall t m a. Ord t => [t] -> Predicate (Event t m a)
containsTag [t]
t1) f (Event t m a)
es, forall (f :: * -> *) t m a.
Filterable f =>
Predicate (Event t m a) -> f (Event t m a) -> f (Event t m a)
filterEvents (forall t m a. Ord t => [t] -> Predicate (Event t m a)
containsTag [t]
t2) f (Event t m a)
es)

{-|
Tally the number of events in a container satisfying the given predicate.
-}
tallyEvents
  :: (W.Witherable f) => Predicate (Event t m a) -> f (Event t m a) -> Int
tallyEvents :: forall (f :: * -> *) t m a.
Witherable f =>
Predicate (Event t m a) -> f (Event t m a) -> Int
tallyEvents Predicate (Event t m a)
p = forall (t :: * -> *) a. Foldable t => t a -> Int
length forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) t m a.
Filterable f =>
Predicate (Event t m a) -> f (Event t m a) -> f (Event t m a)
filterEvents Predicate (Event t m a)
p