# -*- coding: utf-8 -*-
"""
    question
    ~~~~~~~~

    Allow questions to be inserted into your documentation.
    The questionlist directive collects
    all questions of your project and lists them along with a backlink to the
    original location.

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

global questno, qno
questno = 1
qno = 0

from docutils import nodes

from sphinx.locale import _
from sphinx.environment import NoUri
from sphinx.util.nodes import set_source_info
#from sphinx.util.compat import Directive #, make_admonition
from docutils.parsers.rst.directives.admonitions import Directive
#from docutils.parsers.rst.directives.admonitions import make_admonition

class question_node(nodes.Admonition, nodes.Element): pass
class questionlist(nodes.General, nodes.Element): pass

def make_admonition(node_class, name, arguments, options, content, lineno,
                       content_offset, block_text, state, state_machine):
    if not content:
        error = state_machine.reporter.error(
            'The "%s" admonition is empty; content required.' % (name),
            nodes.literal_block(block_text, block_text), line=lineno)
        return [error]
    text = '\n'.join(content)
    admonition_node = node_class(text)
    if arguments:
        title_text = arguments[0]
        textnodes, messages = state.inline_text(title_text, lineno)
        admonition_node += nodes.title(title_text, '', *textnodes)
        admonition_node += messages
        if 'class' in options:
            classes = options['class']
        else:
            classes = ['admonition-' + nodes.make_id(title_text)]
        admonition_node['classes'] += classes
    state.nested_parse(content, content_offset, admonition_node)
    return [admonition_node]

class Question(Directive):
    """
    A question entry, displayed in the form of an admonition.
    """

    has_content = True
    required_arguments = 0
    optional_arguments = 1
    final_argument_whitespace = False
    option_spec = {'number': int}

    def run(self):
        global questno
        questno = self.options.get('number', questno)
        env = self.state.document.settings.env
        targetid = 'index-%s' % env.new_serialno('index')
        targetnode = nodes.target('', '', ids=[targetid])

        self.options['class'] = [_('question')]
        ad = make_admonition(question_node, self.name,
                            [_('Question %d'%questno)], self.options,
                             self.content, self.lineno, self.content_offset,
                             self.block_text, self.state, self.state_machine)
        questno += 1
        set_source_info(self, ad[0])
        return [targetnode] + ad


def process_questions(app, doctree):
    # collect all questions in the environment
    # this is not done in the directive itself because it some transformations
    # must have already been run, e.g. substitutions
    env = app.builder.env
    if not hasattr(env, 'question_all_questions'):
        env.question_all_questions = []
    global qno
    for node in doctree.traverse(question_node):
        try:
            targetnode = node.parent[node.parent.index(node) - 1]
            if not isinstance(targetnode, nodes.target):
                raise IndexError
        except IndexError:
            targetnode = None
        qno += 1
        env.question_all_questions.append({
            'docname': env.docname,
            'source': node.source or env.doc2path(env.docname),
            'lineno': node.line,
            'question': node.deepcopy(),
            'questno': qno,
            'target': targetnode,
        })


class QuestionList(Directive):
    """
    A list of all question entries.
    """

    has_content = False
    required_arguments = 0
    optional_arguments = 0
    final_argument_whitespace = False
    option_spec = {}

    def run(self):
        # Simply insert an empty questionlist node which will be replaced later
        # when process_question_nodes is called
        return [questionlist('')]


def process_question_nodes(app, doctree, fromdocname):
###    if not app.config['question_include_questions']:
    if False:
        for node in doctree.traverse(question_node):
            node.parent.remove(node)

    # Replace all questionlist nodes with a list of the collected questions.
    # Augment each question with a backlink to the original location.
    env = app.builder.env

    if not hasattr(env, 'question_all_questions'):
        env.question_all_questions = []

    for node in doctree.traverse(questionlist):
###        if not app.config['question_include_questions']:
#        if False:
#            node.replace_self([])
#            continue

        content = []

        for question_info in env.question_all_questions:
            qno = question_info['questno']
            para = nodes.paragraph(classes=['question-source'])
            description = _('<<original entry>>')
            desc1 = description[:description.find('<<')]
            desc2 = description[description.find('>>')+2:]
            para += nodes.Text(desc1, desc1)

            # Create a reference
            newnode = nodes.reference('', '', internal=True)
###            innernode = nodes.emphasis(_('original entry'), _('original entry'))
            innernode = nodes.strong(_('Question %d'%qno), _('Question %d'%qno))
            try:
                newnode['refuri'] = app.builder.get_relative_uri(
                    fromdocname, question_info['docname'])
                newnode['refuri'] += '#' + question_info['target']['refid']
            except NoUri:
                # ignore if no URI can be determined, e.g. for LaTeX output
                pass
            newnode.append(innernode)
            para += newnode
            para += nodes.Text(desc2, desc2)

            # (Recursively) resolve references in the question content
            question_entry = question_info['question']
            env.resolve_references(question_entry, question_info['docname'],
                                   app.builder)

            # Insert into the questionlist
###            content.append(question_entry)
            content.append(para)

        node.replace_self(content)


def purge_questions(app, env, docname):
    if not hasattr(env, 'question_all_questions'):
        return
    env.question_all_questions = [question for question in env.question_all_questions
                          if question['docname'] != docname]


def visit_question_node(self, node):
    self.visit_admonition(node)

def depart_question_node(self, node):
    self.depart_admonition(node)

def setup(app):
    app.add_config_value('question_include_questions', False, False)

    app.add_node(questionlist)
    app.add_node(question_node,
                 html=(visit_question_node, depart_question_node),
                 latex=(visit_question_node, depart_question_node),
                 text=(visit_question_node, depart_question_node),
                 man=(visit_question_node, depart_question_node),
                 texinfo=(visit_question_node, depart_question_node))

    app.add_directive('question', Question)
    app.add_directive('questionlist', QuestionList)
    app.connect('doctree-read', process_questions)
    app.connect('doctree-resolved', process_question_nodes)
    app.connect('env-purge-doc', purge_questions)