content: module SimFileUtils ( analyzeBluesimDependencies
                      , codeGenOptionDescr
                      ) where
  
  import Id(Id,getIdBaseString)
  import Flags(Flags(..))
  import SimPackage
  import SimPrimitiveModules(isPrimitiveModule)
  import ASyntax(AVInst(..))
  import ABinUtil(ABinMap)
  import VModInfo(VModInfo(..), getVNameString)
  import Version(bscVersionStr)
  import FileNameUtil
  import ErrorUtil(internalError)
  
  import System.Posix.Files
  import System.Posix.Types(EpochTime)
  import System.IO(openFile, hGetContents, hClose, IOMode(..))
  import Control.Monad(filterM)
  import Control.Exception(bracketOnError)
  import Data.List(delete,find,isPrefixOf)
  import qualified Data.Map as M
  
  -- import Debug.Trace(traceM)
  
  getModTime :: FilePath -> IO (Maybe EpochTime)
  getModTime f =
      do ok <- fileExist f
         if ok
          then do s <- getFileStatus f
                  return $ Just (modificationTime s)
          else return Nothing
  
  codeGenOptionDescr :: Flags -> Bool -> String
  codeGenOptionDescr flags is_top =
      unwords $ [ "Generation options:" ] ++
                (if (keepFires flags) then ["keep-fires"] else []) ++
                (if (is_top && (genSysC flags)) then ["sysc-top"] else [])
  
  readCodeGenOptionDescr :: FilePath -> IO (Maybe String)
  readCodeGenOptionDescr f =
      do ok <- fileExist f
         if ok
          then do bracketOnError (openFile f ReadMode)
                                 (\hdl -> do hClose hdl
                                             return Nothing)
                                 (\hdl -> do content <- hGetContents hdl
                                             let search_window = take 15 (lines content)
                                                 comment = find ("/* Generation options: " `isPrefixOf`) search_window
                                             comment `seq` hClose hdl
                                             return comment)
          else return Nothing
  
  isStale :: Flags -> FilePath -> ABinMap -> Id -> SimPackage -> IO Bool
  isStale flags prefix ba_map top_pkg pkg =
      if (sp_version pkg /= bscVersionStr True)
      then return True
      else let name = getIdBaseString (sp_name pkg)
           in case (M.lookup name ba_map) of
                Nothing        -> internalError $ "isStale: unknown package " ++ name
                (Just ba_file) -> do h_file <- genFileName mkHName (cdir flags) "" name
                                     o_file <- genFileName mkObjName (cdir flags) "" name
                                     ba_time <- getModTime ba_file
                                     h_time <- getModTime h_file
                                     obj_time <- getModTime o_file
                                     let stale_time =
                                          case (ba_time, h_time, obj_time) of
                                            (Just t1, Just t2, Just t3) -> (t2 < t1) || (t3 < t1)
                                            _                           -> True
                                     cg_opt <- readCodeGenOptionDescr h_file
                                     let is_top = (sp_name pkg) == top_pkg
                                         cg_tgt = Just ("/* " ++ (codeGenOptionDescr flags is_top) ++ " */")
                                         stale_options = cg_opt /= cg_tgt
                                     return $ stale_time || stale_options
  
  remove_stale :: M.Map String [String] -> [String] -> [String] -> [String]
  remove_stale _     []   _      = []
  remove_stale _     pkgs []     = pkgs
  remove_stale feeds pkgs (x:xs) =
      if (x `elem` pkgs)
      then let invalidated = maybe [] id (M.lookup x feeds)
           in remove_stale feeds (delete x pkgs) (invalidated ++ xs)
      else remove_stale feeds pkgs xs
  
  analyzeBluesimDependencies :: Flags -> SimSystem -> FilePath -> IO [String]
  analyzeBluesimDependencies flags sim_system prefix =
      do let pkgs = M.elems (ssys_packages sim_system)
             ba_map = ssys_filemap sim_system
             influences pkg = let pname = getIdBaseString (sp_name pkg)
                                  insts = [ name
                                          | i <- M.elems (sp_state_instances pkg)
                                          , let name = getVNameString (vName (avi_vmi i))
                                          , not (isPrimitiveModule name)
                                          ]
                              in map (\x -> (x,[pname])) insts
             feeds = M.fromListWith (++) (concatMap influences pkgs)
             top_mod = ssys_top sim_system
         stale_pkgs <- filterM (isStale flags prefix ba_map top_mod) pkgs
         let all_pkg_names = map (getIdBaseString . sp_name) pkgs
             stale_pkg_names = map (getIdBaseString . sp_name) stale_pkgs
             reusable = remove_stale feeds all_pkg_names stale_pkg_names
         return reusable
  