"""Markdown filters

This file contains a collection of utility filters for dealing with 
markdown within Jinja templates.
"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.

from __future__ import print_function

import os
import subprocess
from io import TextIOWrapper, BytesIO

try:
    from .markdown_mistune import markdown2html_mistune
except ImportError as e:
    # store in variable for Python 3
    _mistune_import_error = e
    def markdown2html_mistune(source):
        """mistune is unavailable, raise ImportError"""
        raise ImportError("markdown2html requires mistune: %s" % _mistune_import_error)

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


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, markup='markdown', extra_args=None):
    """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.
    markup : string
      Markup used by pandoc's reader
      default : pandoc extended markdown
      (see http://johnmacfarlane.net/pandoc/README.html#pandocs-markdown)

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


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


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, extra_args=None):
    """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', extra_args=extra_args)

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')
