import contextlib
import logging
import os
import subprocess
import tempfile


@contextlib.contextmanager
def svg_file_input(inkscape, svg: str, binary=False):
    with tempfile.NamedTemporaryFile("wb" if binary else "w", suffix=".svg") as f:
        f.write(svg)
        f.flush()
        inkscape.run_command(f"file-open:{f.name}")
        yield
        inkscape.run_command("file-close")


class InkscapeShell:
    def __init__(self, inkscape_bin: str):

        self.process = subprocess.Popen(
            [inkscape_bin, "--shell"],
            stdout=subprocess.PIPE,
            stdin=subprocess.PIPE,
            stderr=subprocess.PIPE,
            universal_newlines=True,
        )
        self.wait_for_prompt()

        self.run_command("file-close")

    def close(self):
        self.process.stdout.close()

    def wait_for_prompt(self):
        chars = []
        stdout = self.process.stdout
        while True:
            character = stdout.read(1)
            if (
                character == " "
                and chars
                and chars[-1] == ">"
                and (len(chars) == 1 or chars[-2] == "\n")
            ):
                line = "".join(chars[:-2])
                return line
            else:
                chars.append(character)

    def convert_to_pdf(self, source, target: str, type: str):
        with svg_file_input(self, source, binary=True):
            self.run_command("export-area-page")
            # Inkscape 1.1
            self.run_command("export-png-color-mode:RGBA_8")
            self.run_command("export-background-opacity:0")
            self.run_command(f"export-type:{type}")
            self.run_command(f"export-filename:{target}")
            self.run_command("export-do")

    def get_width(self, svg: str, id: str):
        return self.run_query(svg, "query-width", id)

    def get_height(self, svg: str, id: str):
        return self.run_query(svg, "query-height", id)

    def get_x(self, svg: str, id: str):
        return self.run_query(svg, "query-x", id)

    def run_query(self, svg: str, query: str, id: str):
        with svg_file_input(self, svg):
            self.run_command(f"select:{id}")
            value = self.run_command(query)
            try:
                return float(value)
            except ValueError:
                raise Exception(
                    f"Inkscape query executed ({query}) and should return "
                    f"float but returned {repr(value)}"
                )

    def run_command(self, command: str):
        logging.debug(f"Sending {command} to Inkscape")
        self.process.stdin.write(f"{command}\n")
        self.process.stdin.flush()
        output = self.wait_for_prompt().strip()
        if output:
            output = output.splitlines(keepends=False)[-1]
        return output

    def get_version(self):
        return self.run_command("inkscape-version")


def export_by_inkscape(inkscape: InkscapeShell, source: str, target: str, type: str):
    inkscape.convert_to_pdf(source, target, type)
    if not os.path.isfile(target):
        raise Exception(
            "Inkscape should produced file '{}' but it is not found".format(target)
        )
