processInternalImports :: L2.L2NamespaceID -> ModelTranslation ()
processInternalImports startFrom = do
  allNS <- S.insert startFrom <$> findNamespaceDescendents "pii" startFrom
  ModelTranslationState { mtsL2ToL1Map = nsmap } <- get
  -- Make a list of namespaces that actually have local imports to process...
  let
      isFinished nsid = do
            Just (L1.L1NamespaceContents l1nsc) <- M.lookup nsid . mtsL2ToL1Map <$> get
            return $ not (any isLocalImport l1nsc)
      isLocalImport (L1.L1NSImport { L1.l1nsImportFrom = Nothing }) = True
      isLocalImport _ = False        
  (pending, finished) <- (\(a, b) -> (S.fromList a, S.fromList b)) <$> partitionM isFinished allNS
  let
      processInternalImports'' pending finished =
        if S.null pending
          then return ()
          else do
            let chosen = head (S.elems pending)
            (pending', finished') <-
              processInternalImports' [chosen] (S.delete chosen pending) finished
            processInternalImports'' pending' finished'

      processInternalImports' :: [L2.L2NamespaceID] -> S.Set L2.L2NamespaceID -> S.Set L2.L2NamespaceID ->
                                 ModelTranslation (S.Set L2.L2NamespaceID, S.Set L2.L2NamespaceID)
      processInternalImports' stack@(curNSID:_) pending finished = do
        let (Just (L1.L1NamespaceContents l1nsc)) = M.lookup curNSID nsmap
        flip (flip foldM (pending, finished)) l1nsc $ \(pending, finished) st -> case st of
          L1.L1NSImport { L1.l1nsSS = ss,
                          L1.l1nsImportFrom = Nothing,
                          L1.l1nsImportPath = path,
                          L1.l1nsImportWhat = what,
                          L1.l1nsImportHiding = hiding } -> do
            childNSID <- findScopedSymbolByRAPath ss curNSID path
                           (\nsc ident -> M.lookup (L1.l1IdBS ident) (L2.l2nsNamespaces nsc))
            when (childNSID `elem` stack) $ do
              paths <- intercalate ", which is imported by " <$> mapM (nsidToFriendlyName nsMain)
                         ((childNSID:(takeWhile (/= childNSID) stack)) ++ [childNSID])
              fail $ "Illegal import cycle found involving namespace " ++
                     paths ++ ", at " ++ (show ss)
            (pending', finished') <-
              if (childNSID `S.member` finished)
                then return (pending, finished)
                else processInternalImports' (childNSID:stack) (S.delete childNSID pending) finished
            l2model <- getL2Model
            childNSC <- getNamespaceContents "internal imports - child NS" childNSID
            importSyms <- importListFromWhatAndHiding childNSID l2model what hiding
            forM_ importSyms $ \(L1.L1Identifier _ importSym) -> do
              -- TODO - check importSym doesn't already exist in curNSID.
              let
                importSomething :: (L2.L2NamespaceContents -> M.Map L2.Identifier a) ->
                                   (L2.L2NamespaceContents -> M.Map L2.Identifier a -> L2.L2NamespaceContents) ->
                                   ModelTranslation ()
                importSomething getter setter =
                    case M.lookup importSym (getter childNSC) of
                      Nothing -> return ()
                      Just impSomething ->
                        modifyNamespaceContents curNSID (\nsc -> setter nsc (M.insert importSym impSomething (getter nsc)))
              importSomething L2.l2nsNamespaces (\nsc x -> nsc { L2.l2nsNamespaces = x })
              importSomething L2.l2nsDomains (\nsc x -> nsc { L2.l2nsDomains = x })
              importSomething L2.l2nsNamedValues (\nsc x -> nsc { L2.l2nsNamedValues = x })
              importSomething L2.l2nsClassValues (\nsc x -> nsc { L2.l2nsClassValues = x })
              importSomething L2.l2nsUnits (\nsc x -> nsc { L2.l2nsUnits = x })
              importSomething L2.l2nsClasses (\nsc x -> nsc { L2.l2nsClasses = x })
              importSomething L2.l2nsDomainFunctions (\nsc x -> nsc { L2.l2nsDomainFunctions = x })
              importSomething L2.l2nsLabels (\nsc x -> nsc { L2.l2nsLabels = x })
              
            return (pending', S.insert curNSID finished')
  processInternalImports'' pending finished

-- | Recursively translates a model, starting from the specified namespace.
processInternalImports :: L2.L2NamespaceID -> ModelTranslation ()
processInternalImports startFrom = do
  allNS <- S.insert startFrom <$> findNamespaceDescendents "pii" startFrom
  ModelTranslationState { mtsL2ToL1Map = nsmap } <- get
  -- Make a list of namespaces that actually have local imports to process...
  let
      isFinished nsid = do
            Just (L1.L1NamespaceContents l1nsc) <- M.lookup nsid . mtsL2ToL1Map <$> get
            return $ not (any isLocalImport l1nsc)
      isLocalImport (L1.L1NSImport { L1.l1nsImportFrom = Nothing }) = True
      isLocalImport _ = False        
  (pending, finished) <- (\(a, b) -> (S.fromList a, S.fromList b)) <$> partitionM isFinished allNS
  let
      processInternalImports'' pending finished =
        if S.null pending
          then return ()
          else do
            let chosen = head (S.elems pending)
            (pending', finished') <-
              processInternalImports' [chosen] (S.delete chosen pending) finished
            processInternalImports'' pending' finished'

      processInternalImports' :: [L2.L2NamespaceID] -> S.Set L2.L2NamespaceID -> S.Set L2.L2NamespaceID ->
                                 ModelTranslation (S.Set L2.L2NamespaceID, S.Set L2.L2NamespaceID)
      processInternalImports' stack@(curNSID:_) pending finished = do
        let (Just (L1.L1NamespaceContents l1nsc)) = M.lookup curNSID nsmap
        flip (flip foldM (pending, finished)) l1nsc $ \(pending, finished) st -> case st of
          L1.L1NSImport { L1.l1nsSS = ss,
                          L1.l1nsImportFrom = Nothing,
                          L1.l1nsImportPath = path,
                          L1.l1nsImportWhat = what,
                          L1.l1nsImportHiding = hiding } -> do
            childNSID <- findScopedSymbolByRAPath ss curNSID path
                           (\nsc ident -> M.lookup (L1.l1IdBS ident) (L2.l2nsNamespaces nsc))
            when (childNSID `elem` stack) $ do
              paths <- intercalate ", which is imported by " <$> mapM (nsidToFriendlyName nsMain)
                         ((childNSID:(takeWhile (/= childNSID) stack)) ++ [childNSID])
              fail $ "Illegal import cycle found involving namespace " ++
                     paths ++ ", at " ++ (show ss)
            (pending', finished') <-
              if (childNSID `S.member` finished)
                then return (pending, finished)
                else processInternalImports' (childNSID:stack) (S.delete childNSID pending) finished
            l2model <- getL2Model
            childNSC <- getNamespaceContents "internal imports - child NS" childNSID
            importSyms <- importListFromWhatAndHiding childNSID l2model what hiding
            forM_ importSyms $ \(L1.L1Identifier _ importSym) -> do
              -- TODO - check importSym doesn't already exist in curNSID.
              let
                importSomething :: (L2.L2NamespaceContents -> M.Map L2.Identifier a) ->
                                   (L2.L2NamespaceContents -> M.Map L2.Identifier a -> L2.L2NamespaceContents) ->
                                   ModelTranslation ()
                importSomething getter setter =
                    case M.lookup importSym (getter childNSC) of
                      Nothing -> return ()
                      Just impSomething ->
                        modifyNamespaceContents curNSID (\nsc -> setter nsc (M.insert importSym impSomething (getter nsc)))
              importSomething L2.l2nsNamespaces (\nsc x -> nsc { L2.l2nsNamespaces = x })
              importSomething L2.l2nsDomains (\nsc x -> nsc { L2.l2nsDomains = x })
              importSomething L2.l2nsNamedValues (\nsc x -> nsc { L2.l2nsNamedValues = x })
              importSomething L2.l2nsClassValues (\nsc x -> nsc { L2.l2nsClassValues = x })
              importSomething L2.l2nsUnits (\nsc x -> nsc { L2.l2nsUnits = x })
              importSomething L2.l2nsClasses (\nsc x -> nsc { L2.l2nsClasses = x })
              importSomething L2.l2nsDomainFunctions (\nsc x -> nsc { L2.l2nsDomainFunctions = x })
              importSomething L2.l2nsLabels (\nsc x -> nsc { L2.l2nsLabels = x })
              
            return (pending', S.insert curNSID finished')
  processInternalImports'' pending finished

-- | Recursively translates a model, starting from the specified namespace.
processInternalImports startFrom = do
  allNS <- S.insert startFrom <$> findNamespaceDescendents "pii" startFrom
  ModelTranslationState { mtsL2ToL1Map = nsmap } <- get
  -- Make a list of namespaces that actually have local imports to process...
  let
      isFinished nsid = do
            Just (L1.L1NamespaceContents l1nsc) <- M.lookup nsid . mtsL2ToL1Map <$> get
            return $ not (any isLocalImport l1nsc)
      isLocalImport (L1.L1NSImport { L1.l1nsImportFrom = Nothing }) = True
      isLocalImport _ = False        
  (pending, finished) <- (\(a, b) -> (S.fromList a, S.fromList b)) <$> partitionM isFinished allNS
  let
      processInternalImports'' pending finished =
        if S.null pending
          then return ()
          else do
            let chosen = head (S.elems pending)
            (pending', finished') <-
              processInternalImports' [chosen] (S.delete chosen pending) finished
            processInternalImports'' pending' finished'

      processInternalImports' :: [L2.L2NamespaceID] -> S.Set L2.L2NamespaceID -> S.Set L2.L2NamespaceID ->
                                 ModelTranslation (S.Set L2.L2NamespaceID, S.Set L2.L2NamespaceID)
      processInternalImports' stack@(curNSID:_) pending finished = do
        let (Just (L1.L1NamespaceContents l1nsc)) = M.lookup curNSID nsmap
        flip (flip foldM (pending, finished)) l1nsc $ \(pending, finished) st -> case st of
          L1.L1NSImport { L1.l1nsSS = ss,
                          L1.l1nsImportFrom = Nothing,
                          L1.l1nsImportPath = path,
                          L1.l1nsImportWhat = what,
                          L1.l1nsImportHiding = hiding } -> do
            childNSID <- findScopedSymbolByRAPath ss curNSID path
                           (\nsc ident -> M.lookup (L1.l1IdBS ident) (L2.l2nsNamespaces nsc))
            when (childNSID `elem` stack) $ do
              paths <- intercalate ", which is imported by " <$> mapM (nsidToFriendlyName nsMain)
                         ((childNSID:(takeWhile (/= childNSID) stack)) ++ [childNSID])
              fail $ "Illegal import cycle found involving namespace " ++
                     paths ++ ", at " ++ (show ss)
            (pending', finished') <-
              if (childNSID `S.member` finished)
                then return (pending, finished)
                else processInternalImports' (childNSID:stack) (S.delete childNSID pending) finished
            l2model <- getL2Model
            childNSC <- getNamespaceContents "internal imports - child NS" childNSID
            importSyms <- importListFromWhatAndHiding childNSID l2model what hiding
            forM_ importSyms $ \(L1.L1Identifier _ importSym) -> do
              -- TODO - check importSym doesn't already exist in curNSID.
              let
                importSomething :: (L2.L2NamespaceContents -> M.Map L2.Identifier a) ->
                                   (L2.L2NamespaceContents -> M.Map L2.Identifier a -> L2.L2NamespaceContents) ->
                                   ModelTranslation ()
                importSomething getter setter =
                    case M.lookup importSym (getter childNSC) of
                      Nothing -> return ()
                      Just impSomething ->
                        modifyNamespaceContents curNSID (\nsc -> setter nsc (M.insert importSym impSomething (getter nsc)))
              importSomething L2.l2nsNamespaces (\nsc x -> nsc { L2.l2nsNamespaces = x })
              importSomething L2.l2nsDomains (\nsc x -> nsc { L2.l2nsDomains = x })
              importSomething L2.l2nsNamedValues (\nsc x -> nsc { L2.l2nsNamedValues = x })
              importSomething L2.l2nsClassValues (\nsc x -> nsc { L2.l2nsClassValues = x })
              importSomething L2.l2nsUnits (\nsc x -> nsc { L2.l2nsUnits = x })
              importSomething L2.l2nsClasses (\nsc x -> nsc { L2.l2nsClasses = x })
              importSomething L2.l2nsDomainFunctions (\nsc x -> nsc { L2.l2nsDomainFunctions = x })
              importSomething L2.l2nsLabels (\nsc x -> nsc { L2.l2nsLabels = x })
              
            return (pending', S.insert curNSID finished')
  processInternalImports'' pending finished

-- | Recursively translates a model, starting from the specified namespace.