{-# Language GADTs, ScopedTypeVariables #-}
module Codec.Phaser.Permutation (
  Permutable,
  runPermutable,
  term
 ) where

import Codec.Phaser.Core
import Control.Applicative

data Permutable p c t a where
  Term :: Phase p c t a -> Permutable p c t a
  Filled :: a -> Permutable p c t a
  (:<*>) :: Permutable p c t (a -> b) -> Permutable p c t a -> Permutable p c t b

instance Functor (Permutable p c t) where
  fmap f (Term p) = Term (fmap f p)
  fmap f (Filled a) = Filled (f a)
  fmap f (l :<*> r) = (fmap . fmap) f l :<*> r

instance Applicative (Permutable p c t) where
  pure = Filled
  (<*>) = (:<*>)

-- | Create a 'Phase' which runs the constituent terms of the 'Permutable'
-- in every order in which they succeed, running a separator 'Phase' between
-- each term which consumes input.
runPermutable :: forall p c t a b . Monoid p => Phase p c t b -> Permutable p c t a -> Phase p c t a
runPermutable sep = go0 where
  go0 p = resolve p <|> (fill1 p >>= go)
  go p = resolve p <|> (sep >> fill1 p >>= go)
  resolve :: Permutable p c t x -> Phase p c t x
  resolve (Term p) = fromAutomaton $ starve $ toAutomaton p
  resolve (Filled a) = pure a
  resolve (l :<*> r) = resolve l <*> resolve r
  fill1 :: Permutable p c t x -> Phase p c t (Permutable p c t x)
  fill1 (Term p) = Filled <$> p
  fill1 (Filled _) = empty
  fill1 (l :<*> r) = (flip simplify r <$> fill1 l) <|> (simplify l <$> fill1 r)
  simplify (Filled l) r = fmap l r
  simplify l  (Filled r) = fmap ($ r) l
  simplify l r = l :<*> r

term :: Phase p c t a -> Permutable p c t a
term = Term