import sys
import os
import shutil
import inspect
import warnings

_INDENT_LEVEL = 4
_INDENT = ' ' * _INDENT_LEVEL
# Note: This should match a single indent used in the actual source code.

if sys.version_info.major == 2:
    from StringIO import StringIO as _StringIO

    class StringIO(_StringIO):
        def __enter__(self):
            return self

        def __exit__(self, exception_type, exception_value, traceback):
            '''
            :param exception_type: Type of exception that triggered the exit. 
            :param exception_value: Value of exception that triggered the exit.
            :param traceback: Traceback when exit was triggered.
            '''
            self.close()

else:
    from io import StringIO # pragma: no coverage

def _get_fresh_path(relative_path):
    '''
    Delete the contents of a given relative path then get its absolute path.

    :param relative_path: Relative path to be cleaned up and returned as an absolute path.
    :return: Absolute path of the given relative path.
    '''
    path = os.path.abspath(relative_path)
    if os.path.exists(path):
        shutil.rmtree(path)
    os.mkdir(path)
    return path

def _write_to_sidebar(file, name, link=None, depth=0):
    '''
    Add an item to the GitHub Wiki _Sidebar file as a link.

    :param file: _Sidebar file handle.
    :param name: Readable name of the item to be added.
    :param link: (optional) Link to the item of interest.
        If not specified, the item name will be used as the link.
    :param depth: (optional) Indentation level of the given item in the _Sidebar.
        Defaults to 0.
    '''
    link_content = name
    if link is not None and name != link:
        link_content += '|' + link

    file.write(_INDENT * depth + '* **[[%s]]**' % link_content)
    file.write('\n')

class _GenerateDocFiles(object):
    '''
    Functor that autogenerates Sphinx source files for each public object (with a docstring)
    under a given module. This also adds corresponding GitHub Wiki _Sidebar links for each
    generated file. 
    '''

    def __init__(self, module, doc_directory, sidebar_file):
        '''
        :param module: Module to parse and autogenerate documentation for.
        :param doc_directory: Sphinx directory to create the source files in.
        :param sidebar_file: GitHub Wiki _Sidebar file handle.
        '''
        self.module = module
        self.doc_directory = doc_directory
        self.source_ext = '.rst'
        self.sidebar_file = sidebar_file

    def __call__(self):
        '''
        :return: The extension of the autogenerated Sphinx source files.
        '''
        self._recurse(self.module, [])
        return self.source_ext

    def _recurse(self, element, ancestry):
        '''
        Helper function that recurses through the module tree to autogenerate documentation.

        :param element: Element to process.
        :param ancestry: Current ancestry of the element, as a list ordered from its highest
            ancestor to its immediate parent.
        '''
        for name, item in element.__dict__.items():
            # Skip private and non-public items:
            if name[0] == '_':
                continue

            # Skip items that don't have a docstring of their own:
            if item.__doc__ is None or item.__doc__ == getattr(getattr(item, '__class__', None), '__doc__', None):
                continue

            # Get this item's ancestry:
            new_ancestry = ancestry.copy()
            new_ancestry.append(name)
            full_name = '.'.join(new_ancestry)

            # Determine the proper Sphinx directive for the item:
            doc_type = None
            if inspect.isroutine(item): # TODO: Likely incomplete - this doesn't handle modules nor attributes, for example.
                doc_type = 'automethod'
            elif inspect.isclass(item):
                doc_type = 'autoclass'
            else:
                warnings.warn("`%s`'s type isn't supported in documentation (or it isn't implemented yet)." % full_name)
                continue

            # Generate the Sphinx source file for this item:
            with open('%s/%s%s' % (self.doc_directory, full_name, self.source_ext), 'w') as file:
                file.write(':orphan:\n\n')
                file.write('.. %s:: %s::%s\n' % (doc_type, self.module.__name__, full_name))

            # Add a link to this item in the _Sidebar file:
            _write_to_sidebar(self.sidebar_file, name, full_name, len(ancestry))

            # Continue parsing the module tree:
            self._recurse(item, new_ancestry)
