'''GUI for PyMOLProbity plugin.'''

from __future__ import absolute_import

import logging
import Pmw
import sys

if sys.version_info[0] < 3:
    import Tkinter as tk
    import tkMessageBox
else:
    import tkinter as tk
    import tkinter.messagebox as tkMessageBox

from pymol import cmd

from . import main

logger = logging.getLogger(__name__)


def popup_on_exception(func):
    '''Decorator for GUI error handling'''
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            tkMessageBox.showerror('Error', str(e))

    return wrapper


class PyMOLProbity:
    '''The main PyMOLProbity plugin dialog.'''
    def __init__(self, app):

        def get_object_list():
            all_obj = cmd.get_names_of_type('object:molecule')
            # filter to exclude plugin-generated objects
            ret = [o for o in all_obj if not o.startswith('mp_')]
            logger.debug('available objects are: {}'.format(ret))
            return ret

        def update_combo():
            input_combo.setlist(get_object_list())
            input_combo.selectitem(mpobj.get())

        def refresh_page(page):
            try:
                logger.debug('Refreshing page {}.'.format(page))
                # Refresh the combo box in case something has changed
                update_combo(input_combo)
                nb.setnaturalsize()
            except Exception as e:
                logger.debug('Failed to refresh page {}.'.format(page))

        def set_mpobj(val):
            mpobj.set(val)
            cmd.zoom(val, 3)
            logger.debug('mpobj set to {}'.format(val))
            # Get active flipkin
            flipkin = self.flipkin_radio.getvalue()
            o = main.get_object(val)
            if o.kin[flipkin] is not None:
                logger.debug('populating flips group...')
                populate_flips_grp(flipkin)
            else:

                msg = "No {} for object {}...clearing flips.".format(
                        flipkin, val)
                logger.debug(msg)
                clear_child_widgets(flips_grp.interior())

        @popup_on_exception
        def reduce_mpobj():
            main.reduce_object(mpobj.get())

        @popup_on_exception
        def flipkin_mpobj():
            main.flipkin_object(mpobj.get())
            frame = flipkin_ctl.interior()
            self.flipkin_radio = gen_flipkin_radioselect(frame)
            self.flipkin_group_chk = gen_flipkin_group_checkbuttons(frame)
            self.flipkin_radio.invoke('flipkinNQ')  # always start with NQ
            animate_mpobj()

        def select_flipkin(flipkin):
            o = main.get_object(mpobj.get())
            o.solo_kin(flipkin)

            # Set the proper PDB based on the active group
            try:
                val = self.flipkin_group_chk.getvalue()
            except:
                # first time running, default to NQ
                val = 'flipkinNQ'
            logger.debug('self.flipkin_group_chk = {} during select_flipkin'.format(val))
            if 'flip' in val:
                o.solo_pdb(flipkin)
                if 'reduce' in val:
                    o.enable_pdb('reduce')
            else:
                o.solo_pdb('reduce')

            # Set up the view controls
            frame = flipkin_ctl.interior()
            self.flipkin_subgroups_chk = gen_flipkin_subgroup_checkbuttons(frame)
            self.flipkin_masters_chk = gen_flipkin_master_checkbuttons(frame)
            # self.flipkin_pointmasters_chk = gen_flipkin_pointmaster_checkbuttons(frame)

            # Show and populate the flips group for this flipkin
            populate_flips_grp(flipkin)
            nb.setnaturalsize()

        def enable_flipkin_group(group, enable=True):
            o = main.get_object(mpobj.get())

            # use full names (e.g. 'flipkinNQ') for PDBs
            if group.startswith('flip'):
                pdb = self.flipkin_radio.getvalue()
                suffix = pdb.replace('flipkin','')  # yields 'NQ' or 'H'
                assert suffix in ['NQ', 'H']
                kin_grp = '{}{}'.format(group, suffix)
            else:
                # unless something is wrong with the flipkin, group is 'reduce'
                pdb = group
                kin_grp = group

            # enable (or disable if enable=False)
            if enable:
                logger.debug('enabling {} and {}'.format(kin_grp, pdb))
                o.enable_flipkin_group(kin_grp)
                o.enable_pdb(pdb)
            else:
                logger.debug('disabling {} and {}'.format(kin_grp, pdb))
                o.disable_flipkin_group(kin_grp)
                o.disable_pdb(pdb)

        def animate_mpobj():
            o = main.get_object(mpobj.get())
            # Ensure only one is checked
            val = self.flipkin_group_chk.getvalue()
            if len(val) == 1:
                # Toggle both buttons
                self.flipkin_group_chk.invoke('reduce')
                self.flipkin_group_chk.invoke('flip')
            elif len(val) in [0, 2]:
                # If they're both checked or unchecked, only toggle one
                self.flipkin_group_chk.invoke('reduce')

        def toggle_subgroup(subgroup, enable):
            SEL = {
                    '1->2': 'mp_*.1to2.*',
                    '2->1': 'mp_*.2to1.*',
                    }
            if enable:
                cmd.enable(SEL[subgroup])
            else:
                cmd.disable(SEL[subgroup])

        def toggle_master(master, enable):
            SEL = {
                    'vdw contact': 'mp_*vdw_contact',
                    'small overlap': 'mp_*small_overlap',
                    'bad overlap': 'mp_*bad_overlap',
                    'H-bonds': 'mp_*H_bonds',
                    }
            if enable:
                cmd.enable(SEL[master])
                # Keep buttons on Flipkin and Probe tabs in sync.
                try:
                    self.flipkin_masters_chk.button(master).select()
                    self.probe_masters_chk.button(master).select()
                except:
                    # don't choke if the button doesn't exist
                    pass

            else:
                cmd.disable(SEL[master])
                try:
                    self.flipkin_masters_chk.button(master).deselect()
                    self.probe_masters_chk.button(master).deselect()
                except:
                    pass

        def toggle_pointmaster(pointmaster, enable):
            pass

        def zoom_and_refresh(sel):
            cmd.orient(sel)
            cmd.zoom(sel, 3)
            cmd.refresh()

        def clear_child_widgets(parent):
            # TODO may be better to keep the widgets and use grid_forget()
            for widget in parent.winfo_children():
                widget.destroy()
            logger.debug('cleared contents of {}'.format(parent))

        def populate_flips_grp(flipkin):
            parent = flips_grp.interior()
            # Clear any existing child widgets
            clear_child_widgets(parent)
            # Recreate the scrolling frame to house the flip rows
            frame = gen_scrolling_frame(parent)

            # Column headers
            gen_flips_list_header(frame)

            # Repopulate with flips
            o = main.get_object(mpobj.get())
            if not o.views[flipkin]:
                kin = o.kin[flipkin]
                o.views[flipkin] = kin.viewids()
            logger.debug('begin popu
lating flip rows')
            for i, v in enumerate(o.views[flipkin]):
                gen_flips_list_row(frame, o, flipkin, i+1, v)
            logger.debug('finished populating flip rows')
            nb.setnaturalsize()

        @popup_on_exception
        def save_flip_selections():
            '''Save the user-selected flips to a new PDB object.'''
            o = main.get_object(mpobj.get())
            flipki
n = self.flipkin_radio.getvalue()
            reduce_obj = o.pdb['reduce']
            flipkin_obj = o.pdb[flipkin]
            userflips_obj = o.pdb['userflips']
            # Create a new user