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

__all__ = ['Selection', 'Controller']

# Cell
from IPython.display import display, Javascript, HTML
import json
import traitlets
from traitlets import HasTraits, Int, Unicode, observe
import ipywidgets as ipyw
from IPython import get_ipython

# Cell
from .widgets import IntSlider, Button, Dropdown, FloatText
from .widget_containers import Tab, SelectionVBox, VBox, Box, Accordion
from .views.tabs import LandingTab, EstimatesTab, ResultsTab, SettingsTab
from .models.chisq import *
from .views.chisq import *
from .models.corr import *
from .views.corr import *
from .models.anova import *
from .views.anova import *
from .results import ResultsModel, Results
from .settings import SettingsModel, Settings
from superpower_gui import log
from .config import CONFIG

# Cell
from collections import namedtuple
Selection = namedtuple('Selection', ['label', 'LandingModel', 'Model', 'Landing', 'Estimates'])

# Cell
class Controller(traitlets.HasTraits):

    modelName = Unicode()
    corrModelName = Unicode()

    def __init__(self):

        ''' log handlers'''
        self.logger = log.getLogger(__name__)
        self.logger.info('Autosave: off')
        display(Javascript('Jupyter.notebook.set_autosave_interval(0);'))
        self.logger.info('Output area scroll: off')
        display(Javascript('IPython.OutputArea.prototype._should_scroll = function(lines){return false;}'))
        self.logger.info('Set container width to 100%')
        display(HTML("<style>.container { width:100% !important; }</style>"))
        log.loadCSS()

        ''' this wonderful piece of magic lets us catch global ipython exceptions
        that are usually handled by calling showtraceback that pretty prints the
        traceback beneath the cell in which the error occured. This is important,
        because ipywidget and traitlets have a lot of callbacks, and these can't be
        caught with a simple try/catch block.
        https://github.com/ipython/ipython/blob/5da6784b690b085bd9cde88e08ab4e2def9f65ff/IPython/core/interactiveshell.py
        and search for dummy_handler for original example'''


        if CONFIG['ERRORS'].getboolean('catch_all'):
            python_exception_types = (Exception, StopIteration, SystemExit, ArithmeticError, OverflowError,
                     FloatingPointError, ZeroDivisionError, AssertionError, AttributeError, EOFError,
                     ImportError, KeyboardInterrupt, LookupError, IndexError, KeyError, NameError,
                     UnboundLocalError, EnvironmentError, IOError, OSError, SyntaxError, IndentationError,
                     SystemError, SystemExit, TypeError, ValueError, RuntimeError, NotImplementedError)
            get_ipython().set_custom_exc(python_exception_types, self.catch_all)

        ''' create views and models '''
        self.create_global_widgets()
        self.define_box_selections()
        self.initialize_mvcs()
        self.view = self.tab_based_view()
        self.view.add_class('main_tab')
        self.view.layout.height = '100%'
        self.add_observations()

        ''' initialize '''
        self.corrDropdown.value = 'cramerv'
        self.dropdown.value = 'chisq'

    def catch_all(self, ip, etype, value, tb, tb_offset=None):
        if hasattr(etype, '__name__') and value:
            self.logger.error(etype.__name__ + ': ' + str(value))
            display(Javascript('alert("' + etype.__name__ + ': ' + str(value) + '\\n\\nIf a problem occurs, restart the app. For farther information, check the log accordion in the settings tab.")'))
        else:
            self.logger.error('An error has occurred.')
            display(Javascript('alert("An error has occured. Check the log accordion in the settings tab for more details, or restart the app.")'))
        if tb:
            self.logger.error(str(tb))

    def _ipython_display_(self):
        display(self.view)
        self.logger.info('Cell height expanded.')
        js = """
        var main_cell = $('.main_tab').parent().parent();
        var height = $('#site').height()
        main_cell.height(height - 100);
        """
        display(Javascript(js))

    def create_global_widgets(self):

        ''' landing '''
        self.corrDropdown = Dropdown(description='Coefficient: ',
            layout=ipyw.Layout(max_width='350px',description_width='100px'))
        self.corrSelection = SelectionVBox()
        self.corrBox = VBox(children=(self.corrDropdown, self.corrSelection))

        self.landingSelection = SelectionVBox()
        self.dropdownOptions = [('Correlation', 'corr')] # populated in self.initialize_view_and_models
        self.dropdown = Dropdown( options=self.dropdownOptions, description='Model Class: ',
                layout=ipyw.Layout(max_width='300px', description_width='80px'))

        self.okayButton = Button(description='Proceed', button_style='success')

        ''' estimates '''
        self.estimatesBox = Box()
        self.runButton = Button(description='Calculate', button_style='success')
        self.restartButton = Button(description='Restart', layout=ipyw.Layout(width='90px'))

        ''' results '''
        style = {'description_width': 'initial'}
        self.repopulateLanding = Button(description='Repopulate Model Tab', layout=ipyw.Layout(width='180px'))
        self.repopulateEstimates = Button(description='Repopulate Estimates Tab', layout=ipyw.Layout(width='200px'))

    def define_box_selections(self):

        self.options = {}
        self.options['chisq'] = Selection('Chi Squared', ChiSqLandingModel, ChiSqModel, ChiSqLanding, ChiSq)
        self.options['anova'] = Selection('Analysis of Variance', ANOVALandingModel, ANOVAModel, ANOVALanding, ANOVAEstimates)

        self.corrOptions = {}
        self.corrOptions['tetra'] = Selection('Tetrachoric', TetraLandingModel, TetraModel, TetraLanding, Tetra)
        self.corrOptions['cramerv'] = Selection("Cramer's V", CramerVLandingModel, CramerVModel, CramerVLanding, CramerV)
        self.corrOptions['pears'] = Selection("Pearson's Correlation Coefficient", CorrLandingModel, PearsonModel, PearsonLanding, Pearson)
        self.corrOptions['spear'] = Selection("Spearman's Rho", CorrLandingModel, SpearmansRhoModel, SpearmansRhoLanding, SpearmansRho)
        self.corrOptions['point'] = Selection("Point Biserial", CorrLandingModel, PBiModel, PBiLanding, PBi)

    def initialize_single_mvc(self, options, selection, dropdown):
        for name, option in options.items():
            label, LandingModel, Model, Landing, Estimates = option
            landingModel = LandingModel()
            self.landingModels[name] = landingModel
            self.Model[name] = Model
            dropdown.options += ((label, name), )
            selection.add_child(Landing(landingModel), name)
            self.Estimates[name] = Estimates

    def initialize_mvcs(self):

        ''' selectable boxes in landing '''
        self.Model = {}
        self.Estimates = {}
        self.landingModels = {}
        self.initialize_single_mvc(self.options, self.landingSelection, self.dropdown)

        self.corrLandingModels = {}
        self.initialize_single_mvc(self.corrOptions, self.corrSelection, self.corrDropdown)
        self.landingSelection.add_child(self.corrBox, 'corr')

        '''other mvcs'''
        self.resultsModel = ResultsModel()
        self.settingsModel = SettingsModel()

        self.resultsView = Results(self.resultsModel)

    def tab_based_view(self):
        landingTab = LandingTab(self.dropdown, self.landingSelection, self.okayButton, name='Model')
        estimatesTab = EstimatesTab(self.estimatesBox, self.runButton, name='Estimates')
        resultsTab = ResultsTab(self.restartButton, self.repopulateLanding, self.repopulateEstimates, self.resultsView, name='Results')
        settingsTab = Settings(self.settingsModel, name='Settings')
        return Tab(children=(landingTab, estimatesTab, resultsTab, settingsTab))

    def add_observations(self):
        self.okayButton.on_click(self.on_okayButton_click)
        self.runButton.on_click(self.on_runButton_click)
        self.restartButton.on_click(self.on_restartButton_click)
        self.repopulateLanding.on_click(self.on_repopulateLanding_click)
        self.repopulateEstimates.on_click(self.on_repopulateEstimates_click)

        self.dropdown.observe(self.on_dropdown_change, names='value')
        self.corrDropdown.observe(self.on_corrDropdown_change, names='value')

    def on_dropdown_change(self, change):
        if change['new'] == 'corr':
            self.modelName = self.corrDropdown.value
            self.landingSelection.selected_name = 'corr'
        else:
            self.modelName = change['new']
            self.landingSelection.selected_name = self.modelName

    def on_corrDropdown_change(self, change):
        self.corrSelection.selected_name = change['new']
        self.modelName = change['new']

    ''' on button click'''
    def on_okayButton_click(self, button):
        model = self.Model[self.modelName](**self.landingModels[self.modelName].getTraits())
        model.setEstimates()
        self.estimates = self.Estimates[self.modelName](model, name=self.modelName)
        self.estimatesBox.children = (self.estimates, )
        self.view.selected_name = 'Estimates'

    def on_runButton_click(self, button):
        self.update_model_with_settings()
        result = self.estimates.run()
        if result:
            self.resultsView.addResult(result)
            self.view.selected_name='Results'

    def on_restartButton_click(self, button):
        if self.modelName == 'corr':
            self.corrSelection[self.corrModelName].setLanding()
        else:
            self.landingSelection[self.modelName].setLanding()
        self.view.selected_name= 'Model'

    def on_repopulateLanding_click(self, button):
        self.view.selected_name = 'Model'

    def on_repopulateEstimates_click(self, button):
        self.view.selected_name = 'Estimates'

    def update_model_with_settings(self):
        if self.modelName in ('chisq','tetra', 'cramerv'):
            self.estimates.model.pVal = self.settingsModel.alpha
            self.estimates.model.tail = self.settingsModel.tail
        else:
            self.estimates.model.setTraits(**self.settingsModel.getTraits())