{-# OPTIONS_GHC -fno-warn-unused-binds #-} --TODO

module Il.Analyzer (analyzeIl) where

import Defs.Structures
import Defs.Common
import Il.AST

import Analyzer hiding (getFunName, getFunArgs, getFunType)

import Data.Monoid
import Control.Monad.State

-- | Runs everything that is needed to analyze a program
analyzeIl :: [Function] -> [DSInfo]
analyzeIl functions = let dsfs = map generateDSF functions in
    analyzeDSF dsfs

-- | Start the state monad to create a 'DSFun' for function
generateDSF :: Function -> DSFun Type
generateDSF fn = let (dsus, st) = runState (sumTerms step [getFunBody fn]) (AS []) in
    let fd = FunDecl (getFunName fn) (getFunType fn) (getFunArgs fn) in
    DSF fd (getStateCalls st) (generateDSI (getFunName fn) dsus)

-- | Analyze a block of terms using the state monad
stepBlock :: [Term] -> TermAnalyzer Output
stepBlock = sumTerms step

-- | Sums the 'DSUse's using the 'step' function and concating the results
sumTerms :: (Term -> TermAnalyzer Output) -> [Term] -> TermAnalyzer Output
sumTerms f ts = fmap concat (mapM f ts)

-- | Function putting a function call in the state
putCall :: FunctionName -> [Term] -> TermAnalyzer ()
putCall name args = do
    let cleanArgs = map justifyVars args
    let call = (name, cleanArgs)
    modify  $ \s -> s {getStateCalls = call:getStateCalls s} where
        justifyVars :: Term -> Maybe VariableName
        justifyVars (Var v) = Just v -- FIXME should work on function calls returning dses, not only vars
        justifyVars _ = Nothing

-- | Generate 'DSUse's for a single 'Term'
step :: Term -> TermAnalyzer Output

step (Block body) = stepBlock body

step (VarInit _ Ds) = return []
step (InitAssign _ _ Ds) = return []

step (While cond body) = stepBlock [cond,body]

step (Funcall name args) = do
    let opname = case name of -- FIXME nicer with usage of dsinfFunctions from Common
            F "insert"        -> Just InsertVal
            F "find"          -> Just FindByVal
            F "update"        -> Just UpdateByRef
            F "max"           -> Just ExtremalVal
            F "delete_max"    -> Just DeleteExtremalVal
            _               -> Nothing
                                            -- FIXME add reading the function calls
    argDsus <- stepBlock args

    funcallDsu <- case opname of
        Nothing ->  do
            putCall name args
            return []
        Just op ->  case head args of       -- FIXME dsinfFunctions ds argument recognition
            Var varname -> return [(varname, DSU op False False)]
            _           -> error "Not implemented yet"

    return $ argDsus ++ funcallDsu

step (If cond t1 t2) = do
    dsuCond <- step cond
    oldState <- get

    dsuT1 <- step t1
    stateT1 <- get

    put oldState

    dsuT2 <- step t2
    stateT2 <- get

    put (stateT1 `mappend` stateT2)
    return $ concat [dsuCond, dsuT1, dsuT2]

-- Dummy steps
step t = case t of
    Var _ -> return []
    VarInit _ _ -> return []
    Inc _ -> return []
    Dec _ -> return []
    Int _ -> return []

    Assign _ t -> step t
    Lt t1 t2 -> stepBlock [t1, t2]
    Mul t1 t2 -> stepBlock [t1, t2]
    s -> error $ "No step for " ++ show s
