#!/bin/python
"""
A module for generating tree sorting layout
based on pyFPDF.
GPL3 License, Mara Karayanni December 2022
"""
import glob
import json
import random
import re
import subprocess


class Tree:
    def __init__(self, zine):
        self.zine = zine

    def design_tree(self, title, size, margin, steps):
        """ Simple layout of branching with black color"""
        initial_steps = steps
        for letter in title:

            if self.zine.y + size > self.zine.page_break_trigger:  # self.zine.h:
                print(self.zine.h)
                print(self.zine.get_y())
                self.zine.add_page()
                center_x = self.zine.w/2 + margin
                self.zine.set_xy(center_x, margin)
                steps = initial_steps

            point_a = self.zine.get_x()
            point_b = self.zine.get_y()
            print(point_a, point_b)
            self.zine.cell(size, size, letter)  # letter here is sorted by the tree
            node_right = self.zine.get_x() + steps / 2
            node_left = point_a - steps / 2
            point_bb = point_b + steps
            self.zine.line(point_a, point_b, node_left, point_bb)
            self.zine.line(point_a, point_b, node_right, point_bb)
            self.zine.set_xy(node_left, point_bb)
            steps *= 2

    def keys_sorting(self, data):
        """ Make a list of sorted keys using Python's sorted function"""
        global res
        if type(data) is dict:
            for key in sorted(data.keys()):
                res.append(key)
        else:
            for key in sorted(data):
                res.append()
        return res

    def tree_sorting(self, data):
        """ Make a list of keys, which are first sorted
            by the insert_node function
        """
        global node
        if type(data) is dict:
            for key in data.keys():
                node = self.insert_node(node, key)
        else:
            for key in data:
                node = self.insert_node(node, key)
        return node

    def insert_node(self, node, key):
        """ Sorting key elements by recursive call of the function
            and the use of the class Node, which has left and right keys
        """

        if node is None:
            node = Node(key)
            return node
        if (key < node.key):

            # recurse
            node.left = self.insert_node(node.left, key)

        elif (key > node.key):
            # recurse
            node.right = self.insert_node(node.right, key)

        return node

    def print_nodes(self, node):
        """ Print left and right keys of an instance of the class Node,
            mostly for debugging purposes
        """
        global res
        if (node is not None):
            self.print_nodes(node.left)
            print("LEFT SORTED KEY IS", node.key, end="\n")
            res.append(node.key)
            self.print_nodes(node.right)
            print("RIGHT SORTED KEY IS", node.key, end="\n")
        return res

    def draw_year(self, data, root, x, y,
                  page_height, width, margin, steps=30):
        """ Tree method draw_year layout:
            Simple year stamps in vertical position and progressively
            horizontally to the left side
        """
        letter_size = 8
        right_margin = margin
        parent_x = x
        parent_y = y

        if root is not None:
            sorted_root = self.keys_sorting(data)
            print("SORTED ROOT", sorted_root)
            for key in sorted_root:

                if parent_y >= page_height-margin*2:
                    self.zine.add_page()
                    self.zine.set_xy(width/2, margin)
                    print("NEW PAGE", "Y", self.zine.get_y())
                if parent_x >= width or parent_x <= margin:
                    self.zine.set_xy(width/2, parent_y)

                parent_x = self.zine.get_x()
                parent_y = self.zine.get_y()

                # draw the year
                self.zine.cell(letter_size, letter_size, str(key))  # draw the root
                print("YEAR", key, "YEAR X Y", parent_x, parent_y)

                parent_x -= steps/5
                parent_y += steps
                self.zine.set_xy(parent_x, parent_y)

    def draw_year_name(self, data, root, page_height, margin, steps=30):
        """ Tree method draw_year_name layout:
            Simple year and name/letters stamps in vertical position
            and progressively horizontally to the left side
        """
        letter_size = 8
        width = self.zine.w
        parent_x = self.zine.get_x()
        parent_y = self.zine.get_y()

        if root is not None:
            sorted_root = self.keys_sorting(data)
            print("SORTED ROOT", sorted_root)
            for key in sorted_root:

                if parent_y >= page_height-margin*2:
                    self.zine.add_page()
                    self.zine.set_xy(width/2, margin)
                    print("NEW PAGE", "Y", self.zine.get_y())
                if parent_x >= width or parent_x <= margin:
                    self.zine.set_xy(width/2, parent_y)

                parent_x = self.zine.get_x()
                parent_y = self.zine.get_y()

                # draw the year
                self.zine.cell(letter_size, letter_size, str(key))  # draw the root
                print("YEAR", key, "YEAR X Y", parent_x, parent_y)

                # draw the tree names
                val = data[key]["scientificName"]
                child_x = parent_x - steps
                child_y = parent_y + letter_size
                if child_x <= margin:
                    print("CHILD OFFSET X", "PARENT X Y", parent_x, parent_y, "STEPS", steps)
                if child_y >= page_height:
                    print("CHILD OFFSET Y", "PARENT X Y", parent_x, parent_y, "STEPS", steps)
                if type(val) is list:
                    for v in val:
                        self.zine.set_draw_color(r=255, g=128, b=0)
                        self.zine.line(parent_x, parent_y, child_x, child_y)
                        self.zine.set_xy(child_x, child_y)
                        self.zine.cell(letter_size, letter_size, str(v))  # draw child
                        child_y +=5
                    child_x += self.zine.get_x() + 5
                else:
                    self.zine.line(parent_x, parent_y, child_x, child_y)
                    self.zine.set_xy(child_x, child_y)
                    self.zine.cell(letter_size, letter_size, str(val))  # draw child
                    child_x += self.zine.get_x() + 5

                previous_x = parent_x
                previous_y = parent_y
                parent_x -= steps/5
                parent_y += steps
                self.zine.set_xy(parent_x, parent_y)
                self.zine.set_draw_color(r=128, g=255, b=0)
                self.zine.line(
                    previous_x, previous_y,
                    self.zine.get_x(), self.zine.get_y()
                )

    def draw_year_name_reddit(self, tree_json, reddit_json, root, page_height,
                              width, margin, steps=30):
        """ Tree method draw_year_name_reddit layout:
            Branching sorted year followed by trees named that year on the
            left side and reddit blog titles related to those trees
            on the right side"""

        letter_size = 8
        right_margin = margin
        parent_x = self.zine.get_x()
        parent_y = self.zine.get_y()

        if root is not None:
            sorted_root = self.keys_sorting(tree_json)
            print("SORTED ROOT", sorted_root)
            for key in sorted_root:

                if parent_y >= page_height-margin*2:
                    self.zine.add_page()
                    self.zine.set_xy(width/2, margin)
                    print("NEW PAGE", "Y", self.zine.get_y())
                if parent_x >= width or parent_x <= margin:
                    self.zine.set_xy(width/2, parent_y)

                parent_x = self.zine.get_x()
                parent_y = self.zine.get_y()

                # draw the year
                self.zine.cell(letter_size, letter_size, str(key))  # draw the root
                print("YEAR", key, "YEAR X Y", parent_x, parent_y)

                # draw the tree names
                val = tree_json[key]["scientificName"]
                family = tree_json[key]["family"]
                child_x = parent_x - steps
                child_y = parent_y + letter_size
                if type(val) is list:
                    for v in val:
                        self.zine.set_draw_color(r=255, g=0, b=0)
                        self.zine.line(parent_x, parent_y, child_x, child_y)
                        self.zine.set_xy(child_x, child_y)
                        self.zine.cell(letter_size, letter_size, str(v))  # draw child

                        # draw reddit
                        self.zine.set_draw_color(r=0, g=0, b=255)
                        for r in reddit_json.keys():
                            if r in family:
                                reddit_list = reddit_json[r]
                                print("REDIT", reddit_list)
                                reddit_x = width -2*margin - child_x
                                reddit_y = child_y
                                for reddit in reddit_list:
                                    reddit_y += steps/5
                                self.zine.set_xy(reddit_x, reddit_y)
                                self.zine.cell(letter_size, letter_size, str(reddit))  # draw child
                                self.zine.line(child_x, child_y, reddit_x, reddit_y)
                                self.zine.set_xy(child_x, child_y)

                        child_y +=5
                    child_x += self.zine.get_x() + 5
                else:
                    self.zine.line(parent_x, parent_y, child_x, child_y)
                    self.zine.set_xy(child_x, child_y)
                    self.zine.cell(letter_size, letter_size, str(val))  # draw child
                    child_x += self.zine.get_x() + 5

                previous_x = parent_x
                previous_y = parent_y
                parent_x -= steps/5
                parent_y += steps
                self.zine.set_xy(parent_x, parent_y)
                self.zine.set_draw_color(r=128, g=255, b=0)
                self.zine.line(previous_x, previous_y, self.zine.get_x(), self.zine.get_y())

    def preorder_position_nodes(self, data, root, page_height,
                                width, margin, steps=30):
        letter_size = 5
        right_margin = margin
        parent_x = self.zine.get_x()
        parent_y = self.zine.get_y()

        if root is not None:
            sorted_root = self.keys_sorting(data)
            print("SORTED ROOT", sorted_root)
            for key in sorted_root:

                if parent_y >= page_height:
                    self.zine.add_page()
                    self.zine.set_xy(width/2, margin)
                    print("NEW PAGE", "Y", self.zine.get_y())
                if parent_x >= width or parent_x <= margin:
                    self.zine.set_xy(width/2, parent_y)

                parent_x = self.zine.get_x()
                parent_y = self.zine.get_y()

                # draw the year
                self.zine.cell(letter_size, letter_size, str(key))  # draw the root
                print("YEAR", key, "YEAR X Y", parent_x, parent_y)

                # draw the tree names
                val = data[key]["scientificName"]
                child_x = parent_x - steps
                child_y = parent_y + letter_size
                if child_x >= right_margin:
                    print("CHILD OFFSET", "PARENT X Y", parent_x, parent_y, "STEPS", steps)
                print("VALUE", val, "X", child_x, "Y", child_y)
                if type(val) is list:
                    for v in val:
                        self.zine.set_draw_color(r=255, g=128, b=0)
                        self.zine.line(parent_x, parent_y, child_x, child_y)
                        self.zine.set_xy(child_x, child_y)
                        self.zine.cell(letter_size, letter_size, str(v))  # draw child
                        child_y +=5
                    child_x += self.zine.get_x() + 5
                else:
                    self.zine.line(parent_x, parent_y, child_x, child_y)
                    self.zine.set_xy(child_x, child_y)
                    self.zine.cell(letter_size, letter_size, str(val))  # draw child
                    child_x += self.zine.get_x() + 5

#                parent_x -= steps/5
#                parent_y += steps*2
#                self.zine.set_draw_color(r=128, g=255, b=0)
#                self.zine.line(self.zine.get_x(), self.zine.get_y(), parent_x, parent_y)


    def weird_position_nodes(self, data, root, page_height, width, margin, steps=30):
        letter_size = 5
        right_margin = margin

        x = self.zine.get_x()
        y = self.zine.get_y()


        if root is None:
            root = self.keys_sorting(data)
            print("SORTED ROOT", root)
        for key in root:
            parent_x = x
            parent_y = y

            # draw the year
            self.zine.cell(letter_size, letter_size, str(key))  # draw the root
            print("YEAR", key, "YEAR X Y", parent_x, parent_y)

            # draw the tree names
            val = data[key]["scientificName"]
            child_x = parent_x - steps
            child_y = parent_y + letter_size
            if child_x >= right_margin:
                print("CHILD OFFSET", "PARENT X Y", parent_x, parent_y, "STEPS", steps)
            print("VALUE", val, "X", child_x, "Y", child_y)
            if type(val) is list:
                for v in val:
                    self.zine.set_draw_color(r=255, g=128, b=0)
                    self.zine.line(parent_x, parent_y, child_x, child_y)
                    self.zine.set_xy(child_x, child_y)
                    self.zine.cell(letter_size, letter_size, str(v))  # draw child
                    child_y +=5
                child_x += self.zine.get_x() + 5
            else:
                self.zine.line(parent_x, parent_y, child_x, child_y)
                self.zine.set_xy(child_x, child_y)
                self.zine.cell(letter_size, letter_size, str(val))  # draw child
                child_x += self.zine.get_x() + 5

            x -= steps/5
            y += steps
            self.zine.set_draw_color(r=128, g=255, b=0)
            self.zine.line(parent_x, parent_y, x, y)
#                self.zine.set_xy(x, y)

            if y >= page_height:
                print("NEW PAGE")
                self.zine.add_page()
                self.zine.set_xy(x, margin)
                print("Y", self.zine.get_y())
            if x >= width or x <= margin:
                self.zine.set_xy(width/2, y)
#                else:
#                    self.zine.set_xy(x, y)

#                self.wrap_page(x, y, page_height, width, margin)

    def position_nodes(self, root, x, y, page_height, margin, steps=26):
        letter_size = 10
        if self.zine.get_y() >= page_height:
            self.zine.add_page()
            self.zine.set_xy(margin, margin)
            x = self.zine.get_x()
            y = self.zine.get_y()
        else:
            self.zine.set_xy(x, y)
        root_x = x
        root_y = y

        if root is not None:
#            if root.left:  # left display node name

            # draw left branch
            self.zine.set_draw_color(r=255, g=128, b=0)
            node_left_x = root_x - steps / 2
            node_left_y = root_y + steps
            self.zine.cell(letter_size, letter_size, str(root.key))  # letter is sorted
            self.zine.line(root_x, root_y, node_left_x, node_left_y)
            print("LEFT NODE", root.key, node_left_x, node_left_y)

            # is there more nodes at left side?
            steps *= 2
            self.zine.set_xy(node_left_x, node_left_y)
            self.position_nodes(root.left, node_left_x, node_left_y, page_height, margin)

#            if root.right:  # right display node name

            # draw right branch
            self.zine.set_draw_color(r=128, g=255, b=0)
            node_right_x = root_x + steps / 2
            node_right_y = root_y + steps
            self.zine.cell(letter_size, letter_size, str(root.key))  # letter is sorted
            self.zine.line(root_x, root_y, node_right_x, node_right_y)
            print("RIGHT NODE", root.key, node_right_x, node_right_y)

            # is there more nodes at right side?
            steps *= 2
            self.zine.set_xy(node_right_x, node_right_y)
            self.position_nodes(root.right, node_right_x, node_right_y, page_height, margin)

    def print_2d_tree(self, root, space=0, LEVEL_SPACE = 5):
        if (root == None): return
        space += LEVEL_SPACE
        self.print_2d_tree(root.right, space)
        # print() # neighbor space
        for i in range(LEVEL_SPACE, space): print(end = " ")
        print("|" + str(root.key) + "|<")
        self.print_2d_tree(root.left, space)

    def draw_tree(self, node, level=0, res=[]):
        if level < len(res):
            if node:
                res[level].append(node.key)
            else:
                res[level].append(" ")
        else:
            if node:
                res.append([node.key])
            else:
                res.append([" "])
        if not node:
            return
        self.draw_tree(node.left, level+1, res)
        self.draw_tree(node.right, level+1, res)
        print("RES from draw tree is", res)
        return res

    def print_tree(self, node, x, y, width, margin, page_height, steps=5):
        letter_size = 20

        tree_array = self.draw_tree(node)
        h = len(tree_array)
        white_spaces = (2**h)-1

        def print_spaces(n, x, i):
            for i in range(n):
                if i % 2 == 0:
                    x += float(steps/2)
                else:
                    x -= float(steps/2)
                self.zine.set_xy(x, y)

        prev_x = None
        prev_y = None
        for level in tree_array:
            print("LEVEL is", level, "AND OF TYPE", type(level))
            for i, key in enumerate(level):
                if i==0 and len(level) == 1:
                    self.zine.cell(letter_size, letter_size, str(key))  # letter is sorted
                    print("FIRST NODE", key, "POSITION OF KEY", x, y, "i is", i)
                    prev_x = x
                    prev_y = y
                    self.zine.set_draw_color(r=0, g=255, b=0)
                    print_spaces(1+2*white_spaces, i, x)
#                    self.zine.line(prev_x, prev_y, self.zine.get_x(), self.zine.get_y())
                    print("NEW xy after first node", self.zine.get_x(), self.zine.get_y())
                else:
                    if key == " ":
                        pass
                    else:
                        print("POSITION OF KEY", key, self.zine.get_x(), self.zine.get_y(), "i is", i)
                        self.zine.line(prev_x, prev_y, self.zine.get_x(), self.zine.get_y())
                        self.zine.cell(letter_size, letter_size, str(key))  # letter is sorted
                        print_spaces(1+2*white_spaces, i, x)
            y += steps/2
            if i % 2 == 0:
                x += float(steps)
            else:
                x -= float(steps)
            self.zine.set_xy(x, y)
        print()

    def print_bin_tree(self, node, x, y, width, margin, page_height, steps=5):
        letter_size = 20

        tree_array = self.draw_tree(node)
        h = len(tree_array)
        white_spaces = (2**h)-1

        def print_spaces(n, x, i):
            for i in range(n):
                if i % 2 == 0:
                    x += float(steps/2)
                else:
                    x -= float(steps/2)
                self.zine.set_xy(x, y)

        prev_x = None
        prev_y = None
        count = 0
        pos_level = {}
        for level in tree_array:
            # TODO: count the levels and revisit them, keep a dict of level and
            # x,y
            count += 1
            pos_level[count] = (x, y)
            print("LEVEL is", level, "AND OF TYPE", type(level))
            for i, key in enumerate(level):
                if i==0 and len(level) == 1:
                    prev_x = x
                    prev_y = y
                    self.zine.cell(letter_size, letter_size, str(key))  # letter is sorted
                    print("FIRST NODE", key, "POSITION OF KEY", x, y, "i is", i)
                    print_spaces(1+2*white_spaces, i, x)
                    print("NEW xy after first node", self.zine.get_x(), self.zine.get_y())
                else:
                    if key == " ":
                        pass
                    else:
                        print("POSITION OF KEY", key, self.zine.get_x(), self.zine.get_y(), "i is", i)
                        self.zine.cell(letter_size, letter_size, str(key))  # letter is sorted
                        print_spaces(1+2*white_spaces, i, x)
            y += steps/2
            if i % 2 == 0:
                x += float(steps)
            else:
                x -= float(steps)
            self.zine.set_xy(x, y)
        print("LEVELS ARE", pos_level)
        for count, coord in pos_level.items():
            self.zine.set_draw_color(r=0, g=255, b=0)
            try:
                self.zine.line(coord[0], coord[1], pos_level[count+1][0], pos_level[count+1][1])
            except Exception as e:
                print(e)

    def print_tree_terminal(self, node):

        tree_array = self.draw_tree(node)
        h = len(tree_array)
        white_spaces = (2**h)-1

        def print_spaces(n):
            for i in range(n):
                print(" ",end="")

        for level in tree_array:
            white_spaces = white_spaces//2
            for i, key in enumerate(level):
                if i==0:
                    print_spaces(white_spaces)
            print(key, end="")
            print_spaces(1+2*white_spaces)
        print()

    def wrap_page(self, x, y, page_height, page_width, margin):
        right_margin = page_width/2
        if y >= page_height:
            self.zine.add_page()
            self.zine.set_xy(right_margin, margin)
        elif x >= page_width:
            self.zine.set_xy(right_margin, y)
        else:
            self.zine.set_xy(x, y)

class Node:

    def __init__(self, item = 0):
        self.key = item
        self.left, self.right = None, None

root = None
res = []
