# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/views.slope_graph.ipynb (unless otherwise specified).

__all__ = ['ZOOM_OUT', 'ZOOM_IN', 'X_TOLERANCE', 'Y_TOL_DIV', 'LOW', 'HIGH', 'StaticSlopeGraph', 'SlopeGraph']

# Cell
from ..widgets import Output
import numpy as np
from itertools import cycle
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
from matplotlib import rcParams
from matplotlib import widgets as mpl

ZOOM_OUT = '-'
ZOOM_IN = '+'
X_TOLERANCE = 0.1
Y_TOL_DIV = 50
LOW = 'Low'
HIGH = 'High'

# Cell
class StaticSlopeGraph(Output):

    def __init__(self, model, width=3, height = 1.75):
        super().__init__()
        self.model = model

        rcParams.update({'font.size': 6})
        rcParams['legend.handlelength'] = 0
        rcParams['legend.numpoints'] = 1

        with self:
            self.fig = plt.figure(figsize=[width, height])
            self.axes = self.fig.add_subplot()
            self.fig.suptitle(self.model.title, x=0.4, y=0.99, size='large')
            #self.fig.tight_layout(pad=0, w_pad=0, h_pad=0)  #  TODO Remove dead code?
            self.fig.subplots_adjust(right=0.80)

            # Interactive canvas has a lot of features we want to hide
            self.fig.canvas.toolbar_visible = False
            self.fig.canvas.header_visible = False # Hide name at top of figure
            self.fig.canvas.footer_visible = False
            self.fig.canvas.resizable = False
            self.fig.canvas.capture_scroll = False

        self.lines = []

        # Create plot components, store lines
        line_cycler = cycle(["-"])  # TODO Use different line styles? ,"--","-.",":"])
        marker_cycler = cycle(['d','s','o','+','*','x'])

        for i in range(len(self.model.names)):
            line = Line2D(self.model.xs[i],self.model.ys[i], label=self.model.names[i],
                          marker=next(marker_cycler), c='black', markerfacecolor='black',
                          linestyle=next(line_cycler), picker=True)
            self.axes.add_line(line)
            self.lines.append(line)

        #self.axes.set_ylabel(est.land.dep_var.value)
        self.axes.set_ylabel(self.model.ylabel)
        self.axes.yaxis.set_label_coords(-0.19,0.5)
        self.fig.subplots_adjust(left=0.17)

        self.axes.autoscale()
        self.axes.legend(loc='right', bbox_to_anchor=(1.27, 0.5), borderaxespad=0.0, frameon=False)
        self.axes.set_xticklabels(['',LOW+' '+self.model.xvar,'','','',HIGH+' '+self.model.xvar])

        # Note: Keeping refs to Button()'s to prevent gc
        self.zoom_out = mpl.Button(plt.axes([0.81, 0.79, 0.05, 0.07]), ZOOM_OUT, color='1')
        self.zoom_in = mpl.Button(plt.axes([0.88, 0.79, 0.05, 0.07]), ZOOM_IN, color='1')

# Cell

#out = Output()

class SlopeGraph(StaticSlopeGraph):

    def __init__(self, model, width=3, height = 1.75):
        super().__init__(model)

        self.cur_line = None  # Selected line: index into self.lines or None
        self.ind = None  # Index of selected start/end point: None, 0, or 1
        self.adjust_y_tolerance()

        with self:
        # Register our methods as event callbacks
            self.fig.canvas.mpl_connect('button_press_event', self.on_button_press)
            self.fig.canvas.mpl_connect('pick_event', self.on_pick)

    def adjust_y_tolerance(self):
        ylim = self.axes.get_ylim()
        self.y_tolerance = (ylim[1] - ylim[0]) / Y_TOL_DIV

    def scale(self, lower_change, upper_change):
        """Change Y-axis limits, copensate y click distance, redraw"""
        ylim = self.axes.get_ylim()
        self.axes.set_ylim([ylim[0]+lower_change, ylim[1]+upper_change])
        self.adjust_y_tolerance()
        self.fig.canvas.draw()

    def on_button_press(self, event):
        #with out:
            if event.button == 1:

                # Adjust axes?
                if event.inaxes == self.zoom_out.ax:
                    self.scale(-1,+1)
                elif event.inaxes == self.zoom_in.ax:
                    self.scale(+1,-1)

                else: # Starting to drag line start/end point?
                    self.cur_line = None
                    self.ind = None

                    for i in range(len(self.model.names)):
                        x = np.array(self.lines[i].get_xdata())
                        y = np.array(self.lines[i].get_ydata())
                        d = np.sqrt((x-event.xdata)**2 + (y - event.ydata)**2)

                        #print(min(d))
                        #print(self.y_tolerance)
                        if min(d) <= self.y_tolerance:
                            self.cur_line = i
                            self.ind = 0 if d[0] < d[1] else 1
                            break

                    if self.cur_line is None:
                        self.on_plot_change()
                    else:
                        self.on_plot_change(self.model.xvar,self.model.names[self.cur_line])


    def on_plot_change(self, xvar=None, name=None, slope=None):

        if xvar is None:
            self.model.plot_status = ''
        else:
            self.model.plot_status = SIMPLE_EFFECT_OF_3 % (xvar, self.model.dep_var, name[0].lower()+name[1:])
            # Demo code
            #self.model.predictors[0].slope = '%.4f' % slope
            #self.model.predictors[1].slope = '%.4f' % slope

    def on_pick(self, event):
        #with out:
            # Calc slope
            x = event.artist.get_xdata()
            y = event.artist.get_ydata()

            if float(x[1]) - float(x[0]) == 0.0:
                slope = 0.0
            else:
                slope = (float(y[1]) - float(y[0])) / (float(x[1]) - float(x[0]))

            # Highlight selected line, update status text
            #self.est.on_plot_select(self.title, event.artist.get_label(), slope)
            event.artist.set_color('blue')


    def update(self, lines):
        ''' also not currently used? '''
        """Change plot to reflect new line start/end point locations"""

        for i, line in enumerate(lines):
            self.lines[i].set_data([line[0][0],line[1][0]], [line[0][1],line[1][1]])

        # Crude auto zoom TODO Why autoscale() no worky worky?

        self.ymin = min(self.ymin, line[0][1], line[1][1])
        self.ymax = max(self.ymin, line[0][1], line[1][1])
        view_low, view_high = self.axes.get_ylim()

        while self.ymin < view_low or self.ymax > view_high:
            self.scale(-1,+1)
            view_low, view_high = self.axes.get_ylim()

        # TODO Auto zoom out? - Currently cancels zoom in

        #prop = abs(self.ymax - self.ymin) / abs(view_high - view_low)

        #while prop < 0.07:
        #    self.scale(+1,-1)
        #    view_low, view_high = self.axes.get_ylim()
        #    prop = abs(self.ymax - self.ymin) / abs(view_high - view_low)

        # self.debug('"'+self.title+'": '+str(self.ymin)+' '+str(self.ymax)+' w/in '+str(view_low)+' '+str(view_high)+' prop='+str(prop))

    def set_layout(self, new_layout):
        self.layout = new_layout

    def on_motion_notify(self, event):
        ''' not currently used '''
        if event.button == 1 and self.ind is not None and self.cur_line is not None:
            if event.inaxes == self.lines[self.cur_line].axes:
                self.xs[self.cur_line][self.ind] = self.ind + 1
                self.ys[self.cur_line][self.ind] = event.ydata
                self.lines[self.cur_line].set_data(self.xs[self.cur_line],
                                                self.ys[self.cur_line])

                # Calc slope
                if self.xs[self.cur_line][1] - self.xs[self.cur_line][0] == 0:
                    slope = 0.0
                else:
                    slope = (self.ys[self.cur_line][1] - self.ys[self.cur_line][0]) / \
                            (self.xs[self.cur_line][1] - self.xs[self.cur_line][0])

                self.est.on_plot_change(self.xvar,self.names[self.cur_line],slope)

    def on_button_release(self, event):
        ''' not currently used '''
        if event.button == 1:
            self.ind = None
            self.background = None

            if self.cur_line is not None:
                self.lines[self.cur_line].figure.canvas.draw()

            self.cur_line = None
            self.est.on_plot_change()