"""Markdown filters
This file contains a collection of utility filters for dealing with 
markdown within Jinja templates.
"""
#-----------------------------------------------------------------------------
# Copyright (c) 2013, the IPython Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file COPYING.txt, distributed with this software.
#-----------------------------------------------------------------------------

#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
from __future__ import print_function

# Stdlib imports
import os
import subprocess
from io import TextIOWrapper, BytesIO
import re

import mistune
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import HtmlFormatter

# IPython imports
from IPython.nbconvert.utils.pandoc import pandoc
from IPython.nbconvert.utils.exceptions import ConversionException
from IPython.utils.process import get_output_error_code
from IPython.utils.py3compat import cast_bytes
from IPython.utils.version import check_version

#-----------------------------------------------------------------------------
# Functions
#-----------------------------------------------------------------------------
marked = os.path.join(os.path.dirname(__file__), "marked.js")
_node = None

__all__ = [
    'markdown2html',
    'markdown2html_pandoc',
    'markdown2html_marked',
    'markdown2html_mistune',
    'markdown2latex',
    'markdown2rst',
]

class NodeJSMissing(ConversionException):
    """Exception raised when node.js is missing."""
    pass

def markdown2latex(source):
    """Convert a markdown string to LaTeX via pandoc.

    This function will raise an error if pandoc is not installed.
    Any error messages generated by pandoc are printed to stderr.

    Parameters
    ----------
    source : string
      Input string, assumed to be valid markdown.

    Returns
    -------
    out : string
      Output as returned by pandoc.
    """
    return pandoc(source, 'markdown', 'latex')

class MathBlockGrammar(mistune.BlockGrammar):
    block_math = re.compile("^\$\$(.*?)\$\$")
    block_math2 = re.compile(r"^\\begin(.*?)\\end")

class MathBlockLexer(mistune.BlockLexer):
    default_features = ['block_math', 'block_math2'] + mistune.BlockLexer.default_features

    def __init__(self, rules=None, **kwargs):
        if rules is None:
            rules = MathBlockGrammar()
        super(MathBlockLexer, self).__init__(rules, **kwargs)

    def parse_block_math(self, m):
        """Parse a $$math$$ block"""
        self.tokens.append({
            'type': 'block_math',
            'text': m.group(1)
        })

    parse_block_math2 = parse_block_math

class MathInlineGrammar(mistune.InlineGrammar):
    math = re.compile("^\$(.+?)\$")

class MathInlineLexer(mistune.InlineLexer):
    default_features = ['math'] + mistune.InlineLexer.default_features

    def __init__(self, renderer, rules=None, **kwargs):
        if rules is None:
            rules = MathInlineGrammar()
        super(MathInlineLexer, self).__init__(renderer, rules, **kwargs)

    def output_math(self, m):
        self.renderer.inline_math(m.group(1))

class MarkdownWithMath(mistune.Markdown):
    def __init__(self, renderer, **kwargs):
        if 'inline' not in kwargs:
            kwargs['inline'] = MathInlineLexer(renderer, **kwargs)
        if 'block' not in kwargs:
            kwargs['block'] = MathBlockLexer(**kwargs)
        super(MarkdownWithMath, self).__init__(renderer, **kwargs)

    def parse_block_math(self):
        return self.renderer.block_math(self.token['text'])

class IPythonRenderer(mistune.Renderer):
    def block_code(self, code, lang):
        if not lang:
            return '\n<pre><code>%s</code></pre>\n' % \
                mistune.escape(code)
        lexer = get_lexer_by_name(lang, stripall=True)
        formatter = HtmlFormatter()
        return highlight(code, lexer, formatter)

    # Pass math through unaltered - mathjax does the rendering in the browser
    def block_math(self, text):
        return '$$%s$$' % text

    def inline_math(self, text):
        return '$%s$' % text

def markdown2html_mistune(source):
    """Convert a markdown string to HTML using mistune"""
    return MarkdownWithMath(renderer=IPythonRenderer()).render(source)

def markdown2html_pandoc(source):
    """Convert a markdown string to HTML via pandoc"""
    return pandoc(source, 'markdown', 'html', extra_args=['--mathjax'])

def _find_nodejs():
    global _node
    if _node is None:
        # prefer md2html via marked if node.js >= 0.9.12 is available
        # node is called nodejs on debian, so try that first
        _node = 'nodejs'
        if not _verify_node(_node):
            _node = 'node'
    return _node

def markdown2html_marked(source, encoding='utf-8'):
    """Convert a markdown string to HTML via marked"""
    command = [_find_nodejs(), marked]
    try:
        p = subprocess.Popen(command,
                             stdin=subprocess.PIPE, stdout=subprocess.PIPE
        )
    except OSError as e:
        raise NodeJSMissing(
            "The command '%s' returned an error: %s.\n" % (" ".join(command), e) +
            "Please check that Node.js is installed."
        )
    out, _ = p.communicate(cast_bytes(source, encoding))
    out = TextIOWrapper(BytesIO(out), encoding, 'replace').read()
    return out.rstrip('\n')

# The mistune renderer is the default, because it's simple to depend on it
markdown2html = markdown2html_mistune

def markdown2rst(source):
    """Convert a markdown string to ReST via pandoc.

    This function will raise an error if pandoc is not installed.
    Any error messages generated by pandoc are printed to stderr.

    Parameters
    ----------
    source : string
      Input string, assumed to be valid markdown.

    Returns
    -------
    out : string
      Output as returned by pandoc.
    """
    return pandoc(source, 'markdown', 'rst')

def _verify_node(cmd):
    """Verify that the node command exists and is at least the minimum supported
    version of node.

    Parameters
    ----------
    cmd : string
        Node command to verify (i.e 'node')."""
    try:
        out, err, return_code = get_output_error_code([cmd, '--version'])
    except OSError:
        # Command not found
        return False
    if return_code:
        # Command error
        return False
    return check_version(out.lstrip('v'), '0.9.12')

@@ -910,16 +910,56 @@
 ormatter
+%0Afrom pygments.util import ClassNotFound
 %0A%0A# IPyt
@@ -3866,16 +3866,218 @@
  lang):%0A
+        if lang:%0A            try:%0A                lexer = get_lexer_by_name(lang, stripall=True)%0A            except ClassNotFound:%0A                code = lang + '%5Cn' + code%0A                lang = None%0A%0A
         
@@ -4085,24 +4085,24 @@
 f not lang:%0A
-
             
@@ -4186,62 +4186,8 @@
 de)%0A
-        lexer = get_lexer_by_name(lang, stripall=True)
 %0A   

Fix code block rendering when lexer not known