#!/usr/bin/python3

# Cbenchsuite - A C benchmarking suite for Linux benchmarking.
# Copyright (C) 2013  Markus Pargmann <mpargmann@allfex.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# 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 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.


import matplotlib
matplotlib.use('svg')
import matplotlib.pyplot as pyplt
import statistics
import operator
import re
import datetime
import json
import traceback

def property_get(properties, key):
    if properties and key in properties:
        return properties[key]
    return None

def mean_confidence_interval(data, confidence=0.95):
    mean, _, _, _, _, h = statistics.stats(data, 1-confidence)
    return mean, h

class parameter_sort:
    def _part_parameter(self, par):
        slash = par.split('/')
        equal = [i.split('=') for i in slash]

        ret = []

        for i in equal:
            ret += [j.split(' ') for j in i]
        return ret
    def __init__(self, obj, *args):
        if isinstance(obj, tuple):
            obj = obj[0]
        self.cmp_list = self._part_parameter(obj)
    def _rec_cmp(self, a, b):
        if isinstance(a, int):
            if a < b:
                return -1
            if a > b:
                return 1
            return 0
        if isinstance(a, str):
            mstr = "([^0-9]*)([0-9]+(.[0-9]+|))(.*)"
            am = re.match(mstr, a)
            bm = re.match(mstr, b)
            if not am or not bm:
                if a < b:
                    return -1
                if a == b:
                    return 0
                return 1

            if am.group(1) < bm.group(1):
                return -1
            if am.group(1) > bm.group(1):
                return 1
            ai = float(am.group(2))
            bi = float(bm.group(2))
            if ai < bi:
                return -1
            if ai > bi:
                return 1

            if am.group(4) < bm.group(4):
                return -1
            if am.group(4) > bm.group(4):
                return 1
            return 0

        for i in range(len(a)):
            if i == len(b):
                return -1
            ret = self._rec_cmp(a[i], b[i])
            if ret != 0:
                return ret

        if len(a) < len(b):
            return -1
        return 0
    def _compare(self, opt):
        a = self.cmp_list
        b = opt.cmp_list
        return self._rec_cmp(a, b)
    def __lt__(self, obj):
        return self._compare(obj) < 0
    def __gt__(self, obj):
        return self._compare(obj) > 0
    def __le__(self, obj):
        return self._compare(obj) <= 0
    def __ge__(self, obj):
        return self._compare(obj) >= 0
    def __eq__(self, obj):
        return self._compare(obj) == 0
    def __ne__(self, obj):
        return self._compare(obj) != 0
def parameter_sort_create(obj):
    return parameter_sort(obj)

def _sort_keys(keys):
    return sorted(keys, key=parameter_sort_create)

def _create_figure(properties):
    xsize = property_get(properties, 'xsize')
    ysize = property_get(properties, 'ysize')
    dpi = property_get(properties, 'dpi')
    fontsize = property_get(properties, 'fontsize')
    return pyplt.figure(figsize=(xsize, ysize), dpi=dpi)

def _plot_stuff(fig, ax, properties, path, legend_handles=None, legend_labels=None, nr_runs=None):
    fs = property_get(properties, 'watermarkfontsize')

    additional_info = ''
    if nr_runs:
        additional_info += 'Repetitions: ' + str(min(nr_runs))
 + " - " + str(max(nr_runs))

    additional_info += "\n" + datetime.date.today().isoformat()

    wm = property_get(properties, 'watermark')
    if wm != '':
        additional_info += "\n" + wm

    fig.text(1,
 0, additional_info, fontsize=fs, color='black', ha='right', va='bottom', alpha=0.7)

    fs = property_get(properties, 'xtickfontsize')
    ax.tick_params(axis='x', which='major', labelsize=fs)
    ax.tick_params(axis='x', which='minor', labelsize=fs)

    fs = property_get(properties, 'ytickfontsize')
    ax.tick_params(axis='y', which='major', labelsize=fs)
    ax.tick_params(axis='y', which='minor', labelsize=fs)

    title = property_get(properties, 'title')
    if title != None:
        fs = property_get(properties, 'titlefontsize')
        ax.set_title(title, fontsize=fs)
    x_label = property_get(properties, 'xlabel')
    if x_label != None:
        fs = property_get(properties, 'xlabelfontsize')
        ax.set_xlabel(x_label, fontsize=fs)
    y_label = property_get(properties, 'ylabel')
    if y_label != None:
        fs = property_get(properties, 'ylabelfontsize')
        ax.set_ylabel(y_label, fontsize=fs)
    ax.grid(b=properties['grid'], which=properties['grid-ticks'], axis=properties['grid-axis'])
    no_legend = property_get(properties, 'no-legend')
    if not no_legend:
        fs = property_get(properties, 'legendfontsize')
        if legend_handles and legend_labels:
            handles = legend_handles
            labels = legend_labels
        else:
            handles, labels = ax.get_legend_handles_labels()
            by_label = dict(zip(labels, handles))
            labels = []
            for k,v in by_label.items():
                labels.append((k,v))
            hl = sorted(labels, key=parameter_sort_create)
            labels, handles = zip(*hl)
        max_label = 0
        for k in labels:
            max_label = max(len(k), max_label)

        est_len = (max_label * fs) / 30
        xlims = ax.get_xlim()
        x_range = xlims[1] - xlims[0]
        ax.set_xlim(xmax = xlims[0] + x_range * max(1.01, 0.00155 * est_len ** 2 - 0.0135 * est_len + 1.09))
        ax.legend(handles, labels, loc='center right', bbox_to_anchor=(1.13, 0.5), fancybox=True, shadow=True, fontsize=fs)

    path += '.' + property_get(properties, 'file-type')
    for i in range(10):
        try:
            fig.savefig(path)
            break
        except Exception as err:
            if i == 9:
                raise(err)
            print("Failed to plot " + path + " for the " + str(i) + " time. This may be due to some parallel rendering bugs in matplotlib, trying again.")
    fig.delaxes(ax)
    pyplt.close(fig)
    print("Generated figure " + path)


# Takes something like this:
#data_line = {
#        'x': [(0,1), (1,3), (2,2), (3,5)],
#        'y': [(0,1), (2,1), (3,2), (4,5)],
#        'z': [(0,[1,2,3,2,2,2,2]), (1,[2,3,2,2,3,3,3,3]), (2,2), (3,5)]
#}

def plot_line_chart(data, path, properties=None, fmts_arg = None, x_keys = None):
    fig = _create_figure(properties)
    ax = fig.add_subplot(111)
    if fmts_arg == None:
        fmts = [
                {'color': '#348ABD', 'linestyle': '-', 'marker': ' '},
                {'color': '#A60628', 'linestyle': '-', 'marker': ' '},
                {'color': '#467821', 'linestyle': '-', 'marker': ' '},
                {'color': '#CF4457', 'linestyle': '-', 'marker': ' '},
                {'color': '#188487', 'linestyle': '-', 'marker': ' '},
                {'color': '#E24A33', 'linestyle': '-', 'marker': ' '},
                {'color': '#348ABD', 'linestyle': '--', 'marker': ' '},
                {'color': '#A60628', 'linestyle': '--', 'marker': ' '},
                {'color': '#467821', 'linestyle': '--', 'marker': ' '},
                {'color': '#CF4457', 'linestyle': '--', 'marker': ' '},
                {'color': '#188487', 'linestyle': '--', 'marker': ' '},
                {'color': '#E24A33', 'linestyle': '--', 'marker': ' '},
                {'color': '0.4', 'linestyle': '-', 'marker': ' '},
                {'color': '0.4', 'linestyle': '--', 'marker': ' '},
            ]