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

__all__ = ['VariableLanding', 'LEVELS', 'INIT_LEVEL', 'CONT', 'CATG', 'CONT_CATG_OPTS', 'FLOAT_TEXT_STEP',
           'DEFAULT_SD_VALUE', 'PROP_MIN', 'PROP_MAX', 'LEVEL_MIN', 'LEVEL_MAX', 'Interactions', 'Equation',
           'ModerationLandingView', 'DEPENDENT_VARIABLE', 'PREDICTORS', 'POWER_COVARIATES', 'POWER_CUSTOM_EFFECTS',
           'STD', 'UNSTD', 'TOTAL_COV_OPTS', 'PRED_MIN', 'COV_MIN', 'PRED_MAX', 'COV_MAX', 'BIG_BETA', 'LITTLE_BETA',
           'CONT', 'CATG']

# Cell
import itertools
import ipywidgets as ipyw
import functools
import traitlets

# Cell
from .mod_plot import ModerationPlot
from ..widget_containers import VBox, Box, HBox, GridBox
from ..widgets import HTML, Text, Dropdown, FloatText, BoundedFloatText, Label, HTMLMath, Checkbox, Output, Button

# Cell
LEVELS = 'Levels:'
INIT_LEVEL = 'Lvl'
CONT = 'Continuous'
CATG = 'Categorical'
CONT_CATG_OPTS = [CONT, CATG]
FLOAT_TEXT_STEP = 0.01
DEFAULT_SD_VALUE = 1
PROP_MIN = 0
PROP_MAX = 1
LEVEL_MIN = 2
LEVEL_MAX = 6

class VariableLanding:

    def __init__(self,  observe_method, model=None):
        self.observe_method = observe_method
        self.name =  Text(layout=ipyw.Layout(width='100px'), continuous_update=False)
        self.type = Dropdown(layout=ipyw.Layout(width='100px'), options=CONT_CATG_OPTS)
        #self.sd = FloatText(value=self.DEFAULT_SD_VALUE, step=self.FLOAT_TEXT_STEP)
        #self.slope = FloatText(step=self.FLOAT_TEXT_STEP)
        #self.cur_value = None

        # Levels - if categorical

        self.count = Dropdown(description=LEVELS, layout=ipyw.Layout(width='97px'),
                                   style={'description_width': '50px'},
                                   options=[i for i in range(LEVEL_MIN, LEVEL_MAX+1)])

        self.levels = []  # Names for each level
        #self.props = []  # Proportions for each level
        #self.slopes = []  # Slopes for each level
        #self.cur_values = []  # Current values for each level

        self.levels = [Text(layout=ipyw.Layout(width='100px')) for i in range(LEVEL_MAX)]
        for i, level in enumerate(self.levels):
            level.observe(functools.partial(self.observeLevels, i), 'value')

        '''
        for i in range(LEVEL_MAX):
            self.levels.append(Text(layout=ipyw.Layout(width='100px'), value=model.name + INIT_LEVEL + str(i+1)))
            self.props.append(BoundedFloatText(min=self.PROP_MIN, max=self.PROP_MAX, step=self.FLOAT_TEXT_STEP))
            self.slopes.append(FloatText(step=self.FLOAT_TEXT_STEP))
            self.cur_values.append(None)

        traitlets.link((self.model, 'name'), (self.name, 'value'))
        traitlets.link((self.model, 'count'), (self.count, 'value'))
        traitlets.link((self.model, 'type'), (self.type, 'value'))

        self.count.observe(observe_method, 'value')
        self.name.observe(observe_method, 'value')
        self.type.observe(observe_method, 'value')
        '''

        self.count.observe(self.observeCount, 'value')
        self.name.observe(self.observeName, 'value')
        self.type.observe(self.observeType, 'value')

        if model:
            self.handshake(model)

    def handshake(self, model):
        self.model = model
        self.setLanding()

    def setLanding(self):
        self.name.value = self.model.name
        self.type.value = self.model.type
        self.count.value = self.model.count
        for i in range(len(self.levels)):
            self.levels[i].value = self.model.name + INIT_LEVEL + str(i+1)

    def observeCount(self, change):
        self.model.count = change['new']
        self.observe_method(change)

    def observeName(self, change):
        self.model.name = change['new']
        self.observe_method(change)

    def observeType(self, change):
        self.model.type = change['new']
        self.observe_method(change)

    def observeLevels(self, i, change):
        self.model.levels[i] = change['new']

# Cell
class Interactions:
    """Mange list of interactions for calculation and user selection (include/exclude)"""

    FLOAT_TEXT_STEP = 0.01

    def __init__(self, label, grid, callback, model):
        self.model = model
        self.label = label  # widget, title of ckbox area
        self.grid = grid  # ckbox output widget
        self.callback = callback  # UI refresh method to be called after ckbox change
        self.ckboxes = {}  # Dict of ckboxes indexed by term ("*X1*X2")

    def reset(self):
        self.ckboxes = {}
        self.model.reset()

    def add(self, term, var_list):
        desc = self.model.add(term, var_list)
        ckbox = Checkbox(value=True, indent=False, description=desc,
                              layout=ipyw.Layout(width='max-content', margin='0 0 0 0'))
        ckbox.observe(functools.partial(self.on_checkbox, term), 'value')
        self.ckboxes[term] = ckbox

    def render(self, show_ckboxes):
        if self.ckboxes and show_ckboxes:
            widgets = []

            for ckbox in self.ckboxes.values():
                widgets.append(ckbox)

            self.grid.children = widgets
            self.label.show()
        else:
            self.grid.children = []
            self.label.hide()

    def on_checkbox(self, term, change):
        """Higher order terms depend on the inclusion of lower order terms)"""
        changed = change['owner']
        order_level = changed.description.count('*')

        for ckbox in self.ckboxes.values():
            ckbox.unobserve(None, 'value')  # Temporarily disable ckbox's callback

            if changed.value and ckbox.description.count('*') < order_level:
                ckbox.value = True  # Force inclusion of lower order term
                self.model.included[term] = True

            elif not changed.value and ckbox.description.count('*') > order_level:
                ckbox.value = False  # Force exclusion of higher order term
                self.model.included[term] = False

            ckbox.observe(self.on_checkbox, 'value')  # Re-enable ckbox's callback

        self.callback(ckbox_activity=True)

# Cell
class Equation:
    """Display equation"""
    EMPTY = u''
    SPACE = u'\u2004'
    EQUAL = u'='
    PLUS = u'+'
    EPSILON = u'𝜀'

    def __init__(self,left,right,max=106):  # max=112, 150
        self.left = left  # Left side output widget
        self.right = right  # Right side output widget
        self.max = max  # Maximum line length
        self.line = None  # Output buffer, string
        self.slope_index = None  # Next slope subscript, integer
        self.betaStr = None  # Beta character, string
        self.sub = str.maketrans("Bb0123456789",
                                 u"𝛽𝑏₀₁₂₃₄₅₆₇₈₉")
        self.ital = str.maketrans("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
                                  u"𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍𝑎𝑏𝑐𝑑𝑒𝑓𝑔𝘩𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝘴𝑡𝑢𝑣𝑤𝑥𝑦𝑧")

    def set_left(self, dep_var):
        """Left side uses LaTeX to enable hat stretch over variable"""
        self.left.children = (HTMLMath(value='$\hat{' + dep_var + '} = $'),)

    def start_right(self, big_beta):
        """Right side uses Unicode characters for speed"""
        self.betaStr= 'B' if big_beta else 'b' # will be translated to small beta
        self.slope_index = 0  # for building equation
        self.right.clear_output()
        self.line = self.next_slope()

    def next_slope(self):
        slope = self.betaStr+ str(self.slope_index)
        self.slope_index += 1
        return slope.translate(self.sub)

    def add(self,term):
        additional = self.SPACE + self.PLUS + self.SPACE + self.next_slope() + term.replace('*','').translate(self.ital)

        if len(self.line + additional) > self.max:
            self.flush_right()

        self.line += additional  # buffer output

    def flush_right(self):# ,force=False):
        """Flush output buffer, start new line"""

        if not self.line == self.EMPTY:

            if len(self.line) > 0 and self.line[0] == self.SPACE:
                self.line = self.line[1:]

            with self.right:
                print(self.line)
                self.line = self.EMPTY

    def end_right(self):
        self.flush_right()
        self.line = self.SPACE + self.PLUS + self.SPACE + self.EPSILON
        self.flush_right()

# Cell

#INIT_DEPVAR = 'Y'
#INIT_PRED = 'X'
#INIT_COV = 'Cov'
DEPENDENT_VARIABLE = 'Dependent variable:'
PREDICTORS = 'Predictors:'
POWER_COVARIATES = 'Power covariates?'
POWER_CUSTOM_EFFECTS = 'Power custom effects:'

STD = 'Standardized'
UNSTD = 'Unstandardized'
TOTAL_COV_OPTS = [('No covariates'       , 0),('One covariat'        , 1),
                  ('Two covariates'      , 2),('Three covariates'    , 3),
                  ('Four covariates'     , 4),('Five covariates'     , 5),
                  ('Six covariates'      , 6),('Seven covariates'    , 7),
                  ('Eight covariates'    , 8),('Nine covariates'     , 9),
                  ('Ten covariates'      ,10),('Eleven covariates'   ,11),
                  ('Twelve covariates'   ,12),('Thirteen covariates' ,13),
                  ('Fourteeen covariates',14),('Fifteen covariates'  ,15),
                  ('Sixteen covariates'  ,16),('Seventeen covariates',17),
                  ('Eighteen covariates' ,18),('Nineteen covariates' ,19),
                  ('Twenty covariates'   ,20)]
PRED_MIN = 2
COV_MIN = 0
PRED_MAX = 5
COV_MAX = 20
BIG_BETA = u'𝛽'
LITTLE_BETA = u'𝑏'
CONT = 'Continuous'
CATG = 'Categorical'

class ModerationLandingView(Box):
    """Set up and run UI for moderation landing view"""

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        labelStyle = {'description_width': '135px', 'text-align': 'left'}  # 120px

        # Create one off widgets
        self.eq_left = Box()
        # TODO Fixed eqation height (current) or allow height to vary with size of eq?
        self.eq_right = Output(layout=ipyw.Layout(width='100%', max_height='200px', height='150px',
                                                       overflow_y='auto', margin='0 0 0 5px'))
        self.standard = Dropdown(layout=ipyw.Layout(width='max-content', margin='0 0 20px 0'),
                                      options=[STD, UNSTD])

        self.dep_var = Text(description=DEPENDENT_VARIABLE, style=labelStyle, #value=self.INIT_DEPVAR,
                                 layout=ipyw.Layout(width='max-content'), continuous_update=False)  # 240px
        self.num_pred = Dropdown(description=PREDICTORS,
                                      layout=ipyw.Layout(width='max-content'),
                                      style=labelStyle,
                                      options=[i for i in range(2, PRED_MAX + 1)],
                                      value = None)
        self.total_cov = Dropdown(layout=ipyw.Layout(width='max-content'),options=TOTAL_COV_OPTS, value = None)
        self.pow_cov  = Dropdown(description=POWER_COVARIATES,
                                      layout=ipyw.Layout(width='max-content' ),#visibility = 'hidden'),
                                      style=labelStyle,
                                      options=[0], value = None)

        self.eq = Equation(self.eq_left,self.eq_right)  # Mgr for dynamic output of eqation


        # Create display widgets and storage for interactions
        self.pow_cust_eff_label = Label(layout=ipyw.Layout(margin='10px 0 0 0'))
        self.custom_inter = GridBox(layout=ipyw.Layout(grid_template_columns="repeat(4, 25%)"))

        # Create output areas for variables
        self.pred_out = Box()
        self.cov_out = Box()

        # Lay out widgets
        self.layout = ipyw.Layout(width='100%')
        eq_box = HBox([self.eq_left,self.eq_right], layout=ipyw.Layout(width='85%',
                                                                            margin='20px 0 20px 50px',
                                                                            padding='10px 10px 10px 10px',
                                                                            border='solid 1px LightGrey'))
        left_box = VBox([self.dep_var, self.num_pred, self.pred_out],
                            layout=ipyw.Layout(width='50%'))
        right_box = VBox([self.standard, self.total_cov, self.pow_cov, self.cov_out],
                             layout=ipyw.Layout(width='50%'))
        self.children = (VBox([eq_box,HBox([left_box, right_box]),
                                    self.pow_cust_eff_label, self.custom_inter],
                                    layout = ipyw.Layout(width='100%')),)