readInterfaceFile :: forall m.
                     MonadIO m
                  => NameCacheAccessor m
                  -> FilePath
                  -> m (Either String InterfaceFile)
readInterfaceFile (get_name_cache, set_name_cache) filename = do
  bh0 <- liftIO $ readBinMem filename

  magic   <- liftIO $ get bh0
  version <- liftIO $ get bh0

  case () of
    _ | magic /= binaryInterfaceMagic -> return . Left $
      "Magic number mismatch: couldn't load interface file: " ++ filename
      | version `notElem` binaryInterfaceVersionCompatibility -> return . Left $
      "Interface file is of wrong version: " ++ filename
      | otherwise -> with_name_cache $ \update_nc -> do

      dict  <- get_dictionary bh0

      -- read the symbol table so we are capable of reading the actual data
      bh1 <- do
          let bh1 = setUserData bh0 $ newReadState (error "getSymtabName")
                                                   (getDictFastString dict)
          symtab <- update_nc (get_symbol_table bh1)
          return $ setUserData bh1 $ newReadState (getSymtabName (NCU (\f -> update_nc (return . f))) dict symtab)
                                                  (getDictFastString dict)

      -- load the actual data
      iface <- liftIO $ get bh1
      return (Right iface)
 where
   with_name_cache :: forall a.
                      ((forall n b. MonadIO n
                                => (NameCache -> n (NameCache, b))
                                -> n b)
                       -> m a)
                   -> m a
   with_name_cache act = do
      nc_var <-  get_name_cache >>= (liftIO . newIORef)
      x <- act $ \f -> do
              nc <- liftIO $ readIORef nc_var
              (nc', x) <- f nc
              liftIO $ writeIORef nc_var nc'
              return x
      liftIO (readIORef nc_var) >>= set_name_cache
      return x

   get_dictionary bin_handle = liftIO $ do
      dict_p <- get bin_handle
      data_p <- tellBin bin_handle
      seekBin bin_handle dict_p
      dict <- getDictionary bin_handle
      seekBin bin_handle data_p
      return dict

   get_symbol_table bh1 theNC = liftIO $ do
      symtab_p <- get bh1
      data_p'  <- tellBin bh1
      seekBin bh1 symtab_p
      (nc', symtab) <- getSymbolTable bh1 theNC
      seekBin bh1 data_p'
      return (nc', symtab)


-------------------------------------------------------------------------------
-- * Symbol table
-------------------------------------------------------------------------------
readInterfaceFile :: forall m.
                     MonadIO m
                  => NameCacheAccessor m
                  -> FilePath
                  -> m (Either String InterfaceFile)
readInterfaceFile (get_name_cache, set_name_cache) filename = do
  bh0 <- liftIO $ readBinMem filename

  magic   <- liftIO $ get bh0
  version <- liftIO $ get bh0

  case () of
    _ | magic /= binaryInterfaceMagic -> return . Left $
      "Magic number mismatch: couldn't load interface file: " ++ filename
      | version `notElem` binaryInterfaceVersionCompatibility -> return . Left $
      "Interface file is of wrong version: " ++ filename
      | otherwise -> with_name_cache $ \update_nc -> do

      dict  <- get_dictionary bh0

      -- read the symbol table so we are capable of reading the actual data
      bh1 <- do
          let bh1 = setUserData bh0 $ newReadState (error "getSymtabName")
                                                   (getDictFastString dict)
          symtab <- update_nc (get_symbol_table bh1)
          return $ setUserData bh1 $ newReadState (getSymtabName (NCU (\f -> update_nc (return . f))) dict symtab)
                                                  (getDictFastString dict)

      -- load the actual data
      iface <- liftIO $ get bh1
      return (Right iface)
 where
   with_name_cache :: forall a.
                      ((forall n b. MonadIO n
                                => (NameCache -> n (NameCache, b))
                                -> n b)
                       -> m a)
                   -> m a
   with_name_cache act = do
      nc_var <-  get_name_cache >>= (liftIO . newIORef)
      x <- act $ \f -> do
              nc <- liftIO $ readIORef nc_var
              (nc', x) <- f nc
              liftIO $ writeIORef nc_var nc'
              return x
      liftIO (readIORef nc_var) >>= set_name_cache
      return x

   get_dictionary bin_handle = liftIO $ do
      dict_p <- get bin_handle
      data_p <- tellBin bin_handle
      seekBin bin_handle dict_p
      dict <- getDictionary bin_handle
      seekBin bin_handle data_p
      return dict

   get_symbol_table bh1 theNC = liftIO $ do
      symtab_p <- get bh1
      data_p'  <- tellBin bh1
      seekBin bh1 symtab_p
      (nc', symtab) <- getSymbolTable bh1 theNC
      seekBin bh1 data_p'
      return (nc', symtab)


-------------------------------------------------------------------------------
-- * Symbol table
-------------------------------------------------------------------------------
readInterfaceFile (get_name_cache, set_name_cache) filename = do
  bh0 <- liftIO $ readBinMem filename

  magic   <- liftIO $ get bh0
  version <- liftIO $ get bh0

  case () of
    _ | magic /= binaryInterfaceMagic -> return . Left $
      "Magic number mismatch: couldn't load interface file: " ++ filename
      | version `notElem` binaryInterfaceVersionCompatibility -> return . Left $
      "Interface file is of wrong version: " ++ filename
      | otherwise -> with_name_cache $ \update_nc -> do

      dict  <- get_dictionary bh0

      -- read the symbol table so we are capable of reading the actual data
      bh1 <- do
          let bh1 = setUserData bh0 $ newReadState (error "getSymtabName")
                                                   (getDictFastString dict)
          symtab <- update_nc (get_symbol_table bh1)
          return $ setUserData bh1 $ newReadState (getSymtabName (NCU (\f -> update_nc (return . f))) dict symtab)
                                                  (getDictFastString dict)

      -- load the actual data
      iface <- liftIO $ get bh1
      return (Right iface)
 where
   with_name_cache :: forall a.
                      ((forall n b. MonadIO n
                                => (NameCache -> n (NameCache, b))
                                -> n b)
                       -> m a)
                   -> m a
   with_name_cache act = do
      nc_var <-  get_name_cache >>= (liftIO . newIORef)
      x <- act $ \f -> do
              nc <- liftIO $ readIORef nc_var
              (nc', x) <- f nc
              liftIO $ writeIORef nc_var nc'
              return x
      liftIO (readIORef nc_var) >>= set_name_cache
      return x

   get_dictionary bin_handle = liftIO $ do
      dict_p <- get bin_handle
      data_p <- tellBin bin_handle
      seekBin bin_handle dict_p
      dict <- getDictionary bin_handle
      seekBin bin_handle data_p
      return dict

   get_symbol_table bh1 theNC = liftIO $ do
      symtab_p <- get bh1
      data_p'  <- tellBin bh1
      seekBin bh1 symtab_p
      (nc', symtab) <- getSymbolTable bh1 theNC
      seekBin bh1 data_p'
      return (nc', symtab)


-------------------------------------------------------------------------------
-- * Symbol table
-------------------------------------------------------------------------------