# Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2.0,
# as published by the Free Software Foundation.
#
# This program is also distributed with certain software (including
# but not limited to OpenSSL) that is licensed under separate terms, as
# designated in a particular file or component or in included license
# documentation.  The authors of MySQL hereby grant you an additional
# permission to link the program and your derivative works with the
# separately licensed software that they have included with MySQL.
# This program is distributed in the hope that it will be useful,  but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
# the GNU General Public License, version 2.0, for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

from cairo_utils import *
from math import pi, sin, cos
from random import random

class TreePieChart:
    """A pie chart that displays fractional information hierarchically."""
    def __init__(self, tree, cairo):
        self._tree = tree
        self._level_radius = 100
        self._c = cairo
        self._main_colors = [
        (0.1, 0.4, 0.1),
        (0.1, 0.4, 0.4),
        (0.4, 0.4, 0.1),
        (0.4, 0.1, 0.1),
        ]

    def plot(self):
        self._c.save()
        self._c.translate(400, 400)
        self.plot_chart_level(self._tree, 0.5, 0, 360)
        self._c.restore()

    def plot_chart_level(self, data, depth = 0, start_angle = 0, end_angle = 360, color = None):
        total = 0
        # calculate total value for the level and their fractions
        for c, v in list(data.values()):
            total += v
        if total == 0 or depth > 2:
            return
    
        range = end_angle - start_angle
        i = 0
        text_to_show = []
        # calculate the percentage of each part in relation to the total and render it
        for k, (child_dict, v) in list(data.items()):
            if v == 0:
                continue
            pct = float(v) / total
            end_angle = start_angle + (pct * range)
            self._c.set_line_width(0.5)
            self._c.new_path()
            self._c.arc(0, 0, self._level_radius * (depth+1), start_angle * pi / 180, end_angle * pi / 180)
            self._c.arc_negative(0, 0, (self._level_radius) * (depth + 0), end_angle * pi / 180, start_angle * pi / 180)
            self._c.close_path()
            self._c.set_source_rgba(0, 0, 0, 1)
            self._c.stroke_preserve()
            if color:
                r, g, b = color
                r = r * 1.4 + random() * 0.2
                g = g * 1.4 + random() * 0.2
                b = b * 1.4 + random() * 0.2
            else:
                r, g, b = self._main_colors[i]
            r, g, b = random(),random(),random()
            i += 1
            self._c.set_source_rgb(r, g, b)
            self._c.fill()
            self._c.save()
            text_angle = (start_angle + pct * range/2) 
            self._c.set_font_size(12)
            text = "%s (%i)" % (k, v)
            if text_angle > 90 and text_angle < 270:
                ext = self._c.text_extents(text)
                text_to_show.append((((text_angle+180)*pi / 180), -self._level_radius * (depth+0.2) - ext.width, text))
            else:
                text_to_show.append(((text_angle*pi / 180), self._level_radius * (depth+0.2), text))
            self._c.stroke()
            self._c.restore()
            self.plot_chart_level(child_dict, depth+1, start_angle, end_angle, (r, g, b))
            start_angle = end_angle

        self._c.set_font_size(12)
        self._c.set_source_rgb(1, 1, 1)
        for a, x, t in text_to_show:
            self._c.save()
            self._c.rotate(a)
            self._c.move_to(x, 0)
            self._c.show_text(t)
            self._c.restore()

def event_waits_summary_by_thread_by_event_name_to_tree(before_rows, after_rows):
    before_values = {}
    for thread_id, event_name, count_star, sum_timer_wait, min_timer_wait, avg_timer_wait, max_timer_wait in before_rows:
        before_values[event_name] = count_star, sum_timer_wait, min_timer_wait, avg_timer_wait, max_timer_wait

    root = {}
    for thread_id, event_name, count_star, sum_timer_wait, min_timer_wait, avg_timer_wait, max_timer_wait in after_rows:
        path = event_name.split("/")
        node = root
        old_count_star, old_sum_timer_wait, old_min_timer_wait, old_avg_timer_wait, old_max_timer_wait = before_values[event_name]
        for p in path:
            if p in node:
                node = node[p][0]
            else:
                new_node = {}
                node[p] = new_node, count_star-old_count_star
                node = new_node
    
    def sum_up(node):
        total = 0
        for k, (d, v) in list(node.items()):
            s = sum_up(d)
            node[k] = d, s+v
            total += s+v
        return total
    sum_up(root)

    return root

if __name__ == "__main__":
    import string
    odata = [[int(0) if i[0] in string.digits else i.replace("wait/", "wait-") for i in s.split("\t")] for s in open("event_waits_summary_by_thread_by_event_name.tsv").readlines()]
    data = [[int(i) if i[0] in string.digits else i.replace("wait/", "wait-") for i in s.split("\t")] for s in open("event_waits_summary_by_thread_by_event_name2.tsv").readlines()]
    surf = ImageSurface(cairo.CAIRO_FORMAT_ARGB32, 800, 800)
    c = Context(surf)
    c.set_source_rgb(0,0,0)
    c.paint()
    tree = event_waits_summary_by_thread_by_event_name_to_tree(odata, data)

    xtree = {
      "a" : ({"a1":({}, 5), "a2":({},5)}, 10),
      "b" : ({"b1":({}, 3), "b2":({},5), "b3":({}, 2)}, 10)
    }
    piechart = TreePieChart(tree, c)
    piechart.plot()

    surf.write_to_png("pie.png")
