module Penny.Lincoln.Family.Family where

import Control.Applicative ((<$>), (<*>), pure, Applicative)
import qualified Data.List as L
import Data.Traversable (traverse)

-- | A Family has one parent (ah, the anomie, sorry) and at least two
-- children.
data Family p c =
  Family { parent :: p
         , child1 :: c
         , child2 :: c
         , children :: [c] }
  deriving (Eq, Show)

-- | Maps over all children, in order starting with child
-- 1, then child 2, then the children in the list from left to right.
mapChildrenA ::
  Applicative m
  => (a -> m b)
  -> Family p a
  -> m (Family p b)
mapChildrenA f (Family p c1 c2 cs) =
  Family p <$> f c1 <*> f c2 <*> traverse f cs


-- | Maps over all children.
mapChildren ::
  (a -> b)
  -> Family p a
  -> Family p b
mapChildren f (Family p c1 c2 cs) =
  Family p (f c1) (f c2) (map f cs)


-- | Maps over the parent in an Applicative.
mapParentA ::
  Applicative m
  => (a -> m b)
  -> Family a c
  -> m (Family b c)
mapParentA f (Family p c1 c2 cs) =
  Family <$> f p <*> pure c1 <*> pure c2 <*> pure cs


-- | Maps over the parent.
mapParent :: (a -> b) -> Family a c -> Family b c
mapParent f (Family p c1 c2 cs) = Family (f p) c1 c2 cs


-- | Finds the first child matching a predicate.
find :: (p -> c -> Bool) -> Family p c -> Maybe c
find f (Family p c1 c2 cs)
  | f p c1 = Just c1
  | f p c2 = Just c2
  | otherwise = L.find (f p) cs

-- | Filters the children. Fails if there are not at least two
-- children after filtering. Retains the original order of the
-- children (after removing the children you don't want.)
filterChildren :: (a -> Bool) -> Family p a -> Maybe (Family p a)
filterChildren f (Family p c1 c2 cs) =
  case (f c1, f c2) of
    (True, True) -> Just (Family p c1 c2 (filter f cs))
    (True, False) ->
      case filter f cs of
        [] -> Nothing
        x:xs -> Just (Family p c1 x xs)
    (False, True) ->
      case filter f cs of
        [] -> Nothing
        x:xs -> Just (Family p c2 x xs)
    (False, False) ->
      case filter f cs of
        x1:x2:xs -> Just (Family p x1 x2 xs)
        _ -> Nothing