# Copyright 2016-2017 ActiveState, Inc. All rights reserved.

"""Python 2 and Python 3 stdlib importer."""

import os

from symbols import AbstractScope, AbstractModule
from language.common import Keyword, Module
from language.legacy.cix import Cix

from SilverCity.Keywords import python_keywords, python3_keywords

# The DB file path to store Python 2 stdlib symbols under.
PYTHON2_STDLIB_FILE = ":python2:"
# The DB file path to store Python 3 stdlib symbols under.
PYTHON3_STDLIB_FILE = ":python3:"

# The DB file paths to store legacy catalogs under.
CATALOG_PYWIN32_FILE = ":catalog_pywin32:"
CATALOG_PYTHON3_PYWIN32_FILE = ":catalog_python3_pywin32:"

# Python 2's keywords to add to the stdlib.
PYTHON2_KEYWORDS = python_keywords.split()
# Python 3's keywords to add to the stdlib.
PYTHON3_KEYWORDS = python3_keywords.split()

def scan(stdlib_file):
    """Imports the Python 2 and Python 3 stdlib into the database."""
    if stdlib_file == PYTHON2_STDLIB_FILE or stdlib_file == PYTHON3_STDLIB_FILE:
        stdlib_files = {
            PYTHON2_STDLIB_FILE: "python2.cix",
            PYTHON3_STDLIB_FILE: "python3.cix"
        }
        keywords = {
            PYTHON2_STDLIB_FILE: PYTHON2_KEYWORDS,
            PYTHON3_STDLIB_FILE: PYTHON3_KEYWORDS
        }
        # Import the stdlib.
        f = open(os.path.join(os.path.dirname(__file__), stdlib_files[stdlib_file]))
        stdlib_scope = Cix(f.read()).parse().values()[0]
        f.close()
        # The stdlib has many modules, including a "*" module. Move it into the main
        # scope.
        stdlib_scope.members.update(stdlib_scope.members["*"].members)
        for symbol in stdlib_scope.members["*"].members.values():
            # Adjust enclosingScope field appropriately.
            if isinstance(symbol, AbstractScope):
                symbol._enclosingScope = stdlib_scope
        del stdlib_scope.members["*"]
        # Since some stdlib modules have "." in their names (e.g. "os.path" and
        # "xml.dom"), merge them into true Module Symbols (e.g. "os" and "xml"
        # Modules that contain "path" and "dom" sub-Modules, respectively).
        for k, v in stdlib_scope.members.items():
            if "." in k and isinstance(v, AbstractModule):
                # Find or create the true Module for this stdlib module.
                name_parts = k.split(".")
                symbol = stdlib_scope.resolveMember(name_parts[0])
                if not symbol:
                    symbol = Module(name_parts[0])
                    stdlib_scope.define(symbol)
                for name_part in name_parts[1:]:
                    if not isinstance(symbol.resolveMember(name_part), AbstractScope):
                        if symbol.resolveMember(name_part):
                            # Likely from an '<import ... module="foo.bar"/>' where
                            # '<scope ilk="blob" ... name="foo.bar">' will be
                            # processed later. Delete the existing symbol to make
                            # way.
                            del symbol.members[name_part]
                        symbol = Module(name_part, symbol)
                        symbol.enclosingScope.define(symbol)
                    else:
                        symbol = symbol.resolveMember(name_part)
                # Add the stdlib module's members to the true Module and delete the
                # former.
                symbol.merge(v)
                del stdlib_scope.members[k]
        # Add keywords.
        for keyword in keywords[stdlib_file]:
            stdlib_scope.define(Keyword(keyword))
    else:
        catalogs = {
            CATALOG_PYWIN32_FILE: "pywin32.cix",
            CATALOG_PYTHON3_PYWIN32_FILE: "python3-pywin32.cix"
        }
        # Import the catalog.
        f = open(os.path.join(os.path.dirname(__file__), "catalogs", catalogs[stdlib_file]))
        stdlib_scope = Cix(f.read()).parse().values()[0].members.values()[0]
        f.close()

    # Store scope in database.
    # TODO: subject to change
    from db import Database
    Database.writeFileScope(stdlib_file, stdlib_scope)

if __name__ == "__main__":
    scan(PYTHON2_STDLIB_FILE)
