import wx
from wx.lib.scrolledpanel import ScrolledPanel

from gooey.gui.util import wx_util
from gooey.util.functional import getin, flatmap, merge, compact, indexunique
from gooey.gui.components.widgets.radio_group import RadioGroup


class ConfigPage(ScrolledPanel):
    def __init__(self, parent, rawWidgets, *args, **kwargs):
        super(ConfigPage, self).__init__(parent, *args, **kwargs)
        self.SetupScrolling(scroll_x=False, scrollToTop=False)
        self.rawWidgets = rawWidgets
        self.reifiedWidgets = []
        self.layoutComponent()
        self.widgetsMap = indexunique(lambda x: x._id, self.reifiedWidgets)
        ## TODO: need to rethink what uniquely identifies an argument.
        ## Out-of-band IDs, while simple, make talking to the client program difficult
        ## unless they're agreed upon before hand. Commands, as used here, have the problem
        ## of (a) not being nearly granular enough (for instance,  `-v` could represent totally different
        ## things given context/parser position), and (b) cannot identify positional args.


    def firstCommandIfPresent(self, widget):
        commands = widget._meta['commands']
        return commands[0] if commands else ''

    def getPositionalArgs(self):
        return [widget.getValue()['cmd'] for widget in self.reifiedWidgets
                if widget.info['cli_type'] == 'positional']

    def getOptionalArgs(self):
        return [widget.getValue()['cmd'] for widget in self.reifiedWidgets
                if widget.info['cli_type'] != 'positional']


    def isValid(self):
        states = [widget.getValue() for widget in self.reifiedWidgets]
        return not any(compact([state['error'] for state in states]))


    def seedUI(self, seeds):
        radioWidgets = self.indexInternalRadioGroupWidgets()
        for id, values in seeds.items():
            if id in self.widgetsMap:
                self.widgetsMap[id].setOptions(values)
            if id in radioWidgets:
                radioWidgets[id].setOptions(values)

    def indexInternalRadioGroupWidgets(self):
        groups = filter(lambda x: x.info['type'] == 'RadioGroup', self.reifiedWidgets)
        widgets = flatmap(lambda group: group.widgets, groups)
        return indexunique(lambda x: x._id, widgets)


    def displayErrors(self):
        states = [widget.getValue() for widget in self.reifiedWidgets]
        errors = [state for state in states if state['error']]
        for error in errors:
            widget = self.widgetsMap[error['id']]
            widget.setErrorString(error['error'])
            widget.showErrorString(True)
            while widget.GetParent():
                widget.Layout()
                widget = widget.GetParent()

    def resetErrors(self):
        for widget in self.reifiedWidgets:
            widget.setErrorString('')
            widget.showErrorString(False)

    def hideErrors(self):
        for widget in self.reifiedWidgets:
            widget.hideErrorString()


    def layoutComponent(self):
        sizer = wx.BoxSizer(wx.VERTICAL)
        for item in self.rawWidgets['contents']:
            self.makeGroup(self, sizer, item, 0, wx.EXPAND)
        self.SetSizer(sizer)


    def makeGroup(self, parent, thissizer, group, *args):
        '''
        Messily builds the (potentially) nested and grouped layout

        Note! Mutates `self.reifiedWidgets` in place with the widgets as they're
        instantiated! I cannot figure out how to split out the creation of the
        widgets from their styling without WxPython violently exploding

        TODO: sort out the WX quirks and clean this up.
        '''

        # determine the type of border , if any, the main sizer will use
        if getin(group, ['options', 'show_border'], False):
            boxDetails = wx.StaticBox(parent, -1, group['name'] or '')
            boxSizer = wx.StaticBoxSizer(boxDetails, wx.VERTICAL)
        else:
            boxSizer = wx.BoxSizer(wx.VERTICAL)
            boxSizer.AddSpacer(10)
            if group['name']:
                boxSizer.Add(wx_util.h1(parent, group['name'] or ''), 0, wx.TOP | wx.BOTTOM | wx.LEFT, 8)

        group_description = getin(group, ['description'])
        if group_description:
            description = wx.StaticText(parent, label=group_description)
            boxSizer.Add(description, 0,  wx.EXPAND | wx.LEFT, 10)

        # apply an underline when a grouping border is not specified
        if not getin(group, ['options', 'show_border'], False) and group['name']:
            boxSizer.Add(wx_util.horizontal_rule(parent), 0, wx.EXPAND | wx.LEFT, 10)

        ui_groups = self.chunkWidgets(group)

        for uigroup in ui_groups:
            sizer = wx.BoxSizer(wx.HORIZONTAL)
            for item in uigroup:
                widget = self.reifyWidget(parent, item)
                # !Mutate the reifiedWidgets instance variable in place
                self.reifiedWidgets.append(widget)
                sizer.Add(widget, 1, wx.ALL, 5)
            boxSizer.Add(sizer, 0, wx.ALL | wx.EXPAND, 5)

        # apply the same layout rules recursively for subgroups
        hs = wx.BoxSizer(wx.HORIZONTAL)
        for e, subgroup in enumerate(group['groups']):
            self.makeGroup(parent, hs, subgroup, 1, wx.ALL | wx.EXPAND, 5)
            if e % getin(group, ['options', 'columns'], 2) \
                    or e == len(group['groups']):
                boxSizer.Add(hs, *args)
                hs = wx.BoxSizer(wx.HORIZONTAL)

        thissizer.Add(boxSizer, *args)


    def chunkWidgets(self, group):
        ''' chunk the widgets up into groups based on their sizing hints '''
        ui_groups = []
        subgroup = []
        for index, item in enumerate(group['items']):
            if getin(item, ['options', 'full_width'], False):
                ui_groups.append(subgroup)
                ui_groups.append([item])
                subgroup = []
            else:
                subgroup.append(item)
            if len(subgroup) == getin(group, ['options', 'columns'], 2) \
                    or item == group['items'][-1]:
                ui_groups.append(subgroup)
                subgroup = []
        return ui_groups


    def reifyWidget(self, parent, item):
        ''' Convert a JSON description of a widget into a WxObject '''
        from gooey.gui.components import widgets
        widgetClass = getattr(widgets, item['type'])
        return widgetClass(parent, item)



class TabbedConfigPage(ConfigPage):
    """
    Splits top-level groups across tabs
    """


    def layoutComponent(self):
        # self.rawWidgets['contents'] = self.rawWidgets['contents'][1:2]
        self.notebook = wx.Notebook(self, style=wx.BK_DEFAULT)

        panels = [wx.Panel(self.notebook) for _ in self.rawWidgets['contents']]
        sizers = [wx.BoxSizer(wx.VERTICAL) for _ in panels]

        for group, panel, sizer in zip(self.rawWidgets['contents'], panels, sizers):
            self.makeGroup(panel, sizer, group, 0, wx.EXPAND)
            panel.SetSizer(sizer)
            panel.Layout()
            self.notebook.AddPage(panel, group['name'])
            self.notebook.Layout()


        _sizer = wx.BoxSizer(wx.VERTICAL)
        _sizer.Add(self.notebook, 1, wx.EXPAND)
        self.SetSizer(_sizer)
        self.Layout()