{- | Values implemented named terms with explicit substitutions.
-}

{-# LANGUAGE FlexibleContexts, FlexibleInstances, TypeSynonymInstances, MultiParamTypeClasses,
    OverlappingInstances, IncoherentInstances, UndecidableInstances,
    PatternGuards, TupleSections #-}

module NamedExplSubst where

import Prelude hiding (pi,abs,mapM)

import Control.Applicative
import Control.Monad.Reader hiding (mapM)

{-
import Data.Map (Map)
import qualified Data.Map as Map
-}
import Data.Traversable

import qualified Abstract as A
import qualified ListEnv as Env
-- import qualified MapEnv as Env
import Signature
import Util
import Value
import Fresh

-- * Values

type Var = A.UID

-- | Heads are identifiers excluding @A.Def@.
type Head = A.Ident

data Val
  = Ne   Head       Val [Val]        -- ^ @x^a vs^-1 | c^a vs^-1@
  | Df   A.Name Val Val [Val]        -- ^ @d=v^a vs^-1@  a,v are closed!
  | App  Val [Val]                   -- ^ @v vs^-1@ non-canonical
                                     --   last argument first in list!
  | Sort Sort                        -- ^ @s@
  | K    Val                         -- ^ constant function
  | Abs  A.Name Val                  -- ^ @\xv@ abstraction
  | Fun  Val  Val                    -- ^ @Pi a b@
  | Clos Val  Env                    -- ^ @v[rho]@
  | DontCare

-- * Environments (Values for expression (=bound) variables)

type Env = Env.Env Var Val

-- * Application, Substitution

-- | @app f v@  computes the whnf of @f v@ without expanding definitions
app :: Val -> Val -> Val
app f v =
  case f of
    K w                  -> w
    Ne h t vs            -> Ne h t (v:vs)
    Df h w t vs          -> Df h w t (v:vs)
    App w ws             -> rapps w (v:ws) -- evaluate apps
    Abs x w              -> substFree v (A.uid x) w
    Clos (Abs x w) sigma -> substs (Env.update sigma (A.uid x) v) w
    Clos w sigma         -> substs sigma w `app` v

rapps :: Val -> [Val] -> Val
rapps f vs = foldr (flip app) f vs

-- | @substFree v x w = [v/x]w@ single substitution
substFree :: Val -> Var -> Val -> Val
substFree w x = substs (Env.singleton x w)

-- | parallel substitution, computing whnf
substs :: Env -> Val -> Val
substs sigma = subst where
  subst (Ne h@(A.Var y) a vs) = case Env.lookup (A.uid y) sigma of
    Just w  -> rapps w $ map subst vs
    Nothing -> Ne h (subst a) $ map subst vs
  subst (Ne h a vs)    = Ne h a $ map subst vs    -- a is closed
  subst (Df h v a vs)  = Df h v a $ map subst vs  -- a,v are closed
  subst (App v vs)     = rapps (subst v) (map subst vs)
    -- subst (rapps v vs) -- OR: first compute application ?
  subst (Sort s)       = Sort s
  subst (K v)          = K $ subst v
  subst (Abs x v)      = Clos (Abs x v) sigma
  subst (Clos v tau)   = flip substs v $ flip Env.union sigma $ Env.map subst tau
    -- composing two substitutions (first tau, then sigma) :
    --   apply sigma to tau
    --   add all bindings from sigma that are not yet present
    --   thus, we can take sigma and overwrite it with [sigma]tau
  subst (Fun a b)      = Fun (subst a) $ subst b

-- | computing the whnf of a term, pushing a delayed substitution in
whnf :: Val -> Val
whnf (Clos v rho) = substs rho v
whnf (App f vs)   = rapps f vs
whnf v            = v


-- * Smart Constructors for values.

var :: A.Name -> Val -> Val
var x t = Ne (A.Var x) t []

var_ :: A.Name -> Val
var_ x = var x DontCare

con :: A.Name -> Val -> Val
con x t = Ne (A.Con x) t []

def :: A.Name -> Val -> Val -> Val
def x v t = Df x v t []

-- non-computing application
application :: Val -> Val -> Val
application f v =
  case f of
    Ne h   t vs -> Ne h   t (v:vs)
    Df x w t vs -> Df x w t (v:vs)
    App  w   vs -> App  w   (v:vs)
    K w         -> w               -- because K comes from non-dep fun types
    _           -> App f [v]

-- * projections

boundName :: Val -> A.Name
boundName (Abs n _) = n
boundName _ = A.noName

-- * Translation

-- | @translate e rho = v@ where @rho@ is a renaming.
translate :: (Applicative m, Monad m, Signature Val sig, MonadReader sig m, MonadFresh m) =>
  A.Expr -> Renaming -> m Val
translate e rho =
    case e of
      A.Ident (A.Con x) -> con x . symbType . sigLookup' (A.uid x) <$> ask
      A.Ident (A.Def x) -> do
        ~(SigDef t v) <- sigLookup' (A.uid x) <$> ask
        return $ def x v t
      A.Ident (A.Var x) -> return $ var_ $ Env.lookupSafe (A.uid x) rho
      A.App f e    -> application <$> (evaluate f rho) <*> (evaluate e rho)
      A.Lam x mt e -> do y <- fresh x
                         Abs y <$> translate e (Env.update rho (A.uid x) y)
      A.Pi mx e e' -> Fun <$> (evaluate e rho) <*> case mx of
                        Just x  -> do
                          y <- fresh x
                          Abs y <$> translate e (Env.update rho (A.uid x) y)
                        Nothing -> K <$> evaluate e' rho
      A.Typ        -> typ

-- * Evaluation monad

instance (Applicative m, Monad m, Signature Val sig, MonadReader sig m, MonadFresh m) => MonadEval Head Val Renaming m where

  typ  = return $ Sort Type
  kind = return $ Sort Kind
  freeVar h t = return $ Ne h t []

  valView v = return $
    case (whnf v) of
      Fun a b -> VPi a b
      Sort s  -> VSort s
      Ne h       t vs -> VNe  h t (reverse vs)
      Df x v t vs     -> VDef (A.Def x) t (reverse vs)
      _               -> VAbs

  apply f v = return $ app f v

  evaluate e rho = error "NYI: NamedExplSubst.evaluate"
--  evaluate e rho = whnf <$> translate e rho

  evaluate' e = whnf <$> (translate e =<< renaming)

  unfold v =
    case v of
      Df x f t vs  -> appsR f vs
      _            -> return v

  unfolds v =
    case v of
      Df x f t vs  -> unfolds =<< appsR f vs -- unfolding application
      _            -> return v

  abstractPi a (n, Ne (A.Var x) _ []) b = return $ Fun a $ Abs x b

  reify v = quote v

-- * Reification

-- quote :: Val -> A.SysNameCounter -> EvalM A.Expr
quote :: (Applicative m, Monad m, MonadFresh m, MonadEval Head Val Renaming m) =>
         Val -> m A.Expr
quote v =
  case v of
    Ne h a vs    -> foldr (flip A.App) (A.Ident h) <$> mapM quote vs
    Df x f a vs  -> foldr (flip A.App) (A.Ident (A.Def x)) <$> mapM quote vs
    App f vs      -> foldr (flip A.App) <$> quote f <*> mapM quote vs
    Sort Type    -> return A.Typ
    Sort Kind    -> error "cannot quote sort kind"
    DontCare     -> error "cannot quote the dontcare value"
    Fun a (K b)  -> A.Pi Nothing <$> quote a <*> quote b
    Fun a f      -> do
      u     <- quote a
      (x,t) <- quoteFun f
      return $ A.Pi (Just x) u t
    f            -> do
      (x,e) <- quoteFun f
      return $ A.Lam x Nothing e

-- | @quoteFun n v@ expects @v@ to be a function and returns and its
--   body as an expression.
-- quoteFun :: Val -> A.SysNameCounter -> EvalM (A.Name, A.Expr)
quoteFun :: (Applicative m, Monad m, MonadFresh m, MonadEval Head Val Renaming m) =>
            Val -> m (A.Name, A.Expr)
quoteFun f = do
  x <- fresh $ boundName f
  v <- f `apply` (var_ x)
  (x,) <$> quote v
