{-# LANGUAGE CPP #-}

-- | The core types of Fixplate.
module Data.Generics.Fixplate.Base where

--------------------------------------------------------------------------------

import Control.Applicative
import Control.Monad ( liftM , ap )
import Data.Foldable
import Data.Traversable
import Prelude hiding ( foldl , foldr , mapM , mapM_ , concat , concatMap )

import Text.Show ()
import Text.Read

import Data.Generics.Fixplate.Misc

--------------------------------------------------------------------------------

-- | The fixed-point type.
newtype Mu f = Fix { unFix :: f (Mu f) }

-- | We call a tree \"atomic\" if it has no subtrees.
isAtom :: Foldable f => Mu f -> Bool
isAtom = null . toList . unFix

--------------------------------------------------------------------------------
-- * Annotations

-- | Type of annotations
data Ann f a b = Ann 
  { attr  :: a           -- ^ the annotation
  , unAnn :: f b         -- ^ the original functor
  }
  deriving (Eq,Ord,Show)

-- | Annotated fixed-point type. Equivalent to @CoFree f a@
type Attr f a = Mu (Ann f a)

-- | Lifting natural transformations to annotations.
liftAnn :: (f e -> g e) -> Ann f a e -> Ann g a e
liftAnn trafo (Ann a x) = Ann a (trafo x)

--------------------------------------------------------------------------------
-- * Co-annotations

-- | Categorical dual of 'Ann'.
data CoAnn f a b 
  = Pure a 
  | CoAnn (f b)
  deriving (Eq,Ord,Show)

-- | Categorical dual of 'Attr'. Equivalent to @Free f a@
type CoAttr f a = Mu (CoAnn f a)

-- | Lifting natural transformations to annotations.
liftCoAnn :: (f e -> g e) -> CoAnn f a e -> CoAnn g a e
liftCoAnn trafo x = case x of
  Pure  x -> Pure  x
  CoAnn t -> CoAnn (trafo t)

--------------------------------------------------------------------------------
-- * Annotated trees

-- | The attribute of the root node.
attribute :: Attr f a -> a
attribute = attr . unFix
  
-- | A function forgetting all the attributes from an annotated tree.
forget :: Functor f => Attr f a -> Mu f
forget = Fix . fmap forget . unAnn . unFix

--------------------------------------------------------------------------------
-- * Holes

-- | This a data type defined to be a place-holder for childs.
-- It is used in tree drawing, hashing, and 'Shape'.
-- 
-- It is deliberately not made an instance of 'Show', so that 
-- you can choose your preferred style. For example, an acceptable choice is
--
-- > instance Show Hole where show _ = "_"
--
data Hole = Hole deriving (Eq,Ord)

--------------------------------------------------------------------------------
-- * Higher-order type classes

-- | \"Functorised\" versions of standard type classes. 
-- If you have your a structure functor, for example
--
-- > Expr e 
-- >   = Kst Int 
-- >   | Var String 
-- >   | Add e e 
-- >   deriving (Eq,Ord,Read,Show,Functor,Foldable,Traversable)
--
-- you should make it an instance of these, so that the 
-- fixed-point type @Mu Expr@ can be an instance of
-- @Eq@, @Ord@ and @Show@. Doing so is very easy:
--
-- > instance EqF   Expr where equalF     = (==)
-- > instance OrdF  Expr where compareF   = compare
-- > instance ShowF Expr where showsPrecF = showsPrec
--
-- The @Read@ instance depends on whether we are using GHC or not.
-- The Haskell98 version is
--
-- > instance ReadF Expr where readsPrecF = readsPrec
--
-- while the GHC version is
--
-- > instance ReadF Expr where readPrecF  = readPrec
--
class          EqF   f  where equalF     ::  Eq   a  =>  f a -> f a -> Bool
class EqF f => OrdF  f  where compareF   ::  Ord  a  =>  f a -> f a -> Ordering
class          ShowF f  where showsPrecF ::  Show a  =>  Int -> f a -> ShowS
class          ReadF f  where 
#ifdef __GLASGOW_HASKELL__
                              readPrecF  ::  Read a  =>  ReadPrec (f a)
#else
                              readsPrecF ::  Read a  =>  Int -> ReadS (f a)                              
#endif                        

showF :: (ShowF f, Show a) => f a -> String
showF x = showsF x ""

showsF :: (ShowF f, Show a) => f a -> ShowS
showsF = showsPrecF 0
      
--------------------------------------------------------------------------------

instance EqF f => Eq (Mu f) where Fix x == Fix y = equalF x y
instance OrdF   f  => Ord   (Mu f) where compare (Fix x) (Fix y)  = compareF x y
instance ShowF  f  => Show  (Mu f) where 
  showsPrec d (Fix x) = showParen (d>app_prec) 
    $ showString "Fix " 
    . showsPrecF (app_prec+1) x

instance ReadF  f  => Read  (Mu f) where     
#ifdef __GLASGOW_HASKELL__
  readPrec = parens $ 
    (prec app_prec $ do
      { Ident "Fix" <- lexP
      ; m <- step readPrecF
      ; return (Fix m)            
      })
#else                                  
  readsPrec d r = readParen (d > app_prec)
     (\r -> [ (Fix m, t) 
            | ("Fix", s) <- lex r
            , (m,t) <- readsPrecF (app_prec+1) s]) r    
#endif
            
--------------------------------------------------------------------------------

-- | NOTE: The 'EqF' instance for annotations compares both the annotations and the original part. 
instance (Eq a, EqF f) => EqF (Ann f a) where 
  equalF (Ann a x) (Ann b y) = a == b && equalF x y

-- | NOTE: The 'OrdF' instance for annotations first compares the /annotations/, and then 
-- the functor part. If this is not the desired behaviour (it's not clear to me at the moment
-- what is the good default here), you can use the standard newtype trick to define a new
-- behaviour.
instance (Ord a, OrdF f) => OrdF (Ann f a) where 
  compareF (Ann a x) (Ann b y) = case compare a b of
    LT -> LT
    GT -> GT
    EQ -> compareF x y
  
instance (Show a, ShowF f) => ShowF (Ann f a) where 
  showsPrecF d (Ann a t) 
    = showParen (d>app_prec) 
      $ showString "Ann " 
      . (showsPrec (app_prec+1) a) 
      . showChar ' ' 
      . (showsPrecF (app_prec+1) t)    

instance (Read a, ReadF f) => ReadF (Ann f a) where 
#ifdef __GLASGOW_HASKELL__
  readPrecF = parens $ 
    (prec app_prec $ do
      { Ident "Ann" <- lexP
      ; x <- step readPrec
      ; m <- step readPrecF
      ; return (Ann x m)            
      })
#else                                  
  readsPrecF d r = readParen (d > app_prec)
     (\r -> [ (Ann x m, u) 
            | ("Ann", s) <- lex r
            , (x,t) <- readsPrec  (app_prec+1) s]) r    
            , (m,u) <- readsPrecF (app_prec+1) t]) r    
#endif

--------------------------------------------------------------------------------

instance (Eq a, EqF f) => EqF (CoAnn f a) where 
  equalF (Pure  a) (Pure  b) = a == b
  equalF (CoAnn x) (CoAnn y) = equalF x y
  equalF _         _         = False

instance (Ord a, OrdF f) => OrdF (CoAnn f a) where 
  compareF (Pure  a) (Pure  b) = compare a b
  compareF (CoAnn x) (CoAnn y) = compareF x y
  compareF (Pure  _) (CoAnn _) = LT
  compareF (CoAnn _) (Pure  _) = GT

instance (Show a, ShowF f) => ShowF (CoAnn f a) where 
  showsPrecF d (CoAnn t) 
    = showParen (d>app_prec) 
      $ showString "CoAnn " 
      . (showsPrecF (app_prec+1) t)    
  showsPrecF d (Pure x) 
    = showParen (d>app_prec) 
      $ showString "Pure " 
      . (showsPrec (app_prec+1) x) 
      
--------------------------------------------------------------------------------

instance Functor f => Functor (Ann f a) where
  fmap f (Ann attr t) = Ann attr (fmap f t)

instance Foldable f => Foldable (Ann f a) where
  foldl f x (Ann _ t) = foldl f x t
  foldr f x (Ann _ t) = foldr f x t

instance Traversable f => Traversable (Ann f a) where
  traverse f (Ann x t) = Ann x <$> traverse f t
  mapM f (Ann x t) = liftM (Ann x) (mapM f t)

--------------------------------------------------------------------------------
 
instance Functor f => Functor (CoAnn f a) where
  fmap f (CoAnn t) = CoAnn (fmap f t)
  fmap f (Pure  x) = Pure  x

instance Foldable f => Foldable (CoAnn f a) where
  foldl f a (CoAnn t) = foldl f a t
  foldl f a (Pure  x) = a

  foldr f a (CoAnn t) = foldr f a t
  foldr f a (Pure  x) = a

instance Traversable f => Traversable (CoAnn f a) where
  traverse f (CoAnn t) = CoAnn <$> traverse f t
  traverse f (Pure  x) = pure (Pure x)

  mapM f (CoAnn t) = liftM CoAnn (mapM f t)
  mapM f (Pure  x) = return (Pure x)
 
--------------------------------------------------------------------------------
-- * Attrib (cofree comonad)

-- | A newtype wrapper around @Attr f a@ so that we can make @Attr f@ 
-- an instance of Functor, Foldable and Traversable (and Comonad). This is necessary
-- since Haskell does not allow partial application of type synonyms.
--
-- Equivalent to the co-free comonad.
newtype Attrib f a = Attrib { unAttrib :: Attr f a }

instance (ShowF f, Show a) => Show (Attrib f a) where
  showsPrec d (Attrib x) 
    = showParen (d>app_prec) 
      $ showString "Attrib " 
      . (showsPrec (app_prec+1) x) 

instance Functor f => Functor (Attrib f) where
  fmap h (Attrib y) = Attrib (go y) where
    go (Fix (Ann x t)) = Fix $ Ann (h x) (fmap go t)

instance Foldable f => Foldable (Attrib f) where
  foldl h a (Attrib y) = go a y where go b (Fix (Ann x t)) = foldl go (h b x) t
  foldr h a (Attrib y) = go y a where go (Fix (Ann x t)) b = h x (foldr go b t)

instance Traversable f => Traversable (Attrib f) where
  traverse h (Attrib y) = Attrib <$> go y where
    go (Fix (Ann x t)) = Fix <$> (Ann <$> h x <*> traverse go t)

--------------------------------------------------------------------------------
-- * CoAttrib (free monad)

-- | Categorial dual of 'Attrib'. Equivalent to the free monad.
newtype CoAttrib f a = CoAttrib { unCoAttrib :: CoAttr f a }

instance (ShowF f, Show a) => Show (CoAttrib f a) where
  showsPrec d (CoAttrib x) 
    = showParen (d>app_prec) 
      $ showString "CoAttrib " 
      . (showsPrec (app_prec+1) x) 

instance Functor f => Functor (CoAttrib f) where
  fmap h (CoAttrib y) = CoAttrib (go y) where
    go (Fix (CoAnn t)) = Fix $ CoAnn (fmap go t)
    go (Fix (Pure  x)) = Fix $ Pure  (h x)

instance Foldable f => Foldable (CoAttrib f) where
  foldl h a (CoAttrib y) = go a y where 
     go b (Fix (CoAnn t)) = foldl go b t
     go b (Fix (Pure  x)) = h b x
  foldr h a (CoAttrib y) = go y a where 
     go (Fix (CoAnn t)) b = foldr go b t
     go (Fix (Pure  x)) b = h x b 

instance Traversable f => Traversable (CoAttrib f) where
  traverse h (CoAttrib y) = CoAttrib <$> go y where
    go (Fix (CoAnn t)) = Fix <$> (CoAnn <$> traverse go t)
    go (Fix (Pure  x)) = Fix <$> (Pure  <$> h x)

instance Functor f => Applicative (CoAttrib f) where
  pure x = CoAttrib (Fix (Pure x))
  (<*>)  = ap

instance Functor f => Monad (CoAttrib f) where
  return x = CoAttrib (Fix (Pure x))
  CoAttrib (Fix (CoAnn t))  >>=  u  =  CoAttrib (Fix (CoAnn (fmap (unCoAttrib . (>>=u) . CoAttrib) t)))
  CoAttrib (Fix (Pure  x))  >>=  u  =  u x


--------------------------------------------------------------------------------