###############################################################################
#
# AbstractPlot.py - Abstract base class for plotting.
#
###############################################################################
#                                                                             #
#    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 3 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, see <http://www.gnu.org/licenses/>.     #
#                                                                             #
###############################################################################

from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.transforms as mtransforms

from matplotlib.patches import Rectangle

import matplotlib as mpl

import numpy as np


class AbstractPlot(FigureCanvas):
    '''
    Abstract base class for plotting.
    '''

    def __init__(self, options):
        self.options = options

        # Global plot settings
        mpl.rcParams['font.size'] = self.options.font_size
        mpl.rcParams['axes.titlesize'] = self.options.font_size
        mpl.rcParams['axes.labelsize'] = self.options.font_size
        mpl.rcParams['xtick.labelsize'] = self.options.font_size
        mpl.rcParams['ytick.labelsize'] = self.options.font_size
        mpl.rcParams['legend.fontsize'] = self.options.font_size
        mpl.rcParams['svg.fonttype'] = 'none'

        self.fig = Figure(facecolor='white', dpi=options.dpi)

        FigureCanvas.__init__(self, self.fig)

        self.cid = None

        self.type = '<none>'
        self.name = '<none>'

        self.axesColour = (0.5, 0.5, 0.5)

    def savePlot(self, filename, dpi=300):
        imgFormat = filename[filename.rfind('.') + 1:len(filename)]
        if imgFormat in ['png', 'pdf', 'ps', 'eps', 'svg']:
            self.fig.savefig(filename, format=imgFormat, dpi=dpi,
                             facecolor='white', edgecolor='white', bbox_inches='tight')
        else:
            pass

    def labelExtents(self, xLabels, xFontSize, xRotation, yLabels, yFontSize, yRotation):
        self.fig.clear()

        tempAxes = self.fig.add_axes([0, 0, 1.0, 1.0])

        tempAxes.set_xticks(np.arange(len(xLabels)))
        tempAxes.set_yticks(np.arange(len(yLabels)))

        xText = tempAxes.set_xticklabels(
            xLabels, size=xFontSize, rotation=xRotation)
        yText = tempAxes.set_yticklabels(
            yLabels, size=yFontSize, rotation=yRotation)

        bboxes = []
        for label in xText:
            bbox = label.get_window_extent(self.get_renderer())
            bboxi = bbox.transformed(self.fig.transFigure.inverted())
            bboxes.append(bboxi)
        xLabelBounds = mtransforms.Bbox.union(bboxes)

        bboxes = []
        for label in yText:
            bbox = label.get_window_extent(self.get_renderer())
            bboxi = bbox.transformed(self.fig.transFigure.inverted())
            bboxes.append(bboxi)
        yLabelBounds = mtransforms.Bbox.union(bboxes)

        self.fig.clear()

        return xLabelBounds, yLabelBounds

    def xLabelExtents(self, labels, fontSize, rotation=0):
        self.fig.clear()

        tempAxes = self.fig.add_axes([0, 0, 1.0, 1.0])
        tempAxes.set_xticks(np.arange(len(labels)))
        xLabels = tempAxes.set_xticklabels(
            labels, size=fontSize, rotation=rotation)

        bboxes = []
        for label in xLabels:
            bbox = label.get_window_extent(self.get_renderer())

            bboxi = bbox.transformed(self.fig.transFigure.inverted())
            bboxes.append(bboxi)
        xLabelBounds = mtransforms.Bbox.union(bboxes)

        self.fig.clear()

        return xLabelBounds

    def yLabelExtents(self, labels, fontSize, rotation=0):
        self.fig.clear()

        tempAxes = self.fig.add_axes([0, 0, 1.0, 1.0])
        tempAxes.set_yticks(np.arange(len(labels)))
        yLabels = tempAxes.set_yticklabels(
            labels, size=fontSize, rotation=rotation)

        bboxes = []
        for label in yLabels:
            bbox = label.get_window_extent(self.get_renderer())
            bboxi = bbox.transformed(self.fig.transFigure.inverted())
            bboxes.append(bboxi)
        yLabelBounds = mtransforms.Bbox.union(bboxes)

        self.fig.clear()

        return yLabelBounds

    def formatLabels(self, labels):
        formattedLabels = []
        for label in labels:
            value = float(label.get_text())
            if value < 0.01:
                valueStr = '%.2e' % value
                if 'e-00' in valueStr:
                    valueStr = valueStr.replace('e-00', 'e-')
                elif 'e-0' in valueStr:
                    valueStr = valueStr.replace('e-0', 'e-')
            else:
                valueStr = '%.3f' % value

            formattedLabels.append(valueStr)

        return formattedLabels

    def removeExtraZeros(self, label):
        if '.' in label:
            while label[-1] == '0':
                label = label[0:-1]

        if label[-1] == '.':  # remove potential trailing decimal point
            label = label[0:-1]

        return label

    def boundingBox(self, data, ax, label, bBoundingBoxes, bLabels):
        ''' Draw bounding box around data.'''
        data = np.array(data)

        width = max(data[:, 0]) - min(data[:, 0])
        height = max(data[:, 1]) - min(data[:, 1])
        r = Rectangle((min(data[:, 0]), min(data[:, 1])), width, height)

        if bBoundingBoxes:
            ax.add_artist(r)
            r.set_clip_box(ax.bbox)
            r.set_alpha(0.1)
            r.set_facecolor((0.5, 0.5, 0.5))

        if bLabels:
            ax.annotate(label, xy=(min(data[:, 0]), max(data[:, 1])), xytext=(0, 0),
                        textcoords='offset points', ha='right', va='bottom',
                        bbox=dict(boxstyle='round,pad=0.5', fc=(0.5, 0.5, 0.5), alpha=0.1))
