 topLevel = (ctx, form) ->
   definitionList ctx, spaceSeparatedPairs form
 
-topLevelModule = (moduleName, defaultImports) -> (ctx, form) ->
-  [(jsAssignStatement (jsAccess "Shem", (validIdentifier moduleName)),
-    (exportAll ctx, (join (importAny defaultImports), (topLevel ctx, form))))]
+topLevelModule = (typedModulePath, defaultImports) -> (ctx, form) ->
+  # [(jsAssignStatement (jsAccess "Shem", (validIdentifier moduleName)),
+  #   (exportAll ctx, (join (importAny defaultImports), (topLevel ctx, form))))]
+  definitions = (join (importAny defaultImports), (topLevel ctx, form))
+  shouldBeExported = (name, declaration) ->
+    not declaration.virtual and declaration.final and isExported name
+  isExported = (name) ->
+    inSet ctx._scope().exportedNames, name
+  nonVirtual = filterMap shouldBeExported, ctx._scope()
+  exported = concatSets (subtractSets nonVirtual, builtInDefinitions()),
+    (filterSet isExported, ctx.submodules) # TODO: separate exporting namespaces according to the separation of namespaces
+  exportList = map validIdentifier, (setToArray exported)
+  exportDictionary = (jsDictionary exportList, exportList)
+  [..., type] = typedModulePath.types
+  switch type
+    when 'commonJs'
+      (concat [
+        toJsString 'use strict'
+        definitions
+        [(jsAssignStatement 'module.exports', exportDictionary)]])
+    when 'browser'
+      [(jsAssignStatement (jsAccess "Shem", (validIdentifier (modulePathToName typedModulePath.names))),
+        (iife (concat [
+          toJsString 'use strict'
+          definitions
+          [(jsReturn exportDictionary)]])))]
+    when 'submodule'
+      [..., name] = typedModulePath.names
+      [(jsVarDeclaration (validIdentifier name),
+        (iife (join definitions, [(jsReturn exportDictionary)])))]
+    else
+      throw new Error "Unspecified ctx.moduleCompilationType"
+
+importAny = (defaultImports) ->
+  concat (for name, names of values defaultImports
+    irImport name, names)
 
 topLevelExpressionInModule = (defaultImports) -> (ctx, expression) ->
   (iife (concat [

    @classParams = newMap()
    @types = []
    @isMalformed = no
    @moduleName = null
    @importedModules = newSet()
    @_requested = newMap()


    (toMatchTypes (applyKindFn hashmapType, newVar(), newVar()), type) or
    (toMatchTypes (applyKindFn hashsetType, newVar()), type)


# Possible compilations:
#
#   var _temp = require('Some');
#
#   var _temp = require('./Some');
#
#   var _temp = require('./Some/Other')
#
#   var _temp = Some;
#
#   var _temp = Shem.Some;
#
#   // [a] (req ./Some/Submodule)              => I need to know the type of the module in the path
#   var _temp = require('./Some').Submodule;
#
irImport = (moduleName, names, moduleNameAtom) ->
  {ir: irImportTranslate, moduleName, names, moduleNameAtom}

irImportTranslate = (ctx, {moduleName, names, moduleNameAtom}) ->
  # TODO: pass this info in through context instead of a direct call
  importedTypedModulePath = moduleNameToTypedModulePath moduleName, ctx.typedModulePath
  if not importedTypedModulePath
    return malformed ctx, moduleNameAtom, "Module #{moduleName} could not be found."
  else if importedTypedModulePath.nonExportedLink
    return malformed ctx, moduleNameAtom, "Module #{importedTypedModulePath.nonExportedLink} must be exported."
  {names: pathNames, types} = importedTypedModulePath
  baseType = types[i]
  numModules = (filter ((type) -> type is baseType), types).length
  moduleHandle =
    switch baseType
      when 'commonJs'
        jsCall 'require', (toJsString (pathNames[0...numModules].join '/'))
      when 'browser'
        acc = "Shem"
        for i in [0...numModules]
          acc = (jsAccess acc, validIdentifier pathNames[i])
        acc
  temp = ctx.newJsVariable()
  join [jsVarDeclaration temp, moduleHandle],
    for name in names
      validName = (validIdentifier name)
      jsVarDeclaration validName, (jsAccess temp, validName)

isTypeAnnotation = (expression) ->
  (isCall expression) and (':' is _symbol _operator expression)
