module Agda.TypeChecking.EtaContract where
import Agda.Syntax.Common
import Agda.Syntax.Internal
import Agda.Syntax.Internal.Generic
import Agda.TypeChecking.Substitute
import Agda.TypeChecking.Free
import Agda.TypeChecking.Monad
import Agda.TypeChecking.Reduce.Monad () 
import {-# SOURCE #-} Agda.TypeChecking.Records
import {-# SOURCE #-} Agda.TypeChecking.Datatypes
import Agda.Utils.Monad
import Agda.Utils.Impossible
data BinAppView = App Term (Arg Term)
                | NoApp Term
binAppView :: Term -> BinAppView
binAppView t = case t of
  Var i xs   -> appE (Var i) xs
  Def c xs   -> appE (Def c) xs
  
  
  
  
  
  Con c ci xs
    | null (conFields c) -> appE (Con c ci) xs
    | otherwise          -> noApp
  Lit _      -> noApp
  Level _    -> noApp   
  Lam _ _    -> noApp
  Pi _ _     -> noApp
  Sort _     -> noApp
  MetaV _ _  -> noApp
  DontCare _ -> noApp
  Dummy{}    -> __IMPOSSIBLE__
  where
    noApp = NoApp t
    app f [] = noApp
    app f xs = App (f $ init xs) (last xs)
    appE f [] = noApp
    appE f xs
      | Apply v <- last xs = App (f $ init xs) v
      | otherwise          = noApp
{-# SPECIALIZE etaContract :: TermLike a => a -> TCM a #-}
{-# SPECIALIZE etaContract :: TermLike a => a -> ReduceM a #-}
etaContract :: (MonadTCEnv m, HasConstInfo m, HasOptions m, TermLike a) => a -> m a
etaContract = traverseTermM etaOnce
{-# SPECIALIZE etaOnce :: Term -> TCM Term #-}
{-# SPECIALIZE etaOnce :: Term -> ReduceM Term #-}
etaOnce :: (MonadTCEnv m, HasConstInfo m, HasOptions m) => Term -> m Term
etaOnce v = case v of
  
  
  
  Lam i (Abs x b) -> etaLam i x b  
  
  
  
  Con c ci es -> do
    etaCon c ci es etaContractRecord
  v -> return v
etaCon :: (MonadTCEnv m, HasConstInfo m, HasOptions m)
  => ConHead  
  -> ConInfo  
  -> Elims     
  -> (QName -> ConHead -> ConInfo -> Args -> m Term)
              
  -> m Term   
etaCon c ci es cont = ignoreAbstractMode $ do
  let fallback = return $ Con c ci $ es
  
  r <- getConstructorData $ conName c 
  ifNotM (isEtaRecord r) fallback $  do
    
    let Just args = allApplyElims es
    cont r c ci args
etaLam :: (MonadTCEnv m, HasConstInfo m, HasOptions m)
  => ArgInfo  
  -> ArgName  
  -> Term     
  -> m Term   
etaLam i x b = do
    let fallback = return $ Lam i $ Abs x b
    case binAppView b of
      App u (Arg info v) -> do
        tyty <- typeInType
        if isVar0 tyty v
        
        
        
        
             && sameHiding i info
             && sameModality i info
             && not (freeIn 0 u)
           then return $ strengthen __IMPOSSIBLE__ u
           else fallback
      _ -> fallback
  where
    isVar0 _ (Var 0 [])               = True
    
    
    
    
    
    
    isVar0 tyty (Level (Max 0 [Plus 0 l])) = case l of
      NeutralLevel _ v -> isVar0 tyty v
      UnreducedLevel v -> isVar0 tyty v
      BlockedLevel{}   -> False
      MetaLevel{}      -> False
    isVar0 _ _ = False