{-# LANGUAGE TypeFamilies #-}
-----------------------------------------------------------------------------
-- Copyright 2016, Ideas project team. This file is distributed under the
-- terms of the Apache License 2.0. For more information, see the files
-- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer  :  bastiaan.heeren@ou.nl
-- Stability   :  provisional
-- Portability :  portable (depends on ghc)
--
-- Processes support choices and sequences and are modelled after Hoare's CSP
-- calculus.
--
-----------------------------------------------------------------------------

module Ideas.Common.Strategy.Process
   ( Process, eqProcessBy, menu, withMenu
   , fold, runProcess
   ) where

import Ideas.Common.Classes
import Ideas.Common.Strategy.Choice
import Ideas.Common.Strategy.Sequence

------------------------------------------------------------------------
-- Process data type

-- | Process data type with efficient support for sequences
newtype Process a = P [Menu a (Process a)]

instance Eq a => Eq (Process a) where
   (==) = eqProcessBy (==)

instance Functor Process where
   fmap f = rec
    where
      rec (P xs) = P (map g xs)
      g = onMenu (\a q -> f a |-> rec q) doneMenu

instance Choice (Process a) where
   empty   = P [empty]
   x .|. y = P [menu x .|. menu y]
   x ./. y = P [menu x ./. menu y]
   x |>  y = P [menu x |>  menu y]

instance Sequence (Process a) where
   type Sym (Process a) = a

   done = P []
   a ~> b = P [a |-> b]
   P xs .*. P ys = P (xs ++ ys)
   sequence ps = P [ x | P xs <- ps, x <- xs ]

instance Fix (Process a)

instance Firsts (Process a) where
   type Elem (Process a) = a

   firsts = bests . menu
   ready  = hasDone . menu

runProcess :: Apply f => Process (f a) -> a -> [a]
runProcess p a = withMenu op [a] p
 where
   op f x = [ c | b <- applyAll f a, c <- runProcess x b ]

menu :: Process a -> Menu a (Process a)
menu (P zs) = rec zs
 where
   rec []     = doneMenu
   rec [x]    = x
   rec (x:xs) = onMenu (\a (P ys) -> a |-> P (ys ++ xs)) (rec xs) x

withMenu :: Choice b => (a -> Process a -> b) -> b -> Process a -> b
withMenu op e (P zs) = rec zs
 where
   rec []     = e
   rec [x]    = onMenu op e x
   rec (x:xs) = onMenu (\a (P ys) -> op a (P (ys ++ xs))) (rec xs) x

-- | Generalized equality of processes, which takes an equality function for
-- the symbols.
eqProcessBy :: (a -> a -> Bool) -> Process a -> Process a -> Bool
eqProcessBy eq = rec
 where
   rec p q = eqMenuBy eq rec (menu p) (menu q)

fold :: Choice b => (a -> b -> b) -> b -> Process a -> b
fold op e = rec
 where
   rec = withMenu (\a -> op a . rec) e