-- Calculate the forceable arguments to a constructor and update the set of
-- optimisations

forceArgs :: Name -> Type -> Idris ()
forceArgs n t = do i <- getIState
                   let fargs = force i 0 t
                   copt <- case lookupCtxt n (idris_optimisation i) of
                                 []     -> return $ Optimise False [] []
                                 (op:_) -> return op
                   let opts = addDef n (copt { forceable = fargs }) (idris_optimisation i)
                   putIState (i { idris_optimisation = opts })
                   addIBC (IBCOpt n)
                   iLOG $ "Forced: " ++ show n ++ " " ++ show fargs ++ "\n   from " ++
                          show t
  where
    force :: IState -> Int -> Term -> [Int]
    force ist i (Bind _ (Pi ty) sc)
        | collapsibleIn ist ty 
            = nub $ i : (force ist (i + 1) $ instantiate (P Bound (MN i "?") Erased) sc)
        | otherwise = force ist (i + 1) $ instantiate (P Bound (MN i "?") Erased) sc
    force _ _ sc@(App f a) 
        | (_, args) <- unApply sc 
            = nub $ concatMap guarded args
    force _ _ _ = []

    collapsibleIn i t
        | (P _ tn _, _) <- unApply t
           = case lookupCtxt tn (idris_optimisation i) of
                [oi] -> collapsible oi
                _ -> False
        | otherwise = False

    isF (P _ (MN force "?") _) = Just force
    isF _ = Nothing

    guarded :: Term -> [Int]
    guarded t@(App f a)
--         | (P (TCon _ _) _ _, args) <- unApply t
--             = mapMaybe isF args ++ concatMap guarded args
        | (P (DCon _ _) _ _, args) <- unApply t
            = mapMaybe isF args ++ concatMap guarded args
    guarded t = mapMaybe isF [t]

-- Calculate whether a collection of constructors is collapsible
forceArgs :: Name -> Type -> Idris ()
forceArgs n t = do i <- getIState
                   let fargs = force i 0 t
                   copt <- case lookupCtxt n (idris_optimisation i) of
                                 []     -> return $ Optimise False [] []
                                 (op:_) -> return op
                   let opts = addDef n (copt { forceable = fargs }) (idris_optimisation i)
                   putIState (i { idris_optimisation = opts })
                   addIBC (IBCOpt n)
                   iLOG $ "Forced: " ++ show n ++ " " ++ show fargs ++ "\n   from " ++
                          show t
  where
    force :: IState -> Int -> Term -> [Int]
    force ist i (Bind _ (Pi ty) sc)
        | collapsibleIn ist ty 
            = nub $ i : (force ist (i + 1) $ instantiate (P Bound (MN i "?") Erased) sc)
        | otherwise = force ist (i + 1) $ instantiate (P Bound (MN i "?") Erased) sc
    force _ _ sc@(App f a) 
        | (_, args) <- unApply sc 
            = nub $ concatMap guarded args
    force _ _ _ = []

    collapsibleIn i t
        | (P _ tn _, _) <- unApply t
           = case lookupCtxt tn (idris_optimisation i) of
                [oi] -> collapsible oi
                _ -> False
        | otherwise = False

    isF (P _ (MN force "?") _) = Just force
    isF _ = Nothing

    guarded :: Term -> [Int]
    guarded t@(App f a)
--         | (P (TCon _ _) _ _, args) <- unApply t
--             = mapMaybe isF args ++ concatMap guarded args
        | (P (DCon _ _) _ _, args) <- unApply t
            = mapMaybe isF args ++ concatMap guarded args
    guarded t = mapMaybe isF [t]

-- Calculate whether a collection of constructors is collapsible
forceArgs n t = do i <- getIState
                   let fargs = force i 0 t
                   copt <- case lookupCtxt n (idris_optimisation i) of
                                 []     -> return $ Optimise False [] []
                                 (op:_) -> return op
                   let opts = addDef n (copt { forceable = fargs }) (idris_optimisation i)
                   putIState (i { idris_optimisation = opts })
                   addIBC (IBCOpt n)
                   iLOG $ "Forced: " ++ show n ++ " " ++ show fargs ++ "\n   from " ++
                          show t
  where
    force :: IState -> Int -> Term -> [Int]
    force ist i (Bind _ (Pi ty) sc)
        | collapsibleIn ist ty 
            = nub $ i : (force ist (i + 1) $ instantiate (P Bound (MN i "?") Erased) sc)
        | otherwise = force ist (i + 1) $ instantiate (P Bound (MN i "?") Erased) sc
    force _ _ sc@(App f a) 
        | (_, args) <- unApply sc 
            = nub $ concatMap guarded args
    force _ _ _ = []

    collapsibleIn i t
        | (P _ tn _, _) <- unApply t
           = case lookupCtxt tn (idris_optimisation i) of
                [oi] -> collapsible oi
                _ -> False
        | otherwise = False

    isF (P _ (MN force "?") _) = Just force
    isF _ = Nothing

    guarded :: Term -> [Int]
    guarded t@(App f a)
--         | (P (TCon _ _) _ _, args) <- unApply t
--             = mapMaybe isF args ++ concatMap guarded args
        | (P (DCon _ _) _ _, args) <- unApply t
            = mapMaybe isF args ++ concatMap guarded args
    guarded t = mapMaybe isF [t]

-- Calculate whether a collection of constructors is collapsible