# Copyright 2004, 2009 Toby Dickenson
# Copyright 2014-2015 Aaron Gallagher
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject
# to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import collections
import functools
import json
import os
import modulefinder
import sys
import tempfile

from twisted.python import reflect


class mymf(modulefinder.ModuleFinder):
    def __init__(self, *args, **kwargs):
        self._depgraph = collections.defaultdict(set)
        self._types = {}
        self._last_caller = None
        modulefinder.ModuleFinder.__init__(self, *args, **kwargs)

    def import_hook(self, name, caller=None, fromlist=None, level=None):
        old_last_caller = self._last_caller
        try:
            self._last_caller = caller
            return modulefinder.ModuleFinder.import_hook(
                self, name, caller, fromlist)
        finally:
            self._last_caller = old_last_caller

    def import_module(self, partnam, fqname, parent):
        if partnam.endswith('_py3'):
            return None
        r = modulefinder.ModuleFinder.import_module(
            self, partnam, fqname, parent)
        last_caller = self._last_caller
        if r is not None and 'allmydata' in r.__name__:
            if last_caller is None or last_caller.__name__ == '__main__':
                self._depgraph[fqname]
            else:
                self._depgraph[last_caller.__name__].add(fqname)
        return r

    def load_module(self, fqname, fp, pathname, additional_info):
        (suffix, mode, type) = additional_info
        r = modulefinder.ModuleFinder.load_module(
            self, fqname, fp, pathname, (suffix, mode, type))
        if r is not None:
            self._types[r.__name__] = type
        return r

    def as_json(self):
        return {
            'depgraph': {
                name: dict.fromkeys(deps, 1)
                for name, deps in self._depgraph.items()},
            'types': self._types,
        }


json_dump = functools.partial(
    json.dump, indent=4, separators=(',', ': '), sort_keys=True)


def main(target):
    mf = mymf(sys.path[:], 0, [])

    moduleNames = []
    for path, dirnames, filenames in os.walk(os.path.join(target, 'src', 'allmydata')):
        if 'test' in dirnames:
            dirnames.remove('test')
        for filename in filenames:
            if not filename.endswith('.py'):
                continue
            if filename in ('setup.py',):
                continue
            if '-' in filename:
                # a script like update-documentation.py
                continue
            if filename != '__init__.py':
                filepath = os.path.join(path, filename)
            else:
                filepath = path
            moduleNames.append(reflect.filenameToModuleName(filepath))

    with tempfile.NamedTemporaryFile("w") as tmpfile:
        for moduleName in moduleNames:
            tmpfile.write('import %s\n' % moduleName)
        tmpfile.flush()
        mf.run_script(tmpfile.name)

    with open('tahoe-deps.json', 'w') as outfile:
        json_dump(mf.as_json(), outfile)
        outfile.write('\n')

    ported_modules_path = os.path.join(target, "src", "allmydata", "util", "_python3.py")
    with open(ported_modules_path) as f:
        ported_modules = {}
        exec(f.read(), ported_modules, ported_modules)
        port_status = dict.fromkeys(
            ported_modules["PORTED_MODULES"] + ported_modules["PORTED_TEST_MODULES"],
            "ported"
        )
    with open('tahoe-ported.json', 'w') as outfile:
        json_dump(port_status, outfile)
        outfile.write('\n')


if __name__ == '__main__':
    main(*sys.argv[1:])
