{-# LANGUAGE ExistentialQuantification,
             ScopedTypeVariables #-}
-- | This module contains the combinators for building permutation phrases as described in. 
-- They differ from the version found in Control.Applicative in that elements may recognise the empty string too. 
-- In addition we provide a combinator which allows separators between the elements of the permutation.
-- For an example of their use see the end of the @`main`@ function in "Text.ParserCombinators.UU.Examples"
--
-- @
--      \@article{1030338,
--	Address = {New York, NY, USA},
--	Author = {Baars, Arthur I. and L{\"o}h, Andres and Swierstra, S. Doaitse},
--	Date-Modified = {2008-12-01 21:44:00 +0100},
--	Doi = {http://dx.doi.org/10.1017/S0956796804005143},
--	Issn = {0956-7968},
--	Journal = {J. Funct. Program.},
--	Number = {6},
--	Pages = {635--646},
--	Publisher = {Cambridge University Press},
--	Title = {Parsing permutation phrases},
--	Volume = {14},
--	Year = {2004}}
-- @
--

module Text.ParserCombinators.UU.Perms(Perms(), pPerms, pPermsSep, succeedPerms, (~*~), (~$~)) where
import Text.ParserCombinators.UU.Core
import Data.Maybe

-- =======================================================================================
-- ===== PERMUTATIONS ================================================================
-- =======================================================================================

newtype Perms st a = Perms (Maybe (P st a), [Br st a])
data Br st a = forall b. Br (Perms st (b -> a)) (P st b)

instance Functor (Perms st) where
  fmap f (Perms (ma, brs)) = Perms (fmap (f <$>) ma, (map (fmap f) brs))

instance  Functor (Br st) where
  fmap f (Br perm p) = Br (fmap (f.) perm) p 

(~*~) ::  Perms st (a -> b) -> P st a -> Perms st b
perms ~*~ p = perms `add` (getZeroP p, getOneP p)

(~$~) ::  (a -> b) -> P st a -> Perms st b
f     ~$~ p = succeedPerms f ~*~ p

succeedPerms ::  a -> Perms st a
succeedPerms x = Perms (Just (pure x), []) 

add ::  Perms st (a -> b) -> (Maybe (P st a),Maybe (P st a)) -> Perms st b
add b2a@(Perms (eb2a, nb2a)) bp@(eb, nb)
 =  let changing ::  (a -> b) -> Perms st a -> Perms st b
        f `changing` Perms (ep, np) = Perms (fmap (f <$>) ep, [Br ((f.) `changing` pp) p | Br pp p <- np])
    in Perms
      ( do { f <- eb2a
           ; x <- eb
           ; return (f <*>  x)
           }
      ,  (case nb of
          Nothing     -> id
          Just pb     -> (Br b2a  pb:)
        )[ Br ((flip `changing` c) `add`  bp) d |  Br c d <- nb2a]
      )

pPerms ::  Perms st a -> P st a 
pPerms (Perms (empty,nonempty))
 = foldl (<|>) (fromMaybe pFail empty) [ (flip ($)) <$> p <*> pPerms pp
                                       | Br pp  p <- nonempty
                                       ]

pPermsSep ::  P st x -> Perms st a -> P st a
pPermsSep (sep :: P st z) perm = p2p (pure ()) perm
 where  p2p :: P st x -> Perms st a -> P st a
        p2p fsep (Perms (mbempty, nonempties)) = 
                let empty          = fromMaybe  pFail mbempty
                    pars (Br t p)  = flip ($) <$ fsep <*> p <*> p2p sep t
                in foldr (<|>) empty (map pars nonempties)              
        p2p_sep =  p2p sep 

pFail :: P st a
pFail = empty