{-|
Module      : Hasklepias Feature building functions 
Description : Functions for composing features. 
Copyright   : (c) NoviSci, Inc 2020
License     : BSD3
Maintainer  : bsaul@novisci.com

Provides functions used in defining 'Feature's.
-}

{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE Safe #-}

module Hasklepias.Functions(

    -- * Container predicates
      isNotEmpty
    , atleastNofX
    , twoXOrOneY

    -- * Finding occurrences of concepts
    , nthConceptOccurrence
    , firstConceptOccurrence

    -- * Reshaping containers
    , allPairs
    , splitByConcepts

    -- * Create filters
    , makeConceptsFilter
    , makePairedFilter
) where

import Data.Text                            ( Text )
import Control.Applicative                  ( Applicative(liftA2) )
import IntervalAlgebra                      ( Intervallic(..)
                                            , ComparativePredicateOf1
                                            , ComparativePredicateOf2
                                            , Interval )
import IntervalAlgebra.PairedInterval       ( PairedInterval, getPairData )
-- import IntervalAlgebra.IntervalUtilities    ( compareIntervals )
import Hasklepias.Types.Event               ( Events
                                            , Event
                                            , ConceptEvent
                                            , ctxt )
import Hasklepias.Types.Context             ( Concept
                                            , Concepts
                                            , Context
                                            , HasConcept( hasConcepts ) )
import Safe                                 ( headMay, lastMay ) 
import safe Data.Bool                       ( Bool, (&&), not, (||) )
import safe Data.Function                   ( (.), ($) )
import safe Data.Int                        ( Int )
import safe Data.List                       ( filter, length, null )
import safe Data.Maybe                      ( Maybe(..) )
import safe Data.Ord                        ( Ord((>=)) )


-- | Is the input list empty? 
isNotEmpty :: [a] -> Bool
isNotEmpty :: [a] -> Bool
isNotEmpty = Bool -> Bool
not(Bool -> Bool) -> ([a] -> Bool) -> [a] -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
.[a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null

-- | Filter 'Events' to those that have any of the provided concepts.
makeConceptsFilter ::
       [Text]    -- ^ the list of concepts by which to filter 
    -> Events a
    -> Events a
makeConceptsFilter :: [Text] -> Events a -> Events a
makeConceptsFilter [Text]
cpts = (Event a -> Bool) -> Events a -> Events a
forall a. (a -> Bool) -> [a] -> [a]
filter (Event a -> [Text] -> Bool
forall a. HasConcept a => a -> [Text] -> Bool
`hasConcepts` [Text]
cpts)

-- | Filter 'Events' to a single @'Maybe' 'Event'@, based on a provided function,
--   with the provided concepts. For example, see 'firstConceptOccurrence' and
--  'lastConceptOccurrence'.
nthConceptOccurrence ::
       (Events a -> Maybe (Event a)) -- ^ function used to select a single event
    -> [Text]
    -> Events a
    -> Maybe (Event a)
nthConceptOccurrence :: (Events a -> Maybe (Event a))
-> [Text] -> Events a -> Maybe (Event a)
nthConceptOccurrence Events a -> Maybe (Event a)
f [Text]
c = Events a -> Maybe (Event a)
f(Events a -> Maybe (Event a))
-> (Events a -> Events a) -> Events a -> Maybe (Event a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
.[Text] -> Events a -> Events a
forall a. [Text] -> Events a -> Events a
makeConceptsFilter [Text]
c

-- | Finds the *first* occurrence of an 'Event' with at least one of the concepts.
--   Assumes the input 'Events' list is appropriately sorted.
firstConceptOccurrence ::
      [Text]
    -> Events a
    -> Maybe (Event a)
firstConceptOccurrence :: [Text] -> Events a -> Maybe (Event a)
firstConceptOccurrence = (Events a -> Maybe (Event a))
-> [Text] -> Events a -> Maybe (Event a)
forall a.
(Events a -> Maybe (Event a))
-> [Text] -> Events a -> Maybe (Event a)
nthConceptOccurrence Events a -> Maybe (Event a)
forall a. [a] -> Maybe a
headMay

-- | Finds the *last* occurrence of an 'Event' with at least one of the concepts.
--   Assumes the input 'Events' list is appropriately sorted.
lastConceptOccurrence ::
      [Text]
    -> Events a
    -> Maybe (Event a)
lastConceptOccurrence :: [Text] -> Events a -> Maybe (Event a)
lastConceptOccurrence = (Events a -> Maybe (Event a))
-> [Text] -> Events a -> Maybe (Event a)
forall a.
(Events a -> Maybe (Event a))
-> [Text] -> Events a -> Maybe (Event a)
nthConceptOccurrence Events a -> Maybe (Event a)
forall a. [a] -> Maybe a
lastMay

-- | Does 'Events' have at least @n@ events with any of the Concept in @x@.
atleastNofX ::
      Int -- ^ n
   -> [Text] -- ^ x
   -> Events a -> Bool
atleastNofX :: Int -> [Text] -> Events a -> Bool
atleastNofX Int
n [Text]
x Events a
es = Events a -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([Text] -> Events a -> Events a
forall a. [Text] -> Events a -> Events a
makeConceptsFilter [Text]
x Events a
es) Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
n

-- | TODO
twoXOrOneY :: [Text] -> [Text] -> Events a -> Bool
twoXOrOneY :: [Text] -> [Text] -> Events a -> Bool
twoXOrOneY [Text]
x [Text]
y Events a
es = Int -> [Text] -> Events a -> Bool
forall a. Int -> [Text] -> Events a -> Bool
atleastNofX Int
2 [Text]
x Events a
es Bool -> Bool -> Bool
||
                    Int -> [Text] -> Events a -> Bool
forall a. Int -> [Text] -> Events a -> Bool
atleastNofX Int
1 [Text]
y Events a
es

-- | Takes a predicate of intervals and a predicate on the data part of a 
--   paired interval to create a single predicate such that both input
--   predicates should hold.
makePairPredicate :: ( Intervallic (PairedInterval b) a
                     , Intervallic i0 a) =>
       ComparativePredicateOf2 (i0 a) ((PairedInterval b) a)
    -> i0 a
    -> (b -> Bool)
    -> (PairedInterval b a -> Bool)
makePairPredicate :: ComparativePredicateOf2 (i0 a) (PairedInterval b a)
-> i0 a -> (b -> Bool) -> PairedInterval b a -> Bool
makePairPredicate ComparativePredicateOf2 (i0 a) (PairedInterval b a)
pi i0 a
i b -> Bool
pd PairedInterval b a
x =  ComparativePredicateOf2 (i0 a) (PairedInterval b a)
pi i0 a
i PairedInterval b a
x Bool -> Bool -> Bool
&& b -> Bool
pd (PairedInterval b a -> b
forall b a. PairedInterval b a -> b
getPairData PairedInterval b a
x)

-- | 
makePairedFilter :: ( Intervallic i0 a
                    , Intervallic (PairedInterval b) a) =>
       ComparativePredicateOf2 (i0 a) ((PairedInterval b) a)
    -> i0 a
    -> (b -> Bool)
    -> [PairedInterval b a]
    -> [PairedInterval b a]
makePairedFilter :: ComparativePredicateOf2 (i0 a) (PairedInterval b a)
-> i0 a
-> (b -> Bool)
-> [PairedInterval b a]
-> [PairedInterval b a]
makePairedFilter ComparativePredicateOf2 (i0 a) (PairedInterval b a)
fi i0 a
i b -> Bool
fc = (PairedInterval b a -> Bool)
-> [PairedInterval b a] -> [PairedInterval b a]
forall a. (a -> Bool) -> [a] -> [a]
filter (ComparativePredicateOf2 (i0 a) (PairedInterval b a)
-> i0 a -> (b -> Bool) -> PairedInterval b a -> Bool
forall b a (i0 :: * -> *).
(Intervallic (PairedInterval b) a, Intervallic i0 a) =>
ComparativePredicateOf2 (i0 a) (PairedInterval b a)
-> i0 a -> (b -> Bool) -> PairedInterval b a -> Bool
makePairPredicate ComparativePredicateOf2 (i0 a) (PairedInterval b a)
fi i0 a
i b -> Bool
fc)

-- | Generate all pair-wise combinations from two lists.
allPairs :: [a] -> [a] -> [(a, a)]
allPairs :: [a] -> [a] -> [(a, a)]
allPairs = (a -> a -> (a, a)) -> [a] -> [a] -> [(a, a)]
forall (f :: * -> *) a b c.
Applicative f =>
(a -> b -> c) -> f a -> f b -> f c
liftA2 (,)

-- | Split an @Events a@ into a pair of @Events a@. The first element contains
--   events have any of the concepts in the first argument, similarly for the
--   second element.
splitByConcepts :: [Text] 
        -> [Text]
        -> Events a
        -> (Events a, Events a)
splitByConcepts :: [Text] -> [Text] -> Events a -> (Events a, Events a)
splitByConcepts [Text]
c1 [Text]
c2 Events a
es = ( (Event a -> Bool) -> Events a -> Events a
forall a. (a -> Bool) -> [a] -> [a]
filter (Event a -> [Text] -> Bool
forall a. HasConcept a => a -> [Text] -> Bool
`hasConcepts` [Text]
c1) Events a
es
                           , (Event a -> Bool) -> Events a -> Events a
forall a. (a -> Bool) -> [a] -> [a]
filter (Event a -> [Text] -> Bool
forall a. HasConcept a => a -> [Text] -> Bool
`hasConcepts` [Text]
c2) Events a
es)