import xml.etree.ElementTree as ET
from matplotlib.path import Path
import re

class SVG:
    def pathEnd(self, path):
        pathStart = (0, 0)
        result = (0, 0)
        for i, code in enumerate(path["code"]):
            if code == Path.MOVETO:
                pathStart = path["coords"][i]
                result = pathStart
            elif code == Path.CLOSEPOLY:
                result = pathStart
            else:
                result = path["coords"][i]
        return result

    def smoothPt(self, path):
        if (len(path["code"]) < 2):
            return self.pathEnd(path)
        if (path["code"][-2] != Path.CURVE4):
            return self.pathEnd(path)
        return path["coords"][-2]

    def finishCommand(self, path, command, coords):
        if command == "M":
            path["code"].append(Path.MOVETO)
            path["coords"].append(coords)
        elif command == "L":
            pe = self.pathEnd(path)
            path["code"].append(Path.LINETO)
            path["coords"].append(coords)
        elif command == "H":
            pe = self.pathEnd(path)
            path["code"].append(Path.LINETO)
            path["coords"].append((coords[0], pe[1]))
        elif command == "V":
            pe = self.pathEnd(path)
            path["code"].append(Path.LINETO)
            path["coords"].append((pe[0], coords[0]))
        elif command == "C":
            path["code"].append(Path.CURVE4)
            path["code"].append(Path.CURVE4)
            path["code"].append(Path.CURVE4)
            path["coords"].append(coords[0:2])
            path["coords"].append(coords[2:4])
            path["coords"].append(coords[4:6])
        elif command == "S":
            sp = self.smoothPt(path)
            pe = self.pathEnd(path)
            path["code"].append(Path.CURVE4)
            path["code"].append(Path.CURVE4)
            path["code"].append(Path.CURVE4)
            dsp = (pe[0] - sp[0], pe[1] - sp[1])
            p1 = (pe[0] + dsp[0], pe[1] + dsp[1])
            path["coords"].append(p1)
            path["coords"].append(coords[0:2])
            path["coords"].append(coords[2:4])
        elif command == "Z":
            path["code"].append(Path.CLOSEPOLY)
            path["coords"].append((0, 0))
        else:
            raise Exception("Unhandled command " + command)

    def draw(self, ax, coords, fade):
        for use in self.uses:
            commands = self.paths[use["id"]]["code"]
            rcoords = self.paths[use["id"]]["coords"]
            ref = use["coords"]
            acoords = []
            for rcoord in rcoords:
                acoords.append((rcoord[0] + ref[0] + coords[0], rcoord[1] + ref[1] + coords[1]))
            path = Path(acoords, commands)
            patch = patches.PathPatch(path, facecolor=(0, 0, 0, fade), lw=0)
            ax.add_patch(patch)

    def set_svg(self, svg):
        self.svg = svg
        # This parser is not for general purpose SVG files, but for those
        # generated by dvisvgm. This uses paths defined in the defs section
        # and instantiated in a use section. The path might be disconnected
        self.paths = {}
        self.uses = []
        # Parse the XML into an ElementTree
        root = ET.fromstringlist(self.svg)
        bbox = spaceparse.split(root.attrib["viewBox"])
        self.left = float(bbox[0])
        self.top = float(bbox[1])
        self.width = float(bbox[2])
        self.height = float(bbox[3])

        defs = root.find('{http://www.w3.org/2000/svg}defs')
        if defs is not None:
            # Get all the paths out of the xml, which are in svg/defs
            for svgpath in defs.findall("{http://www.w3.org/2000/svg}path"):
                id = svgpath.attrib["id"]
                d = re.findall('[A-Za-z ]|(?:[-+]?[0-9]+(?:\.[0-9]+)?)', svgpath.attrib["d"])
                d = [x for x in d if x != " "]
                path = {"code": [], "coords": []}
                coords = None
                command = None
                for part in d:
                    if re.match("[A-Za-z]", part):
                        if coords is not None:
                            self.finishCommand(path, command, coords)
                        command = part
                        coords = None
                    else:
                        if coords is None:
                            coords = (float(part),)
                        else:
                            coords = coords + (float(part),)
                if command is not None:
                    self.finishCommand(path, command, coords)
                self.paths[id] = path
        g = root.find('{http://www.w3.org/2000/svg}g')
        if g is not None:
            # Get all the paths out of the xml, which are in svg/defs
            for svguse in g.findall("{http://www.w3.org/2000/svg}use"):
                use = {"id": svguse.attrib["{http://www.w3.org/1999/xlink}href"][1:],
                       "coords": (float(svguse.attrib["x"]), float(svguse.attrib["y"]))}
                self.uses.append(use)

    def shift(self, delta_coords):
        dx = delta_coords[0]
        dy = delta_coords[1]
        self.left += dx
        self.top += dy
        for use in self.uses:
            use["coords"] = (use["coords"][0] + dx, use["coords"][1] + dy)

def test_SVG():
    """
    * Depends on Tex creating an SVG string
    * Depends on having a working PictureBox
    :return:
    """
    from tex import eqn2svg
    svg=eqn2svg(r"x=\frac{-b\pm\sqrt{b^2-4ac}}{2a")
    print(svg)

if __name__=="__main__":
    test_SVG()