"""A small turtle that draws on paper: the same commands as Python's turtle,
but instead of opening a window it saves an SVG for a pen plotter.

Every stroke (pen down to pen up) becomes one <path>, and the drawing is scaled
to fit the page, with one SVG unit per millimeter. Needs only Python itself.

Usage:
    from plotter_turtle import Turtle

    t = Turtle()
    t.forward(100)
    t.left(90)
    t.forward(100)
    t.save("drawing.svg")
"""
import math


class Turtle:
    def __init__(self):
        self.x = 0.0
        self.y = 0.0
        self.angle = 0.0  # degrees, 0 = east, counterclockwise, as in Python's turtle
        self.is_down = True
        self.strokes = []
        self.stroke = [(0.0, 0.0)]

    def forward(self, distance):
        radians = math.radians(self.angle)
        self.goto(self.x + distance * math.cos(radians),
                  self.y + distance * math.sin(radians))

    def back(self, distance):
        self.forward(-distance)

    def left(self, degrees):
        self.angle = (self.angle + degrees) % 360

    def right(self, degrees):
        self.left(-degrees)

    def goto(self, x, y):
        self.x, self.y = x, y
        if self.is_down:
            self.stroke.append((x, y))

    def setheading(self, degrees):
        self.angle = degrees % 360

    def penup(self):
        self.is_down = False
        self._end_stroke()

    def pendown(self):
        if not self.is_down:
            self.is_down = True
            self.stroke = [(self.x, self.y)]

    def _end_stroke(self):
        if len(self.stroke) > 1:
            self.strokes.append(self.stroke)
        self.stroke = []

    # The short names, and two commands that mean nothing on paper.
    fd = forward
    bk = back
    lt = left
    rt = right
    pu = up = penup
    pd = down = pendown

    def speed(self, speed):
        pass

    def hideturtle(self):
        pass

    def save(self, filename, width=297, height=210, margin=15):
        """Scale the drawing to fit the page and save it. Sizes in millimeters."""
        if self.is_down:
            self._end_stroke()
            self.stroke = [(self.x, self.y)]

        points = [point for stroke in self.strokes for point in stroke]
        left = min(x for x, y in points)
        right = max(x for x, y in points)
        bottom = min(y for x, y in points)
        top = max(y for x, y in points)
        scale = min((width - 2 * margin) / (right - left or 1),
                    (height - 2 * margin) / (top - bottom or 1))
        offset_x = (width - scale * (right - left)) / 2 - scale * left
        offset_y = (height - scale * (top - bottom)) / 2 + scale * top

        paths = []
        drawn = 0.0
        for stroke in self.strokes:
            page = [(offset_x + scale * x, offset_y - scale * y) for x, y in stroke]
            drawn += sum(math.dist(a, b) for a, b in zip(page, page[1:]))
            d = "M" + " L".join(f"{x:.2f} {y:.2f}" for x, y in page)
            paths.append(f'  <path d="{d}"/>')

        with open(filename, "w", encoding="utf-8") as file:
            file.write(
                f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}mm" '
                f'height="{height}mm" viewBox="0 0 {width} {height}" '
                'fill="none" stroke="black" stroke-width="0.4" '
                'stroke-linecap="round" stroke-linejoin="round">\n'
                + "\n".join(paths) + "\n</svg>\n"
            )
        print(f"{filename}: {len(paths)} stroke(s), {drawn / 1000:.1f} m of line, "
              f"1 step = {scale:.2f} mm")
