-- TODO overflow return code?
analyzeFiles :: FilePath -> [FilePath] -> IO (Maybe (String, [(VarDecl, CallGraph)]))
analyzeFiles base files = do
  mast <- parseASTFiles base files
  case mast of
    Nothing -> putStrLn "analyzeFile: Invalid file." >> return Nothing
    Just (globals, funcs) -> do
      -- Calculate graphs, keep only those with stack limits
      let pairs = filter (filterCallGraphFn . fst)
                  $ evalStack $ mapM (go globals) funcs

      --mapM_ ppDeclCG pairs

      -- Lookup stack limits
      let limitPairs =
            [ (limit, (name, cg))
            | (decl, cg) <- pairs
            , let name = declString decl
            , let Just limit = lookup name A.stack_limits]

      let traces = map (uncurry maxTraces) limitPairs
          (bad, good) = partitionEithers traces
      lines <- mapM (ppTraceLimit False) good

      -- Output string
      let 
        nsString = "num_sats: " ++ show (A.num_dds+1) ++ "\n"
        errors = case bad of
                   [] -> ""
                   _  -> 
                     "analyzeFiles. error: symbolic values in\n"
                     ++ show bad
        overflows = if any isOverflow good then "POSSIBLE OVERFLOW DETECTED" else ""
        outputString = unlines $ overflows : errors : nsString : lines

      return $ Just (outputString, pairs)
  where
   go :: GlobalDecls -> FunDef -> State StackMap (VarDecl, CallGraph)
   go g (FunDef d s _) | Just params <- funParams d =
     (d,) <$> defStackUsage g (s, params)
   go _ f = error $ "analyzeFiles. not a FunDef?: " ++ show f

   ppTraceLimit :: Bool -> Trace -> IO String
   ppTraceLimit put trace = do
     let limitString = "(limit " ++ show (trLimit trace) ++ ")"
         traceString = ppTrace trace
     if put
       then do
         setSGR [SetColor Foreground Dull Blue]
         putStrLn limitString
         setSGR [Reset]
         putStrLn $ traceString
       else return ()
     return $ unlines [limitString, traceString]
analyzeFiles :: FilePath -> [FilePath] -> IO (Maybe (String, [(VarDecl, CallGraph)]))
analyzeFiles base files = do
  mast <- parseASTFiles base files
  case mast of
    Nothing -> putStrLn "analyzeFile: Invalid file." >> return Nothing
    Just (globals, funcs) -> do
      -- Calculate graphs, keep only those with stack limits
      let pairs = filter (filterCallGraphFn . fst)
                  $ evalStack $ mapM (go globals) funcs

      --mapM_ ppDeclCG pairs

      -- Lookup stack limits
      let limitPairs =
            [ (limit, (name, cg))
            | (decl, cg) <- pairs
            , let name = declString decl
            , let Just limit = lookup name A.stack_limits]

      let traces = map (uncurry maxTraces) limitPairs
          (bad, good) = partitionEithers traces
      lines <- mapM (ppTraceLimit False) good

      -- Output string
      let 
        nsString = "num_sats: " ++ show (A.num_dds+1) ++ "\n"
        errors = case bad of
                   [] -> ""
                   _  -> 
                     "analyzeFiles. error: symbolic values in\n"
                     ++ show bad
        overflows = if any isOverflow good then "POSSIBLE OVERFLOW DETECTED" else ""
        outputString = unlines $ overflows : errors : nsString : lines

      return $ Just (outputString, pairs)
  where
   go :: GlobalDecls -> FunDef -> State StackMap (VarDecl, CallGraph)
   go g (FunDef d s _) | Just params <- funParams d =
     (d,) <$> defStackUsage g (s, params)
   go _ f = error $ "analyzeFiles. not a FunDef?: " ++ show f

   ppTraceLimit :: Bool -> Trace -> IO String
   ppTraceLimit put trace = do
     let limitString = "(limit " ++ show (trLimit trace) ++ ")"
         traceString = ppTrace trace
     if put
       then do
         setSGR [SetColor Foreground Dull Blue]
         putStrLn limitString
         setSGR [Reset]
         putStrLn $ traceString
       else return ()
     return $ unlines [limitString, traceString]
analyzeFiles base files = do
  mast <- parseASTFiles base files
  case mast of
    Nothing -> putStrLn "analyzeFile: Invalid file." >> return Nothing
    Just (globals, funcs) -> do
      -- Calculate graphs, keep only those with stack limits
      let pairs = filter (filterCallGraphFn . fst)
                  $ evalStack $ mapM (go globals) funcs

      --mapM_ ppDeclCG pairs

      -- Lookup stack limits
      let limitPairs =
            [ (limit, (name, cg))
            | (decl, cg) <- pairs
            , let name = declString decl
            , let Just limit = lookup name A.stack_limits]

      let traces = map (uncurry maxTraces) limitPairs
          (bad, good) = partitionEithers traces
      lines <- mapM (ppTraceLimit False) good

      -- Output string
      let 
        nsString = "num_sats: " ++ show (A.num_dds+1) ++ "\n"
        errors = case bad of
                   [] -> ""
                   _  -> 
                     "analyzeFiles. error: symbolic values in\n"
                     ++ show bad
        overflows = if any isOverflow good then "POSSIBLE OVERFLOW DETECTED" else ""
        outputString = unlines $ overflows : errors : nsString : lines

      return $ Just (outputString, pairs)
  where
   go :: GlobalDecls -> FunDef -> State StackMap (VarDecl, CallGraph)
   go g (FunDef d s _) | Just params <- funParams d =
     (d,) <$> defStackUsage g (s, params)
   go _ f = error $ "analyzeFiles. not a FunDef?: " ++ show f

   ppTraceLimit :: Bool -> Trace -> IO String
   ppTraceLimit put trace = do
     let limitString = "(limit " ++ show (trLimit trace) ++ ")"
         traceString = ppTrace trace
     if put
       then do
         setSGR [SetColor Foreground Dull Blue]
         putStrLn limitString
         setSGR [Reset]
         putStrLn $ traceString
       else return ()
     return $ unlines [limitString, traceString]