# Copyright (C) 2017  Tyler Conzett
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
__author__ = "tconzett"

from asyncore import write
from docutils import nodes
from docutils.parsers.rst import directives
from runestone.common.xmlcommon import substitute_departure
from runestone.server.componentdb import addQuestionToDB, addHTMLToDB
from runestone.common.runestonedirective import (
    RunestoneIdDirective,
    RunestoneIdNode,
)
from runestone.common.xmlcommon import (
    substitute_departure,
    write_substitute,
)


def setup(app):
    app.add_directive("showeval", ShowEval)

    app.add_config_value("showeval_div_class", "runestone explainer", "html")
    app.add_node(
        ShowEvalNode,
        html=(visit_showeval_html, depart_showeval_html),
        xml=(visit_showeval_xml, substitute_departure),
    )


# Create visitors, so we can generate HTML after the doctree is resolve (where the question label is determined).
class ShowEvalNode(nodes.General, nodes.Element, RunestoneIdNode):
    pass


def visit_showeval_html(self, node):
    html = CODE % node["runestone_options"]
    self.body.append(html)
    addHTMLToDB(
        node["runestone_options"]["divid"],
        node["runestone_options"]["basecourse"],
        html,
    )


def visit_showeval_xml(self, node):
    html = CODE % node["runestone_options"]
    write_substitute(self, node, html)


def depart_showeval_html(self, node):
    pass


CODE = """
<div data-component="showeval" data-question_label="%(question_label)s" class="%(divclass)s" id="%(divid)s" data-tracemode="%(trace_mode)s" %(optional)s>
    <button class="btn btn-success" id="%(divid)s_nextStep">Next Step</button>
    <button class="btn btn-default" id ="%(divid)s_reset">Reset</button>
    <div class="evalCont" style="background-color: #FDFDFD;">%(preReqLines)s</div>
    <div class="evalCont"></div>
    <script>
    if (typeof window.raw_steps === "undefined") {
    window.raw_steps = {};
    }
    raw_steps["%(divid)s"] = %(steps)s;
    </script>
</div>
"""


class ShowEval(RunestoneIdDirective):
    """
    .. showeval:: unique_id_goes_here
       :trace_mode: boolean  <- Required option that enables 'Trace Mode'

       some code
       more code
       ~~~~
       more {{code}}{{what code becomes in step 1}}
       ##Optional comment for step 2
       more {{what code becomes in step 1}}{{what code becomes in step2}}
       as many steps as you want {{the first double braces}}{{animate into the second}} wherever.


    config values (conf.py):

    - showeval_div_class - custom CSS class of the component's outermost div
    """

    required_arguments = 1
    optional_arguments = 0
    final_argument_whitespace = True
    has_content = True
    option_spec = RunestoneIdDirective.option_spec.copy()
    option_spec.update({"trace_mode": directives.unchanged_required})

    def run(self):
        """
            All prerequisite information that should be displayed above the directive,
            such as variable declaration, are separated from the step strings by "-----".

            The step animations follow the "-----" and are written one per line. Use
            "{{" and "}}" braces to surround the part of the line that should be replaced,
            followed by the replacement text also in "{{" and "}}". If you would like to add
            a comment that will appear in a div beside the animation, denote that at the end
            of the step where you would like it to appear with "##".

        Example:

        .. showeval:: showEval_0
           :trace_mode: false

           eggs = ['dogs', 'cats', 'moose']
           ~~~~

           ''.join({{eggs}}{{['dogs', 'cats', 'moose']}}).upper().join(eggs)
           ##I want to put a comment here!
           {{''.join(['dogs', 'cats', 'moose'])}}{{'dogscatsmoose'}}.upper().join(eggs)
           {{'dogscatsmoose'.upper()}}{{'DOGSCATSMOOSE'}}.join(eggs)
           'DOGSCATSMOOSE'.join({{eggs}}{{['dogs', 'cats', 'moose']}})
           {{'DOGSCATSMOOSE'.join(['dogs', 'cats', 'moose'])}}{{'dogsDOGSCATSMOOSEcatsDOGSCATSMOOSEmoose'}}

        """

        # Raise an error if the directive does not have contents.
        super(ShowEval, self).run()
        addQuestionToDB(self)

        self.options["trace_mode"] = self.options["trace_mode"].lower()
        self.options["preReqLines"] = ""
        self.options["steps"] = []

        env = self.state.document.settings.env
        self.options["divclass"] = env.config.showeval_div_class

        is_dynamic = env.config.html_context.get("dynamic_pages", False)
        is_dynamic = True if is_dynamic == "True" else False
        step = False
        for line in self.content:
            if step == True:
                if line != "":
                    if is_dynamic:
                        esc_line = str(line).replace("{", r"\{")
                    else:
                        esc_line = str(line)
                    self.options["steps"].append(esc_line)
            elif "~~~~" in line:
                step = True
            else:
                self.options["preReqLines"] += line + "<br />\n"
        se_node = ShowEvalNode()
        se_node["runestone_options"] = self.options
        se_node["source"], se_node["line"] = self.state_machine.get_source_and_line(
            self.lineno
        )
        return [se_node]
