# Source code for betterTurtle

    # -*- coding: utf-8 -*-

    """
    A new turtle which is faster.

    >>> t = Turtle()
    >>> t.init()
    >>> t.set_position((0, 0))
    >>> t.fractal.outline(6, 2, 5)

    .. autoclass:: Figures
       :members:
       :undoc-members:

    .. autoclass:: Turtle
       :members:
       :undoc-members:
    """

    import math
    import time

    from PIL import Image, ImageDraw

    class Figures:
        """A lot of function to create some well-know shapes

        :param master: turtle2 to use for draw
        :type master: Turtle

        :returns: Nothing
        :rtype: None"""

        def __init__(self, master):
            self.canvas = master

        def _outline_trace(self, number_of_iterations, length, number_of_sides):
            """Internal function to draw outline of a recursive shape

            :param number_of_iterations: Number of iteration used to draw
            :param length: Size of a single side
            :param number_of_sides: Number of sides of the initial shape
            :type number_of_iterations: int
            :type length: int
            :type number_of_sides: int

            :returns: Nothing
            :rtype: None"""

            if number_of_iterations == 0:
                self.canvas.forward(length)
            else:
                self._outline_trace(number_of_iterations - 1, length, number_of_sides)
                self.canvas.right(360. / number_of_sides)
                self._outline_trace(number_of_iterations - 1, length, number_of_sides)
                self.canvas.right(-360. / number_of_sides)
                self._outline_trace(number_of_iterations - 1, length, number_of_sides)
                self.canvas.right(-360. / number_of_sides)
                self._outline_trace(number_of_iterations - 1, length, number_of_sides)
                self.canvas.right(360. / number_of_sides)
                self._outline_trace(number_of_iterations - 1, length, number_of_sides)

        def regular_polygon(self, number_of_sides, length):
            """Draw a regular polygon

            :param number_of_sides: Number of sides of the polygon
            :param length: Length of a side
            :type number_of_sides: int
            :type length: int

            :returns: Nothing
            :rtype: None"""
            angle = 360. / number_of_sides
            for i in range(number_of_sides):
                self.canvas.forward(length)
                self.canvas.right(angle)

        def poly_repeat(self, length, number_of_side, density):
            """Draw a repetition of a regular polygon

            :param length: Length of a side
            :param number_of_side: Regular polygon's side number
            :param density: quantity of polygon
            :type length: int
            :type number_of_side: int
            :type density: int

            :returns: Nothing
            :rtype: None"""
            angle = 360. / density
            for i in range(density):
                self.regular_polygon(number_of_side, length)
                self.canvas.right(angle)

        def outline(self, number_of_iteration, length, number_of_sides):
            """Draw outline of a recursive shape

            :param number_of_iteration: Number of iteration used to draw
            :param length: Length of a single side
            :param number_of_sides: Number of sides of the initial shape
            :type number_of_iteration: int
            :type length: int
            :type number_of_sides: int

            :returns: Nothing
            :rtype: None"""
            for i in range(number_of_sides):
                self._outline_trace(number_of_iteration, length, number_of_sides)
                self.canvas.right(360. / number_of_sides)

        def tree(self, length, angles, factor=1.5, min_size=5):
            """Draw a tree recursively

            :param length: Length of the root of the tree
            :param angles: List of angles for branch
            :param factor: Reduce factor for next branch
            :param min_size: Minimal length of a branch
            :type length: int
            :type angles: list
            :type factor: float
            :type min_size: float

            :returns: Nothing
            :rtype: None"""
            if length < min_size:
                return ""
            else:
                self.canvas._state["color"] = (int(length), int(length), int(length))
                for angle in angles:
                    pos = self.canvas.get_position()
                    base_angle = self.canvas.get_angle()
                    self.canvas.right(angle)
                    self.canvas.forward(length)
                    self._tree(length / factor, angles, factor=factor, min_size=min_size)
                    self.canvas.set_position(pos)
                    self.canvas.set_angle(base_angle)

        def dragon(self, length, number_of_iteration, angle=1):
            """Draw the dragon curve

            :param angle: Start angle
            :param length: Length of a side
            :param number_of_iteration: Number of iteration for the curve
            :type length: int
            :type number_of_iteration: int

            :returns: Nothing
            :rtype: None"""
            if number_of_iteration == 0:
                self.canvas.forward(length)
            else:
                self.dragon(length, number_of_iteration - 1, 1)
                self.canvas.left(angle * 90)
                self.dragon(length, number_of_iteration - 1, -1)

        def power(self, length, power, base=1.5):
            k = base
            list_powers = []
            for i in range(power):
                k = 10 * (k * 0.15 - int(k * 0.15))
                n = int((k - int(k)) * 10)
                list_powers.append(n)
            for i in list_powers:
                angle = 36 * i
                self.canvas.right(angle)
                self.canvas.forward(length)

        def turning_tree(self, length, angles):
            while True:
                self.tree(length, angles)
                time.sleep(0.1)
                i = 0
                for _ in angles:
                    angles[i] += 1
                    i += 1

        def _koch_curve(self, length):
            self.canvas.forward(length)
            self.canvas.left(60)
            self.canvas.forward(length)
            self.canvas.right(120)
            self.canvas.forward(length)
            self.canvas.left(60)
            self.canvas.forward(length)

        def koch_curve(self, length, number_of_iteration):
            if number_of_iteration > 0:
                self.koch_curve(length / 3., number_of_iteration - 1)
                self.canvas.left(60)
                self.koch_curve(length / 3., number_of_iteration - 1)
                self.canvas.right(120)
                self.koch_curve(length / 3., number_of_iteration - 1)
                self.canvas.left(60)
                self.koch_curve(length / 3., number_of_iteration - 1)
            else:
                self._koch_curve(length)

    class Turtle:

        @staticmethod
        def _calc_center(size):
            return size[0] / 2, size[1] / 2

        def _forward(self, distance):
            AB = (distance * math.cos(math.radians(self._state.get("angle")))) + self._state.get("coordinate_x")
            AC = (distance * math.sin(math.radians(self._state.get("angle")))) + self._state.get("coordinate_y")
            self._forward_image(distance)
            self._set_coordinates((AB, AC))

        def _forward_image(self, distance):
            AB = (distance * math.cos(math.radians(self._state.get("angle")))) + self._state.get("coordinate_x")
            AC = (distance * math.sin(math.radians(self._state.get("angle")))) + self._state.get("coordinate_y")
            self.draw.line((self.get_position('x') * self.resolution, self.get_position('y') * self.resolution,
                            AB * self.resolution, AC * self.resolution), fill=self._state.get("colour"))

        def _turn(self, angle):
            self._set_angle(self._state.get("angle") + angle)

        def _set_angle(self, angle):
            self._state["angle"] = angle
            while self._state.get("angle") >= 360:
                self._state["angle"] = self._state.get("angle") - 360

        def _clear(self):
            pass

        def _clear_img(self):
            self.image = Image.new(
                '1', (self._config.get("size")), (255, 255, 255))
            self.draw = ImageDraw.Draw(self.image)

        def __init__(self, titre="Turtle", size=(
                400, 400), resolution=10):
            self._config = {"titre": titre,
                            "size": size,
                            "size_IMG": (size[0] * resolution, size[1] * resolution),
                            "center": self._calc_center(size),
                            }
            self._state = {"angle": 0,
                           "coordinate_x": self._config.get("center")[0],
                           "coordinate_y": self._config.get("center")[1],
                           "colour": (0, 0, 0),
                           }
            self.fractal = Figures(self)
            self.image = Image.new(
                'RGB',
                (self._config.get("size_IMG")),
                (255,
                 255,
                 255))
            self.draw = ImageDraw.Draw(self.image)
            self.resolution = resolution

        def forward(self, distance):
            self._forward(distance)

        def backward(self, distance):
            self._forward(-distance)
            if self.sauvegarde:
                self._forward_image(-distance)

        def right(self, angle):
            self._turn(angle)

        def left(self, angle):
            self._turn(-angle)

        def set_pixel(self, coordinate, colour):
            self.draw.point(coordinate, colour if colour else self._state["colour"])

        def goto(self, coordinates):
            self._set_coordinates(coordinates)
            self.draw.line(coordinates)

        def _set_coordinates(self, coordinates):
            self._state["coordinate_x"] = coordinates[0]
            self._state["coordinate_y"] = coordinates[1]

        def clear(self):
            self._clear()

        def set_position(self, coordinates):
            self._set_coordinates(coordinates)

        def get_position(self, type_coord=''):
            if type_coord == 'x':
                return self._state.get("coordinate_x")
            elif type_coord == "y":
                return self._state.get("coordinate_y")
            return self._state.get("coordinate_x"), self._state.get("coordinate_y")

        def set_angle(self, angle):
            self._set_angle(angle)

        def get_angle(self):
            return self._state.get("angle")

        def get_state(self):
            text = ""
            for i in self._state.items():
                text = text + "\n" + str(i[0]) + ":" + str(i[1])
            return text

        def save(self, path, type_img=None):
            self.image.save(path, type_img)

    if __name__ == "__main__":
        t = Turtle(size=(10000, 10000), resolution=1)
        t.set_position((0, 0))
        t.fractal.outline(8, 40, 4)
        t.save("D:\\Users\\louis chauvet\\Documents\\GitHub\\fractale\\test.bmp")