r"""
Pandoc class
"""
import json
import subprocess


class Pandoc:
    """
    Execute external pandoc command as a subprocess to parse a source md file to generate PandocAST json object.
    """

    def __init__(self):
        """Constructor"""
        self._version = None
        self._version_str = None

    def _run(self, commandlines, stdin=None):
        """run multiple subcommands
        run multiple subcommands while connecting it with pipes and return the output.
        @param comandlines([Str])
            Commandline strings including options to run as subprocesses.
        @param stdin
            stdin port passed to the first subcommand.
        @return output : Str
            stdout from the last subcommand.
        """
        output = None
        args = commandlines[0].split()

        with subprocess.Popen(
            args, stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.PIPE
        ) as ps:

            if len(commandlines) > 1:
                output = self._run(commandlines[1:], stdin=ps.stdout)
                ps.wait()

            else:
                ps.wait()
                output = b"".join(ps.stdout.readlines())

            if ps.returncode != 0:
                # subprocess.check_output():
                # https://docs.python.org/3/library/subprocess.html#subprocess.check_output
                # > If the return code was non-zero it raises a CalledProcessError.
                raise subprocess.CalledProcessError(
                    ps.returncode,
                    args,
                    b"".join(ps.stdout.readlines()),
                    b"".join(ps.stderr.readlines()),
                )

        return output

    def get_json(self, filepath: str, formatType=None, html=None):
        """
        returns pandoc ast json object.
        @param filepath : str
            File path to the source document to read.
        @param fromType : str / None
            Input filetype to be passed to pandoc with opt '-f'.
        @param html : bool / None
            Flag to specify whether or not to interpret html tags.
        @return output : { PandocAst Object }
            Pandoc AST json object generated by pandoc from source document.
        """
        _SOURCEPOS_ = ["gfm", "commonmark", "commonmark_x"]

        filename = filepath.split("/")[-1]
        file_ext = filename.split(".")
        file_ext = file_ext[-1].lower() if len(file_ext) > 1 else ""

        if formatType is None and html is None:
            if file_ext == "md":
                formatType = "gfm+sourcepos"
                html = True
            else:
                formatType = None
                html = False
        else:
            if html is None:
                html = False

            if formatType in _SOURCEPOS_:
                formatType += "+sourcepos"

        commands = []
        cmd = "pandoc " + filepath

        if formatType is not None:
            cmd += " -f " + formatType

        if not html:
            cmd += " -t json"
            commands.append(cmd)
        else:
            cmd += " -t html"
            commands.append(cmd)
            commands.append("pandoc -f html -t json")

        json_object = json.loads(self._run(commands))

        return json_object

    def get_version(self):
        """
        returns versions of pandoc and pandoc-types.
        @return versions : {
                'pandoc': [int or str],
                'pandoc-types': [int or str],
            }
            version information obtained from 'pandoc --version'
        """
        if self._version is not None:
            return self._version

        version_str = self._run(["pandoc --version"]).decode()
        lines = version_str.split("\n")

        version = {}
        for line in lines:
            words = line.replace(",", " ").split()

            for i, word in enumerate(words):
                if ((word == "pandoc") or (word == "pandoc-types")) and (
                    i < len(words) - 1
                ):
                    vers = words[i + 1].split(".")
                    if not vers[0].isdecimal():
                        break

                    version[word] = []
                    for ver in vers:
                        version[word].append(int(ver) if ver.isdecimal() else ver)

        if ("pandoc" in version) and ("pandoc-types" in version):
            self._version = version
            self._version_str = version_str

        return self._version
