from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import numpy as np
import pandas as pd
import matplotlib.lines as mlines
from matplotlib.patches import Rectangle

from ..scales.utils import resolution
from ..utils import make_iterable_ntimes, to_rgba, copy_missing_columns
from ..utils import SIZE_FACTOR
from .geom_point import geom_point
from .geom_segment import geom_segment
from .geom_crossbar import geom_crossbar
from .geom import geom


class geom_boxplot(geom):
    DEFAULT_AES = {'alpha': 1, 'color': '#333333', 'fill': 'white',
                   'linetype': 'solid', 'shape': 'o', 'size': 0.5,
                   'weight': 1}
    REQUIRED_AES = {'x', 'lower', 'upper', 'middle', 'ymin', 'ymax'}
    DEFAULT_PARAMS = {'stat': 'boxplot', 'position': 'dodge',
                      'outlier_alpha': 1, 'outlier_color': None,
                      'outlier_shape': 'o', 'outlier_size': 1.5,
                      'outlier_stroke': 0.5, 'notch': False,
                      'varwidth': False, 'notchwidth': 0.5,
                      'fatten': 2}

    def setup_data(self, data):
        if 'width' not in data:
            if 'width' in self.params and self.params['width']:
                data['width'] = self.params['width']
            else:
                data['width'] = resolution(data['x'], False) * 0.9

        # min and max outlier values
        omin = [np.min(lst) if len(lst) else +np.inf
                for lst in data['outliers']]
        omax = [np.max(lst) if len(lst) else -np.inf
                for lst in data['outliers']]
        data['ymin_final'] = np.min([data['ymin'], omin])
        data['ymax_final'] = np.max([data['ymax'], omax])

        # if varwidth not requested or not available, don't use it
        if ('varwidth' not in self.params or
                not self.params['varwidth'] or
                'relvarwidth' not in data):
            data['xmin'] = data['x'] - data['width']/2
            data['xmax'] = data['x'] + data['width']/2
        else:
            # make relvarwidth relative to the size of the
            # largest group
            data['relvarwidth'] /= data['relvarwidth'].max()
            data['xmin'] = data['x'] - data['relvarwidth']*data['width']/2
            data['xmax'] = data['x'] + data['relvarwidth']*data['width']/2
            del data['relvarwidth']

        del data['width']

        return data

    @staticmethod
    def draw_group(data, panel_scales, coord, ax, **params):
        def flat(*args):
            """Flatten list-likes"""
            return np.hstack(args)

        common_columns = ['color', 'size', 'linetype',
                          'fill', 'group', 'alpha', 'shape']
        # whiskers
        whiskers = pd.DataFrame({
            'x': flat(data['x'], data['x']),
            'y': flat(data['upper'], data['lower']),
            'yend': flat(data['ymax'], data['ymin'])})
        whiskers['xend'] = whiskers['x']
        copy_missing_columns(whiskers, data[common_columns])

        # box
        box_columns = ['xmin', 'xmax', 'lower', 'middle', 'upper']
        box = data[common_columns + box_columns].copy()
        box.rename(columns={'lower': 'ymin',
                            'middle': 'y',
                            'upper': 'ymax'},
                   inplace=True)

        # notch
        if params['notch']:
            box['ynotchlower'] = data['notchlower']
            box['ynotchupper'] = data['notchupper']

        # outliers
        num_outliers = len(data['outliers'].iloc[0])
        if num_outliers:
            def outlier_value(param):
                oparam = 'outlier_{}'.format(param)
                if params[oparam] is not None:
                    return params[oparam]
                return data[param].iloc[0]

            outliers = pd.DataFrame({
                'y': data['outliers'].iloc[0],
                'x': make_iterable_ntimes(data['x'][0],
                                          num_outliers),
                'fill': None})
            outliers['alpha'] = outlier_value('alpha')
            outliers['color'] = outlier_value('color')
            outliers['shape'] = outlier_value('shape')
            outliers['size'] = outlier_value('size')
            outliers['stroke'] = outlier_value('stroke')
            geom_point.draw_group(outliers, panel_scales,
                                  coord, ax, **params)

        # plot
        geom_segment.draw_group(whiskers, panel_scales,
                                coord, ax, **params)
        geom_crossbar.draw_group(box, panel_scales,
                                 coord, ax, **params)

    @staticmethod
    def draw_legend(data, da, lyr):
        """
        Draw a rectangle in the box

        Parameters
        ----------
        data : dataframe
        da : DrawingArea
        lyr : layer

        Returns
        -------
        out : DrawingArea
        """
        data['size'] *= SIZE_FACTOR

        # box
        facecolor = to_rgba(data['fill'], data['alpha'])
        if facecolor is None:
            facecolor = 'none'

        kwargs = dict(
           linestyle=data['linetype'],
           linewidth=data['size'])

        box = Rectangle((da.width*.125, da.height*.25),
                        width=da.width*.75,
                        height=da.height*.5,
                        facecolor=facecolor,
                        edgecolor=data['color'],
                        capstyle='projecting',
                        antialiased=False,
                        **kwargs)
        da.add_artist(box)

        kwargs['solid_capstyle'] = 'butt'
        kwargs['color'] = data['color']
        # middle strike through
        strike = mlines.Line2D([da.width*.125, da.width*.875],
                               [da.height*.5, da.height*.5],
                               **kwargs)
        da.add_artist(strike)

        # whiskers
        top = mlines.Line2D([da.width*.5, da.width*.5],
                            [da.height*.75, da.height*.9],
                            **kwargs)
        da.add_artist(top)

        bottom = mlines.Line2D([da.width*.5, da.width*.5],
                               [da.height*.25, da.height*.1],
                               **kwargs)
        da.add_artist(bottom)
        return da

@@ -1603,32 +1603,33 @@
 ta%5B'outliers'%5D%5D%0A
+%0A
         data%5B'ym
@@ -1648,16 +1648,45 @@
  np.min(
+np.column_stack(%0A            
 %5Bdata%5B'y
@@ -1698,16 +1698,25 @@
 , omin%5D)
+, axis=1)
 %0A       
@@ -1744,16 +1744,45 @@
  np.max(
+np.column_stack(%0A            
 %5Bdata%5B'y
@@ -1794,16 +1794,25 @@
 , omax%5D)
+, axis=1)
 %0A%0A      

Fix y limits for geom_boxplot