'''
This file contains all the functions that constitute the "frontend of the backend",
i.e. native Python plotting functions. All actual plots are generated by plotting.py --
this is purely about displaying them. manualfit() and geogui() rely on PyQt4, which
is likely to fail, so it's sequestered.

Version: 2019aug06
'''

## Imports and globals...need Qt since matplotlib doesn't support edit boxes, grr!
from optima import OptimaException, dcp, printv, sigfig, makeplots, getplotselections, gridcolors, odict, isnumber, promotetolist, loadobj, sanitizeresults, reanimateplots
from pylab import figure, close, floor, ion, ioff, isinteractive, ceil, array, show, pause
from pylab import subplot, ylabel, transpose, legend, fill_between, xlim, title
from matplotlib.widgets import CheckButtons, Button

global panel, results, origpars, tmppars, parset, fulllabellist, fullkeylist, fullsubkeylist, fulltypelist, fullvallist, plotfig, panelfig, check, checkboxes, updatebutton, clearbutton, defaultsbutton, advancedbutton, closebutton, plusbutton, minusbutton, plotargs, scrwid, scrhei, globaladvanced  # For manualfit GUI
if 1:  panel, results, origpars, tmppars, parset, fulllabellist, fullkeylist, fullsubkeylist, fulltypelist, fullvallist, plotfig, panelfig, check, checkboxes, updatebutton, clearbutton, defaultsbutton, advancedbutton, closebutton, plusbutton, minusbutton, plotargs, scrwid, scrhei, globaladvanced = [None]*25
scrwid, scrhei = 24, 12 # Specify these here...if too large, should shrink anyway


def importpyqt():
    ''' Try to import pyqt, either PyQt4 or PyQt5, but allow it to fail '''
    try:    
        from PyQt4 import QtGui as pyqt
    except:
        try:    from PyQt5 import QtWidgets as pyqt
        except: pyqt = Exception('QtGui could not be imported')
    return  pyqt
    
pyqt = importpyqt()


##############################################################################
### USER-VISIBLE FUNCTIONS
##############################################################################

def plotresults(results, toplot=None, fig=None, figargs=None, **kwargs):
    ''' 
    Does the hard work for updateplots() for pygui()
    Keyword arguments if supplied are passed on to figure().
    
    Usage:
        results = P.runsim('default')
        plotresults(results)
        
    Version: 2016jan25
    '''
    
    if figargs is None: figargs = dict()
    if fig is None: fig = makenewfigure(**figargs)
    
    # Do plotting
    wasinteractive = isinteractive() # You might think you can get rid of this...you can't!
    if wasinteractive: ioff()
    width,height = fig.get_size_inches()
    
    # Actually create plots
    if 'figsize' in kwargs: kwargs.pop('figsize', None)
    plots = makeplots(results, toplot=toplot, die=True, figsize=(width, height), fig=fig, **kwargs)
    naxes = len(plots[0].axes) # If done interactively, they're all in the first plot
    
    # Calculate the rows and columns
    aspectratio = 1.5 # The target aspect ratio for plots, for choosing how many rows and columns to use
    nrows = 1
    ncols = 1
    while nrows*ncols < naxes:
        if width/ncols/aspectratio > height/nrows: ncols += 1 # Height is more squashed: add a column
        else:                                      nrows += 1 # Width is more squashed: add a row
    
    # Adjust margins
    fig.subplots_adjust(left=0.07, bottom=0.05, right=0.85, top=0.95, wspace=0.9, hspace=0.7) # NB, 1.0 seems meaningless for wspace and hspace...
    for a,ax in enumerate(plots[-1].axes):
        ax.change_geometry(nrows, ncols, a+1)

    # Handle interactivity like a boss
    if wasinteractive: ion()
    show()
    return None


def pygui(tmpresults, toplot=None, advanced=False, verbose=2, figargs=None, **kwargs):
    '''
    PYGUI
    
    Make a Python GUI for plotting results. Opens up a control window and a plotting window,
    and when "Update" is clicked, will clear the contents of the plotting window and replot.
    
    Usage:
        pygui(results, [toplot])
    
    where results is the output of e.g. runsim() and toplot is an opti
onal list of form e.g.
        toplot = ['prev-tot', 'inci-pop']
    
    (see epiformatslist in plotting.py)
    
    Warning: the plots won't resize automatically if the figure is resized, but if you click
    "Update", then they will.    
    
    Version: 1.3 (2017feb07)
    '''
    
    global check, checkboxes, updatebutton, clearbutton, defaultsbutton, advanced
button, closebutton, plusbutton, minusbutton, panelfig, results, plotargs, globaladvanced
    plotargs = kwargs # Reset global to match function input
    results = sanitizeresults(tmpresults)
    globaladvanced = advanced
    
    ## Define options for selection
    plotselections = getplotselections(results, advanced=globaladvanced)
    checkboxes = plotselections['keys']
    checkboxnames = plotselections['names']
    isselected = []
    toplot = promotetolist(toplot) # Ensure it's a list
    if not toplot or toplot[0] is None or toplot[0]=='default': 
        if len(toplot): toplot.pop(0) # Remove the first element
        defaultboxes = [checkboxes[i] for i,tf in enumerate(plotselections['defaults']) if tf] # Back-convert defaults from true/false list to list of keys
        toplot.extend(defaultboxes)
    if len(toplot):
        tmptoplot = dcp(toplot) # Make a copy to compare arguments
        for key in checkboxes:
            if key in toplot:
                isselected.append(True)
                tmptoplot.remove(key)
            else:
                isselected.append(False)
        if len(tmptoplot)!=0:
            errormsg = 'Not all keys were recognized; mismatched ones were:\n'
            errormsg += '%s\n' % tmptoplot
            errormsg += 'Available keys are:\n'
            errormsg += '%s' % checkboxes
            if not globaladvanced: errormsg += '\nSet advanced=True for more options'
            printv(errormsg, 1, verbose=verbose)
    
    ## Set up control panel
    if advanced: 
        figwidth = 14
        advwid = 0.6 # Adjust button width
    else:
        figwidth = 7
        advwid = 1.0
    figheight = 12
    panelfig = figure(num='Optima control panel', figsize=(figwidth,figheight), facecolor=(0.95, 0.95, 0.95)) # Open control panel
    xinit = 0.10*advwid
    if advanced: cbapos = [xinit*advwid,  0.07, 0.9, 1.8] # cba="check box axes position": extra tall, for moving later
    else:        cbapos = [xinit,         0.07, 0.8, 0.9]
    ypos = 0.02 # y-position of buttons
    bwid = 0.14*advwid # x-width of buttons
    bhei = 0.03 # y-height of buttons
    sep  = 0.165 # Separation between buttons
    pmwid = 0.03*advwid # Width of plus/minus buttons
    checkboxaxes = panelfig.add_axes(cbapos) # Create checkbox locations
    updateaxes   = panelfig.add_axes([xinit+0*sep, ypos, bwid, bhei]) # Create update button location
    clearaxes    = panelfig.add_axes([xinit+1*sep, ypos, bwid, bhei]) # Create clear button location
    defaultsaxes = panelfig.add_axes([xinit+2*sep, ypos, bwid, bhei]) # Create defaults button location
    advancedaxes = panelfig.add_axes([xinit+3*sep, ypos, bwid, bhei]) # Create defaults button location
    closeaxes    = panelfig.add_axes([xinit+4*sep, ypos, bwid, bhei]) # Create close button location
    plusaxes     = panelfig.add_axes([xinit+5*sep, ypos+0.015, pmwid, 0.02]) # Create plus button location
    minusaxes    = panelfig.add_axes([xinit+5*sep, ypos-0.005, pmwid, 0.02]) # Create plus button location
    check = CheckButtons(checkboxaxes, checkboxnames, isselected) # Actually create checkboxes
    
    # Reformat the checkboxes
    stastr = ' - stacked'
    perstr = ' - population'
    nboxes = len(check.rectangles)
    for b in range(nboxes):
        label = check.labels[b]
        labeltext = label.get_text()
        labelpos = label.get_position()
        label.set_position((labelpos[0]*0.3,labelpos[1])) # Not sure why by default the check boxes are so far away
        if labeltext.endswith(perstr):    label.set_text('Per population') # Clear label
        elif labeltext.endswith(stastr):  label.set_text('Stacked') # Clear label
        else:                             label.set_weight('b