# -----------------------------------------------------------------------
# lindenmayer.py
#
# utf-8
# Python 2.7 - 3.10
# Copyright 2022 Martin Ambauen, www.sqrt.ch
# -----------------------------------------------------------------------

import math

# -----------------------------------------------------------------------
# # SVG Print Anweisungen
# -----------------------------------------------------------------------
def printheader():
    print('<?xml version="1.0" encoding="UTF-8"?>\n<svg shape-rendering="geometricPrecision" style="background-color:black" viewBox="0 0 1000 1000" xmlns="http://www.w3.org/2000/svg">\n\t<rect width="1000" height="1000"/>')

def printpath(x2, y2):
    print(f'\t\t{x2:.5f} {y2:.5f}')

def printpathbegin(x1, y1):
    print(f'\t\t<path d="M {x1:.5f} {y1:.5f}')

def printpos(x1, y1):
    print(f'\t\tM {x1:.5f} {y1:.5f}')

def printpathend(p=1):
    print(f'\t\t" fill="none" stroke="#ff0" stroke-width="{p:.4f}" stroke-linejoin="round" stroke-linecap="round"/>')

def printendsvg():
    print('\t<desc>www.sqrt.ch</desc>\n</svg>\n')

# -----------------------------------------------------------------------
# # SVG Turtle
# -----------------------------------------------------------------------

class Turtle:
    # Konstruktion von self bei (x, y) mit positivem Winkel angle bzgl x-Achse
    def __init__(self, x, y, angle, stroke):
        self._x = x  # x-Koordinate der Turtle
        self._y = y  # y-Koordinate der Turtle
        self._angle = angle  # Orientierung der Turtle bzgl. x-Achse
        self._stroke = stroke  # Konturstärke SVG

    def __get__(self, x, y):
        return self._x, self._y

    # Rotation von self um den positiven Winkel angle.
    def turnLeft(self, delta):
        self._angle += delta

    # Bewege dich um step vorwärts und drucke neue Position in den Pfad.
    def goForward(self, step):
        self._x += step * math.cos(math.radians(self._angle))
        self._y += step * math.sin(math.radians(self._angle))
        printpath(self._x, self._y)

    # Bewege dich um step vorwärts
    def moveTo(self, step):
        self._x += step * math.cos(math.radians(self._angle))
        self._y += step * math.sin(math.radians(self._angle))
        printpos(self._x, self._y)

    # Speichere die aktuelle Position und den Winkel.
    def savePos(self):
        positions.append(self._x)
        positions.append(self._y)
        angles.append(self._angle)

    # Schreibe die aktuelle Position, setze den Winkel zurück auf diese,
    # lösche diese Einträge im Speicher (geschachtelte Klammerung)
    def getPos(self):
        self._x = positions[-2]
        self._y = positions[-1]
        positions.pop(-1)
        positions.pop(-1)
        self._angle = angles[-1]
        angles.pop(-1)
        printpos(self._x, self._y)

# -----------------------------------------------------------------------
# # L-System Erzeugung
# -----------------------------------------------------------------------

# Kochstern, w = 60
rules = ["AB+AB+AB+AB+AB+AB+", "", "-ACAD++AEAC-", "AC-ACAD++AEAC-AC", "-AC-ACAD++AEACAD++AEAC-AC-", "--ACAD++AEACAD++AEAC-AC-"]

def createString(s):
    x = ""
    for char in s:
        if char == "A":
            x += rules[1]
        elif char == "B":
            x += rules[2]
        elif char == "C":
            x += rules[3]
        elif char == "D":
            x += rules[4]
        elif char == "E":
            x += rules[5]
        else: x += char
    return x

def stringToPrint(order):
    if order == 0:
        print(rules[0])
        return
    toRender = createString(rules[0])
    while order - 1 > 0:
        toRender = createString(toRender)
        order -= 1
    print(toRender)

def drawString(a=""):
    for k in a:
        keys[k](size)

def lindenmayer(order):
    if order == 0:
        for k in rules[0]:
            keys[k](size)
        return
    toRender = createString(rules[0])
    while order - 1 > 0:
        toRender = createString(toRender)
        order -= 1
    for k in toRender:
        keys[k](size)

keys = {
    "A": lambda size: myTurtle.goForward(size), 
    "B": lambda size: myTurtle.goForward(size),
    "C": lambda size: myTurtle.goForward(size),
    "D": lambda size: myTurtle.goForward(size),
    "E": lambda size: myTurtle.goForward(size),
    "-": lambda size: myTurtle.turnLeft(w),
    "+": lambda size: myTurtle.turnLeft(-w),
    "F": lambda size: myTurtle.goForward(size),
    "f": lambda size: myTurtle.moveTo(size),
    "R": lambda size: myTurtle.goForward(-size),
    "r": lambda size: myTurtle.moveTo(-size),
    "[": lambda size: myTurtle.savePos(),
    "]": lambda size: myTurtle.getPos(),
    '|': lambda size: myTurtle.turnLeft(180)
}

positions = []
angles = []
x = 420
y = 650
w = 60
w0 = 0
p = 2

order = 4      # Anzahl Ersetzungsschritte
size  = 2      # Grundlänge

# -----------------------------------------------------------------------
# Erstelle eine Vektordatei des L-Systems
printheader()
printpathbegin(x, y)
myTurtle = Turtle(x, y, w0, p)
lindenmayer(order)
# Alternative zu lindenmayer(order)
# drawString("FA++FA++FA++FA++FA++") # Erstelle SVG aus konkretem String
printpathend()
printendsvg()

# -----------------------------------------------------------------------
# Erstelle den String des L-Systems
# stringToPrint(order)

# -----------------------------------------------------------------------

# Eingabe im Terminal in der Form:

# python lindenmayer.py > abc.svg
