# -*- coding: utf-8 -*-
"""
    sphinx.config
    ~~~~~~~~~~~~~

    Build configuration file handling.

    :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
    :license: BSD, see LICENSE for details.
"""

import os
import re
import sys
from os import path

from sphinx.errors import ConfigError
from sphinx.locale import l_
from sphinx.util.osutil import make_filename
from sphinx.util.pycompat import bytes, b, convert_with_2to3

nonascii_re = re.compile(b(r'[\x80-\xff]'))

CONFIG_SYNTAX_ERROR = "There is a syntax error in your configuration file: %s"
if sys.version_info >= (3, 0):
    CONFIG_SYNTAX_ERROR += "\nDid you change the syntax from 2.x to 3.x?"

class Config(object):
    """
    Configuration file abstraction.
    """

    # the values are: (default, what needs to be rebuilt if changed)

    # If you add a value here, don't forget to include it in the
    # quickstart.py file template as well as in the docs!

    config_values = dict(
        # general options
        project = ('Python', 'env'),
        copyright = ('', 'html'),
        version = ('', 'env'),
        release = ('', 'env'),
        today = ('', 'env'),
        today_fmt = (None, 'env'),  # the real default is locale-dependent

        language = (None, 'env'),
        locale_dirs = ([], 'env'),

        master_doc = ('contents', 'env'),
        source_suffix = ('.rst', 'env'),
        source_encoding = ('utf-8-sig', 'env'),
        exclude_patterns = ([], 'env'),
        # the next three are all deprecated now
        unused_docs = ([], 'env'),
        exclude_trees = ([], 'env'),
        exclude_dirnames = ([], 'env'),
        default_role = (None, 'env'),
        add_function_parentheses = (True, 'env'),
        add_module_names = (True, 'env'),
        trim_footnote_reference_space = (False, 'env'),
        show_authors = (False, 'env'),
        pygments_style = (None, 'html'),
        highlight_language = ('python', 'env'),
        templates_path = ([], 'html'),
        template_bridge = (None, 'html'),
        keep_warnings = (False, 'env'),
        modindex_common_prefix = ([], 'html'),
        rst_epilog = (None, 'env'),
        rst_prolog = (None, 'env'),
        trim_doctest_flags = (True, 'env'),
        primary_domain = ('py', 'env'),
        needs_sphinx = (None, None),
        nitpicky = (False, 'env'),
        nitpick_ignore = ([], 'env'),

        # HTML options
        html_theme = ('default', 'html'),
        html_theme_path = ([], 'html'),
        html_theme_options = ({}, 'html'),
        html_title = (lambda self: l_('%s %s documentation') %
                                   (self.project, self.release),
                      'html'),
        html_short_title = (lambda self: self.html_title, 'html'),
        html_style = (None, 'html'),
        html_logo = (None, 'html'),
        html_favicon = (None, 'html'),
        html_static_path = ([], 'html'),
        # the real default is locale-dependent
        html_last_updated_fmt = (None, 'html'),
        html_use_smartypants = (True, 'html'),
        html_translator_class = (None, 'html'),
        html_sidebars = ({}, 'html'),
        html_additional_pages = ({}, 'html'),
        html_use_modindex = (True, 'html'),  # deprecated
        html_domain_indices = (True, 'html'),
        html_add_permalinks = (u'\u00B6', 'html'),
        html_use_index = (True, 'html'),
        html_split_index = (False, 'html'),
        html_copy_source = (True, 'html'),
        html_show_sourcelink = (True, 'html'),
        html_use_opensearch = ('', 'html'),
        html_file_suffix = (None, 'html'),
        html_link_suffix = (None, 'html'),
        html_show_copyright = (True, 'html'),
        html_show_sphinx = (True, 'html'),
        html_context = ({}, 'html'),
        html_output_encoding = ('utf-8', 'html'),
        html_compact_lists = (True, 'html'),
        html_secnumber_suffix = ('. ', 'html'),
        html_search_language = (None, 'html'),
        html_search_options = ({}, 'html'),

        # HTML help only options
        htmlhelp_basename = (lambda self: make_filename(self.project), None),

        # Qt help only options
        qthelp_basename = (lambda self: make_filename(self.project), None),

        # Devhelp only options
        devhelp_basename = (lambda self: make_filename(self.project), None),

        # Epub options
        epub_basename = (lambda self: make_filename(self.project), None),
        epub_theme = ('epub', 'html'),
        epub_title = (lambda self: self.html_title, 'html'),
        epub_author = ('unknown', 'html'),
        epub_language = (lambda self: self.language or 'en', 'html'),
        epub_publisher = ('unknown', 'html'),
        epub_copyright = (lambda self: self.copyright, 'html'),
        epub_identifier = ('unknown', 'html'),
        epub_scheme = ('unknown', 'html'),
        epub_uid = ('unknown', 'env'),
        epub_cover = ((), 'env'),
        epub_pre_files = ([], 'env'),
        epub_post_files = ([], 'env'),
        epub_exclude_files = ([], 'env'),
        epub_tocdepth = (3, 'env'),
        epub_tocdup = (True, 'env'),

        # LaTeX options
        latex_documents = ([], None),
        latex_logo = (None, None),
        latex_appendices = ([], None),
        latex_use_parts = (False, None),
        latex_use_modindex = (True, None),  # deprecated
        latex_domain_indices = (True, None),
        latex_show_urls = ('no', None),
        latex_show_pagerefs = (False, None),
        # paper_size and font_size are still separate values
        # so that you can give them easily on the command line
        latex_paper_size = ('letter', None),
        latex_font_size = ('10pt', None),
        latex_elements = ({}, None),
        latex_additional_files = ([], None),
        latex_docclass = ({}, None),
        # now deprecated - use latex_elements
        latex_preamble = ('', None),

        # text options
        text_sectionchars = ('*=-~"+`', 'env'),
        text_newlines = ('unix', 'env'),

        # manpage options
        man_pages = ([], None),
        man_show_urls = (False, None),

        # Texinfo options
        texinfo_documents = ([], None),
        texinfo_appendices = ([], None),
        texinfo_elements = ({}, None),
        texinfo_domain_indices = (True, None),
        texinfo_show_urls = ('footnote', None),

        # linkcheck options
        linkcheck_ignore = ([], None),
        linkcheck_timeout = (None, None),
        linkcheck_workers = (5, None),

        # gettext options
        gettext_compact = (True, 'gettext'),
    )

    def __init__(self, dirname, filename, overrides, tags):
        self.overrides = overrides
        self.values = Config.config_values.copy()
        config = {}
        if dirname is not None:
            config_file = path.join(dirname, filename)
            config['__file__'] = config_file
            config['tags'] = tags
            olddir = os.getcwd()
            try:
                # we promise to have the config dir as current dir while the
                # config file is executed
                os.chdir(dirname)
                # get config source -- 'b' is a no-op under 2.x, while 'U' is
                # ignored under 3.x (but 3.x compile() accepts \r\n newlines)
                f = open(config_file, 'rbU')
                try:
                    source = f.read()
                finally:
                    f.close()
                try:
                    # compile to a code object, handle syntax errors
                    try:
                        code = compile(source, config_file, 'exec')
                    except SyntaxError:
                        if convert_with_2to3:
                            # maybe the file uses 2.x syntax; try to refactor to
                            # 3.x syntax using 2to3
                            source = convert_with_2to3(config_file)
                            code = compile(source, config_file, 'exec')
                        else:
                            raise
                    exec code in config
                except SyntaxError, err:
                    raise ConfigError(CONFIG_SYNTAX_ERROR % err)
            finally:
                os.chdir(olddir)

        self._raw_config = config
        # these two must be preinitialized because extensions can add their
        # own config values
        self.setup = config.get('setup', None)
        self.extensions = config.get('extensions', [])

    def check_unicode(self, warn):
        # check all string values for non-ASCII characters in bytestrings,
        # since that can result in UnicodeErrors all over the place
        for name, value in self._raw_config.iteritems():
            if isinstance(value, bytes) and nonascii_re.search(value):
                warn('the config value %r is set to a string with non-ASCII '
                     'characters; this can lead to Unicode errors occurring. '
                     'Please use Unicode strings, e.g. %r.' % (name, u'Content')
                )

    def init_values(self):
        config = self._raw_config
        for valname, value in self.overrides.iteritems():
            if '.' in valname:
                realvalname, key = valname.split('.', 1)
                config.setdefault(realvalname, {})[key] = value
            else:
                config[valname] = value
        for name in config:
            if name in self.values:
                self.__dict__[name] = config[name]
        del self._raw_config

    def __getattr__(self, name):
        if name.startswith('_'):
            raise AttributeError(name)
        if name not in self.values:
            raise AttributeError('No such config value: %s' % name)
        default = self.values[name][0]
        if hasattr(default, '__call__'):
            return default(self)
        return default

    def __getitem__(self, name):
        return getattr(self, name)

    def __setitem__(self, name, value):
        setattr(self, name, value)

    def __delitem__(self, name):
        delattr(self, name)

    def __contains__(self, name):
        return name in self.values
