translateNSContents :: ScopeInformation -> L2.L2NamespaceID -> [L1.L1NamespaceStatement] -> ModelTranslation ()
translateNSContents scope thisNSID nsc =
  forM_ nsc $ \nss ->
    case nss of
      -- Note: We assume all remaining imports are local due to
      -- processNamespaceImports, and have no 'as' clause because
      -- of handleL1SimpleSyntacticSugar...
      L1.L1NSImport {} ->
        -- Imports are already done.
        return ()
      L1.L1NSNamespace { L1.l1nsNamespaceName = nsNameL1@(L1.L1Identifier idss nsName) } -> do
        l2nsc <- getNamespaceContents "translateNSContents - namespace" thisNSID
        let Just childNSID = (M.lookup nsName . L2.l2nsNamespaces) l2nsc
        Just (L1.L1NamespaceContents childNSC) <- (M.lookup childNSID . mtsL2ToL1Map) <$> get
        translateNSContents scope childNSID childNSC
        Just (L2.L2DomainReference _ tmpId) <-
              M.lookup nsName . L2.l2nsDomains <$> getNamespaceContents "translateNSContents-nsdr" thisNSID
        labs <- (L2.L2DomainExpressionDisjointUnion idss . L2.L2LabelledDomains .
                   (\n -> [(L2.L2Label childNSID (fromIntegral i), L2.L2DomainExpressionProduct idss (L2.L2LabelledDomains [])) |
                        i <- [0..(L2.l2nsNextLabel n)]])) <$>
                  getNamespaceContents "translateNSContents DU" childNSID
        modify $ \mts -> mts { mtsTempIDDomainType = M.insert tmpId labs
                                                              (mtsTempIDDomainType mts) }
        return ()
      L1.L1NSDomain { L1.l1nsSS = ss,
                      L1.l1nsDomainName = L1.L1Identifier idss ident,
                      L1.l1nsDomainDefinition = dd } -> do
        Just domNSID <- findExactNamespace thisNSID ident
        domStatement <- translateDomainDefinition scope ss domNSID dd
        Just (L2.L2DomainReference _ tmpId) <-
              M.lookup ident . L2.l2nsDomains <$> getNamespaceContents "tnsc domref" thisNSID
        modify $ \mts -> mts { mtsTempIDDomainType = M.insert tmpId domStatement (mtsTempIDDomainType mts) }
        modifyNamespaceContents thisNSID $ \l2nsc -> l2nsc {
          L2.l2nsDomains = M.insert ident domStatement (L2.l2nsDomains l2nsc)
          }
      L1.L1NSAssertion { L1.l1nsSS = ss, L1.l1nsExpression = ex } -> do
        l2ex <- translateExpression scope ss thisNSID ex
        modifyL2Model $ \mod -> mod { L2.l2AllAssertions = l2ex:(L2.l2AllAssertions mod) }
      L1.L1NSNamedValue { L1.l1nsSS = ss, L1.l1nsValueName = L1.L1Identifier { L1.l1IdBS = n },
                          L1.l1nsDomainType = mt } -> do
        l2t <- maybe (return Nothing) (\t -> Just <$> translateDomainExpression scope ss thisNSID t) mt
        Just valueID <- M.lookup n . L2.l2nsNamedValues <$> getNamespaceContents "tnsc nv" thisNSID
        modifyL2Model $ \mod -> mod {
          L2.l2AllValues = M.insert valueID (L2.L2ValueContents ss l2t) (L2.l2AllValues mod)
          }
      L1.L1NSClass { L1.l1nsSS = ss, L1.l1nsClassName = L1.L1Identifier _ n,
                     L1.l1nsClassParameters = p,
                     L1.l1nsClassDomainFunctions = df, L1.l1nsClassValues = cvs } -> do
        Just classID <- M.lookup n . L2.l2nsClasses <$> getNamespaceContents "tnsc c" thisNSID
        Just classContents <- (M.lookup classID . L2.l2AllClasses) <$> getL2Model
        let scope' = scope { siDomainIDMap = foldl' (\m ((l1id, _), (l2id, _)) ->
                                                      M.insert l1id l2id m) (siDomainIDMap scope) (zip p (L2.l2ClassParameters classContents))}
        forM_ cvs $ \(L1.L1Identifier cvss cvName, cvtype) -> do
          let Just cvID = M.lookup cvName (L2.l2ClassValues classContents)
          l2cvtype <- translateDomainExpression scope' ss thisNSID cvtype
          modifyL2Model $ \mod -> mod {
            L2.l2AllClassValues = M.insert cvID (L2.L2ClassValueContents cvss l2cvtype)
                                                (L2.l2AllClassValues mod)
            }
      L1.L1NSEnsemble {} -> return ()
      L1.L1NSUnit { L1.l1nsSS = ss, L1.l1nsUnitName = L1.L1Identifier uss n, L1.l1nsUnitDefinition = d } -> do
        l2uDef <- translateUnitDefinition scope ss thisNSID d
        Just (L2.L2UnitExRef _ tmpId) <- M.lookup n . L2.l2nsUnits <$> getNamespaceContents "tnsc u" thisNSID
        modify $ \mts -> mts { mtsTempIDUnitEx = M.insert tmpId l2uDef (mtsTempIDUnitEx mts) }
        modifyNamespaceContents thisNSID $ \l2nsc -> l2nsc {
            L2.l2nsUnits = M.insert n l2uDef (L2.l2nsUnits l2nsc)
          }
      L1.L1NSInstance { L1.l1nsSS = ss, L1.l1nsInstanceOfClass = cpath,
                        L1.l1nsClassArguments = args, L1.l1nsInstanceDomainFunctions = dfs,
                        L1.l1nsInstanceValues = vals } -> do
        -- Find the class...
        classID <-
          (findScopedSymbolByRAPath ss thisNSID cpath $ \nsc className ->
            M.lookup (L1.l1IdBS className) (L2.l2nsClasses nsc))
        Just class' <- (M.lookup classID . L2.l2AllClasses) <$> getL2Model
        l2args <- mapM (translateDomainExpression scope ss thisNSID) args
        l2dfs <- mapM (\(L1.L1Identifier ifss instfuncident, dts, dex) ->
                            (,,) <$>
                              (case M.lookup instfuncident (L2.l2ClassDomainFunctions class') of
                                  Nothing -> fail $ "Instance refers to domain function " ++
                                                    (BSC.unpack instfuncident) ++ " at " ++
                                                    (show ifss) ++ ", but that domain function " ++
                                                    "could not be found in the class."
                                  Just ifid -> return ifid
                              ) <*>
                              (mapM (translateDomainExpression scope ss thisNSID) dts) <*>
                              translateDomainExpression scope ss thisNSID dex
                          ) dfs
        l2exprs <- mapM (translateExpression scope ss thisNSID) vals
        modifyL2Model $ \mod ->
          mod { L2.l2AllInstances = 
                       (L2.L2InstanceContents { L2.l2InstanceSS = ss,
                                                L2.l2InstanceOfClass = classID, 
                                                L2.l2InstanceClassArguments = l2args,
                                                L2.l2InstanceDomainFunctions = l2dfs,
                                                L2.l2InstanceValues = l2exprs }):
                       (L2.l2AllInstances mod) }
        return ()
  
-- | Translates an L1Label to an L2Expression.
translateNSContents :: ScopeInformation -> L2.L2NamespaceID -> [L1.L1NamespaceStatement] -> ModelTranslation ()
translateNSContents scope thisNSID nsc =
  forM_ nsc $ \nss ->
    case nss of
      -- Note: We assume all remaining imports are local due to
      -- processNamespaceImports, and have no 'as' clause because
      -- of handleL1SimpleSyntacticSugar...
      L1.L1NSImport {} ->
        -- Imports are already done.
        return ()
      L1.L1NSNamespace { L1.l1nsNamespaceName = nsNameL1@(L1.L1Identifier idss nsName) } -> do
        l2nsc <- getNamespaceContents "translateNSContents - namespace" thisNSID
        let Just childNSID = (M.lookup nsName . L2.l2nsNamespaces) l2nsc
        Just (L1.L1NamespaceContents childNSC) <- (M.lookup childNSID . mtsL2ToL1Map) <$> get
        translateNSContents scope childNSID childNSC
        Just (L2.L2DomainReference _ tmpId) <-
              M.lookup nsName . L2.l2nsDomains <$> getNamespaceContents "translateNSContents-nsdr" thisNSID
        labs <- (L2.L2DomainExpressionDisjointUnion idss . L2.L2LabelledDomains .
                   (\n -> [(L2.L2Label childNSID (fromIntegral i), L2.L2DomainExpressionProduct idss (L2.L2LabelledDomains [])) |
                        i <- [0..(L2.l2nsNextLabel n)]])) <$>
                  getNamespaceContents "translateNSContents DU" childNSID
        modify $ \mts -> mts { mtsTempIDDomainType = M.insert tmpId labs
                                                              (mtsTempIDDomainType mts) }
        return ()
      L1.L1NSDomain { L1.l1nsSS = ss,
                      L1.l1nsDomainName = L1.L1Identifier idss ident,
                      L1.l1nsDomainDefinition = dd } -> do
        Just domNSID <- findExactNamespace thisNSID ident
        domStatement <- translateDomainDefinition scope ss domNSID dd
        Just (L2.L2DomainReference _ tmpId) <-
              M.lookup ident . L2.l2nsDomains <$> getNamespaceContents "tnsc domref" thisNSID
        modify $ \mts -> mts { mtsTempIDDomainType = M.insert tmpId domStatement (mtsTempIDDomainType mts) }
        modifyNamespaceContents thisNSID $ \l2nsc -> l2nsc {
          L2.l2nsDomains = M.insert ident domStatement (L2.l2nsDomains l2nsc)
          }
      L1.L1NSAssertion { L1.l1nsSS = ss, L1.l1nsExpression = ex } -> do
        l2ex <- translateExpression scope ss thisNSID ex
        modifyL2Model $ \mod -> mod { L2.l2AllAssertions = l2ex:(L2.l2AllAssertions mod) }
      L1.L1NSNamedValue { L1.l1nsSS = ss, L1.l1nsValueName = L1.L1Identifier { L1.l1IdBS = n },
                          L1.l1nsDomainType = mt } -> do
        l2t <- maybe (return Nothing) (\t -> Just <$> translateDomainExpression scope ss thisNSID t) mt
        Just valueID <- M.lookup n . L2.l2nsNamedValues <$> getNamespaceContents "tnsc nv" thisNSID
        modifyL2Model $ \mod -> mod {
          L2.l2AllValues = M.insert valueID (L2.L2ValueContents ss l2t) (L2.l2AllValues mod)
          }
      L1.L1NSClass { L1.l1nsSS = ss, L1.l1nsClassName = L1.L1Identifier _ n,
                     L1.l1nsClassParameters = p,
                     L1.l1nsClassDomainFunctions = df, L1.l1nsClassValues = cvs } -> do
        Just classID <- M.lookup n . L2.l2nsClasses <$> getNamespaceContents "tnsc c" thisNSID
        Just classContents <- (M.lookup classID . L2.l2AllClasses) <$> getL2Model
        let scope' = scope { siDomainIDMap = foldl' (\m ((l1id, _), (l2id, _)) ->
                                                      M.insert l1id l2id m) (siDomainIDMap scope) (zip p (L2.l2ClassParameters classContents))}
        forM_ cvs $ \(L1.L1Identifier cvss cvName, cvtype) -> do
          let Just cvID = M.lookup cvName (L2.l2ClassValues classContents)
          l2cvtype <- translateDomainExpression scope' ss thisNSID cvtype
          modifyL2Model $ \mod -> mod {
            L2.l2AllClassValues = M.insert cvID (L2.L2ClassValueContents cvss l2cvtype)
                                                (L2.l2AllClassValues mod)
            }
      L1.L1NSEnsemble {} -> return ()
      L1.L1NSUnit { L1.l1nsSS = ss, L1.l1nsUnitName = L1.L1Identifier uss n, L1.l1nsUnitDefinition = d } -> do
        l2uDef <- translateUnitDefinition scope ss thisNSID d
        Just (L2.L2UnitExRef _ tmpId) <- M.lookup n . L2.l2nsUnits <$> getNamespaceContents "tnsc u" thisNSID
        modify $ \mts -> mts { mtsTempIDUnitEx = M.insert tmpId l2uDef (mtsTempIDUnitEx mts) }
        modifyNamespaceContents thisNSID $ \l2nsc -> l2nsc {
            L2.l2nsUnits = M.insert n l2uDef (L2.l2nsUnits l2nsc)
          }
      L1.L1NSInstance { L1.l1nsSS = ss, L1.l1nsInstanceOfClass = cpath,
                        L1.l1nsClassArguments = args, L1.l1nsInstanceDomainFunctions = dfs,
                        L1.l1nsInstanceValues = vals } -> do
        -- Find the class...
        classID <-
          (findScopedSymbolByRAPath ss thisNSID cpath $ \nsc className ->
            M.lookup (L1.l1IdBS className) (L2.l2nsClasses nsc))
        Just class' <- (M.lookup classID . L2.l2AllClasses) <$> getL2Model
        l2args <- mapM (translateDomainExpression scope ss thisNSID) args
        l2dfs <- mapM (\(L1.L1Identifier ifss instfuncident, dts, dex) ->
                            (,,) <$>
                              (case M.lookup instfuncident (L2.l2ClassDomainFunctions class') of
                                  Nothing -> fail $ "Instance refers to domain function " ++
                                                    (BSC.unpack instfuncident) ++ " at " ++
                                                    (show ifss) ++ ", but that domain function " ++
                                                    "could not be found in the class."
                                  Just ifid -> return ifid
                              ) <*>
                              (mapM (translateDomainExpression scope ss thisNSID) dts) <*>
                              translateDomainExpression scope ss thisNSID dex
                          ) dfs
        l2exprs <- mapM (translateExpression scope ss thisNSID) vals
        modifyL2Model $ \mod ->
          mod { L2.l2AllInstances = 
                       (L2.L2InstanceContents { L2.l2InstanceSS = ss,
                                                L2.l2InstanceOfClass = classID, 
                                                L2.l2InstanceClassArguments = l2args,
                                                L2.l2InstanceDomainFunctions = l2dfs,
                                                L2.l2InstanceValues = l2exprs }):
                       (L2.l2AllInstances mod) }
        return ()
  
-- | Translates an L1Label to an L2Expression.
translateNSContents scope thisNSID nsc =
  forM_ nsc $ \nss ->
    case nss of
      -- Note: We assume all remaining imports are local due to
      -- processNamespaceImports, and have no 'as' clause because
      -- of handleL1SimpleSyntacticSugar...
      L1.L1NSImport {} ->
        -- Imports are already done.
        return ()
      L1.L1NSNamespace { L1.l1nsNamespaceName = nsNameL1@(L1.L1Identifier idss nsName) } -> do
        l2nsc <- getNamespaceContents "translateNSContents - namespace" thisNSID
        let Just childNSID = (M.lookup nsName . L2.l2nsNamespaces) l2nsc
        Just (L1.L1NamespaceContents childNSC) <- (M.lookup childNSID . mtsL2ToL1Map) <$> get
        translateNSContents scope childNSID childNSC
        Just (L2.L2DomainReference _ tmpId) <-
              M.lookup nsName . L2.l2nsDomains <$> getNamespaceContents "translateNSContents-nsdr" thisNSID
        labs <- (L2.L2DomainExpressionDisjointUnion idss . L2.L2LabelledDomains .
                   (\n -> [(L2.L2Label childNSID (fromIntegral i), L2.L2DomainExpressionProduct idss (L2.L2LabelledDomains [])) |
                        i <- [0..(L2.l2nsNextLabel n)]])) <$>
                  getNamespaceContents "translateNSContents DU" childNSID
        modify $ \mts -> mts { mtsTempIDDomainType = M.insert tmpId labs
                                                              (mtsTempIDDomainType mts) }
        return ()
      L1.L1NSDomain { L1.l1nsSS = ss,
                      L1.l1nsDomainName = L1.L1Identifier idss ident,
                      L1.l1nsDomainDefinition = dd } -> do
        Just domNSID <- findExactNamespace thisNSID ident
        domStatement <- translateDomainDefinition scope ss domNSID dd
        Just (L2.L2DomainReference _ tmpId) <-
              M.lookup ident . L2.l2nsDomains <$> getNamespaceContents "tnsc domref" thisNSID
        modify $ \mts -> mts { mtsTempIDDomainType = M.insert tmpId domStatement (mtsTempIDDomainType mts) }
        modifyNamespaceContents thisNSID $ \l2nsc -> l2nsc {
          L2.l2nsDomains = M.insert ident domStatement (L2.l2nsDomains l2nsc)
          }
      L1.L1NSAssertion { L1.l1nsSS = ss, L1.l1nsExpression = ex } -> do
        l2ex <- translateExpression scope ss thisNSID ex
        modifyL2Model $ \mod -> mod { L2.l2AllAssertions = l2ex:(L2.l2AllAssertions mod) }
      L1.L1NSNamedValue { L1.l1nsSS = ss, L1.l1nsValueName = L1.L1Identifier { L1.l1IdBS = n },
                          L1.l1nsDomainType = mt } -> do
        l2t <- maybe (return Nothing) (\t -> Just <$> translateDomainExpression scope ss thisNSID t) mt
        Just valueID <- M.lookup n . L2.l2nsNamedValues <$> getNamespaceContents "tnsc nv" thisNSID
        modifyL2Model $ \mod -> mod {
          L2.l2AllValues = M.insert valueID (L2.L2ValueContents ss l2t) (L2.l2AllValues mod)
          }
      L1.L1NSClass { L1.l1nsSS = ss, L1.l1nsClassName = L1.L1Identifier _ n,
                     L1.l1nsClassParameters = p,
                     L1.l1nsClassDomainFunctions = df, L1.l1nsClassValues = cvs } -> do
        Just classID <- M.lookup n . L2.l2nsClasses <$> getNamespaceContents "tnsc c" thisNSID
        Just classContents <- (M.lookup classID . L2.l2AllClasses) <$> getL2Model
        let scope' = scope { siDomainIDMap = foldl' (\m ((l1id, _), (l2id, _)) ->
                                                      M.insert l1id l2id m) (siDomainIDMap scope) (zip p (L2.l2ClassParameters classContents))}
        forM_ cvs $ \(L1.L1Identifier cvss cvName, cvtype) -> do
          let Just cvID = M.lookup cvName (L2.l2ClassValues classContents)
          l2cvtype <- translateDomainExpression scope' ss thisNSID cvtype
          modifyL2Model $ \mod -> mod {
            L2.l2AllClassValues = M.insert cvID (L2.L2ClassValueContents cvss l2cvtype)
                                                (L2.l2AllClassValues mod)
            }
      L1.L1NSEnsemble {} -> return ()
      L1.L1NSUnit { L1.l1nsSS = ss, L1.l1nsUnitName = L1.L1Identifier uss n, L1.l1nsUnitDefinition = d } -> do
        l2uDef <- translateUnitDefinition scope ss thisNSID d
        Just (L2.L2UnitExRef _ tmpId) <- M.lookup n . L2.l2nsUnits <$> getNamespaceContents "tnsc u" thisNSID
        modify $ \mts -> mts { mtsTempIDUnitEx = M.insert tmpId l2uDef (mtsTempIDUnitEx mts) }
        modifyNamespaceContents thisNSID $ \l2nsc -> l2nsc {
            L2.l2nsUnits = M.insert n l2uDef (L2.l2nsUnits l2nsc)
          }
      L1.L1NSInstance { L1.l1nsSS = ss, L1.l1nsInstanceOfClass = cpath,
                        L1.l1nsClassArguments = args, L1.l1nsInstanceDomainFunctions = dfs,
                        L1.l1nsInstanceValues = vals } -> do
        -- Find the class...
        classID <-
          (findScopedSymbolByRAPath ss thisNSID cpath $ \nsc className ->
            M.lookup (L1.l1IdBS className) (L2.l2nsClasses nsc))
        Just class' <- (M.lookup classID . L2.l2AllClasses) <$> getL2Model
        l2args <- mapM (translateDomainExpression scope ss thisNSID) args
        l2dfs <- mapM (\(L1.L1Identifier ifss instfuncident, dts, dex) ->
                            (,,) <$>
                              (case M.lookup instfuncident (L2.l2ClassDomainFunctions class') of
                                  Nothing -> fail $ "Instance refers to domain function " ++
                                                    (BSC.unpack instfuncident) ++ " at " ++
                                                    (show ifss) ++ ", but that domain function " ++
                                                    "could not be found in the class."
                                  Just ifid -> return ifid
                              ) <*>
                              (mapM (translateDomainExpression scope ss thisNSID) dts) <*>
                              translateDomainExpression scope ss thisNSID dex
                          ) dfs
        l2exprs <- mapM (translateExpression scope ss thisNSID) vals
        modifyL2Model $ \mod ->
          mod { L2.l2AllInstances = 
                       (L2.L2InstanceContents { L2.l2InstanceSS = ss,
                                                L2.l2InstanceOfClass = classID, 
                                                L2.l2InstanceClassArguments = l2args,
                                                L2.l2InstanceDomainFunctions = l2dfs,
                                                L2.l2InstanceValues = l2exprs }):
                       (L2.l2AllInstances mod) }
        return ()
  
-- | Translates an L1Label to an L2Expression.