#!/usr/bin/env python3
#
# -----------------------------------------------------------
# Simulation hints management for the
# characterization tool
# -----------------------------------------------------------
# Written by Tim Edwards
# efabless, inc.
# March 21, 2017
# Version 0.1
# --------------------------------------------------------

import re
import tkinter
from tkinter import ttk


class SimHints(tkinter.Toplevel):
    """Characterization tool simulation hints management."""

    def __init__(self, parent=None, fontsize=11, *args, **kwargs):
        """See the __init__ for Tkinter.Toplevel."""
        tkinter.Toplevel.__init__(self, parent, *args, **kwargs)

        s = ttk.Style()
        s.configure(
            'normal.TButton',
            font=('Helvetica', fontsize),
            border=3,
            relief='raised',
        )
        self.protocol('WM_DELETE_WINDOW', self.close)
        self.parent = parent
        self.withdraw()
        self.title('Simulation hints management')
        self.sframe = tkinter.Frame(self)
        self.sframe.grid(column=0, row=0, sticky='news')

        self.sframe.stitle = ttk.Label(
            self.sframe, style='title.TLabel', text='Hints'
        )
        self.sframe.stitle.pack(side='top', fill='x', expand='true')
        self.sframe.sbar = ttk.Separator(self.sframe, orient='horizontal')
        self.sframe.sbar.pack(side='top', fill='x', expand='true')

        self.sframe.curparam = ttk.Label(
            self.sframe, style='title.TLabel', text='None'
        )
        self.sframe.curparam.pack(side='top', fill='x', expand='true')
        self.sframe.sbar2 = ttk.Separator(self.sframe, orient='horizontal')
        self.sframe.sbar2.pack(side='top', fill='x', expand='true')

        # Keep current parameter
        self.param = None

        # --------------------------------------------------------
        # reltol option
        # --------------------------------------------------------

        self.sframe.do_reltol = tkinter.Frame(self.sframe)
        self.sframe.do_reltol.pack(
            side='top', anchor='w', fill='x', expand='true'
        )

        self.do_reltol = tkinter.IntVar(self.sframe)
        self.do_reltol.set(0)
        self.sframe.do_reltol.enable = ttk.Checkbutton(
            self.sframe.do_reltol,
            text='Set reltol',
            variable=self.do_reltol,
            command=self.apply_reltol,
        )
        self.sframe.do_reltol.value = ttk.Entry(self.sframe.do_reltol)
        self.sframe.do_reltol.value.delete(0, 'end')
        self.sframe.do_reltol.value.insert(0, '1.0E-3')
        # Return or leave window applies hint, if enabled
        self.sframe.do_reltol.value.bind('<Return>', self.apply_reltol)
        self.sframe.do_reltol.value.bind('<Leave>', self.apply_reltol)
        self.sframe.do_reltol.enable.pack(side='left', anchor='w')
        self.sframe.do_reltol.value.pack(
            side='left', anchor='w', fill='x', expand='true'
        )

        # --------------------------------------------------------
        # rshunt option
        # --------------------------------------------------------

        self.sframe.do_rshunt = tkinter.Frame(self.sframe)
        self.sframe.do_rshunt.pack(
            side='top', anchor='w', fill='x', expand='true'
        )

        self.do_rshunt = tkinter.IntVar(self.sframe)
        self.do_rshunt.set(0)
        self.sframe.do_rshunt.enable = ttk.Checkbutton(
            self.sframe.do_rshunt,
            text='Use shunt resistance',
            variable=self.do_rshunt,
            command=self.apply_rshunt,
        )
        self.sframe.do_rshunt.value = ttk.Entry(self.sframe.do_rshunt)
        self.sframe.do_rshunt.value.delete(0, 'end')
        self.sframe.do_rshunt.value.insert(0, '1.0E20')
        # Return or leave window applies hint, if enabled
        self.sframe.do_rshunt.value.bind('<Return>', self.apply_rshunt)
        self.sframe.do_rshunt.value.bind('<Leave>', self.apply_rshunt)
        self.sframe.do_rshunt.enable.pack(side='left', anchor='w')
        self.sframe.do_rshunt.value.pack(
            side='left', anchor='w', fill='x', expand='true'
        )

        # --------------------------------------------------------
        # nodeset option
        # --------------------------------------------------------

        self.sframe.do_nodeset = tkinter.Frame(self.sframe)
        self.sframe.do_nodeset.pack(
            side='top', anchor='w', fill='x', expand='true'
        )

        self.do_nodeset = tkinter.IntVar(self.sframe)
        self.do_nodeset.set(0)
        self.sframe.do_nodeset.enable = ttk.Checkbutton(
            self.sframe.do_nodeset,
            text='Use nodeset',
            variable=self.do_nodeset,
            command=self.apply_nodeset,
        )
        self.sframe.do_nodeset.value = ttk.Entry(self.sframe.do_nodeset)
        self.sframe.do_nodeset.value.delete(0, 'end')
        # Return or leave window applies hint, if enabled
        self.sframe.do_nodeset.value.bind('<Return>', self.apply_nodeset)
        self.sframe.do_nodeset.value.bind('<Leave>', self.apply_nodeset)
        self.sframe.do_nodeset.enable.pack(side='left', anchor='w')
        self.sframe.do_nodeset.value.pack(
            side='left', anchor='w', fill='x', expand='true'
        )

        # --------------------------------------------------------
        # itl1 option
        # --------------------------------------------------------

        self.sframe.do_itl1 = tkinter.Frame(self.sframe)
        self.sframe.do_itl1.pack(
            side='top', anchor='w', fill='x', expand='true'
        )

        self.do_itl1 = tkinter.IntVar(self.sframe)
        self.do_itl1.set(0)
        self.sframe.do_itl1.enable = ttk.Checkbutton(
            self.sframe.do_itl1,
            text='Set gmin iterations',
            variable=self.do_itl1,
            command=self.apply_itl1,
        )
        self.sframe.do_itl1.value = ttk.Entry(self.sframe.do_itl1)
        self.sframe.do_itl1.value.delete(0, 'end')
        self.sframe.do_itl1.value.insert(0, '100')
        # Return or leave window applies hint, if enabled
        self.sframe.do_itl1.value.bind('<Return>', self.apply_itl1)
        self.sframe.do_itl1.value.bind('<Leave>', self.apply_itl1)
        self.sframe.do_itl1.enable.pack(side='left', anchor='w')
        self.sframe.do_itl1.value.pack(
            side='left', anchor='w', fill='x', expand='true'
        )

        # --------------------------------------------------------
        # include option
        # Disabled for now.  This needs to limit the selection of
        # files to include to a drop-down selection list.
        # --------------------------------------------------------
        self.sframe.do_include = tkinter.Frame(self.sframe)
        # self.sframe.do_include.pack(side = 'top', anchor = 'w', fill = 'x', expand = 'true')

        self.do_include = tkinter.IntVar(self.sframe)
        self.do_include.set(0)
        self.sframe.do_include.enable = ttk.Checkbutton(
            self.sframe.do_include,
            text='Include netlist',
            variable=self.do_include,
            command=self.apply_include,
        )
        self.sframe.do_include.value = ttk.Entry(self.sframe.do_include)
        self.sframe.do_include.value.delete(0, 'end')
        # Return or leave window applies hint, if enabled
        self.sframe.do_include.value.bind('<Return>', self.apply_include)
        self.sframe.do_include.value.bind('<Leave>', self.apply_include)
        self.sframe.do_include.enable.pack(side='left', anchor='w')
        self.sframe.do_include.value.pack(
            side='left', anchor='w', fill='x', expand='true'
        )

        # --------------------------------------------------------
        # alternative method option
        # --------------------------------------------------------

        self.sframe.do_method = tkinter.Frame(self.sframe)
        self.sframe.do_method.pack(
            side='top', anchor='w', fill='x', expand='true'
        )

        self.do_method = tkinter.IntVar(self.sframe)
        self.do_method.set(0)
        self.sframe.do_method.enable = ttk.Checkbutton(
            self.sframe.do_method,
            text='Use alternate method',
            variable=self.do_method,
            command=self.apply_method,
        )
        self.sframe.do_method.value = ttk.Entry(self.sframe.do_method)
        self.sframe.do_method.value.delete(0, 'end')
        self.sframe.do_method.value.insert(0, '0')
        # Return or leave window applies hint, if enabled
        self.sframe.do_method.value.bind('<Return>', self.apply_method)
        self.sframe.do_method.value.bind('<Leave>', self.apply_method)
        self.sframe.do_method.enable.pack(side='left', anchor='w')
        self.sframe.do_method.value.pack(
            side='left', anchor='w', fill='x', expand='true'
        )

        # --------------------------------------------------------

        self.bbar = ttk.Frame(self)
        self.bbar.grid(column=0, row=1, sticky='news')

        self.bbar.apply_button = ttk.Button(
            self.bbar,
            text='Apply',
            command=self.apply_hints,
            style='normal.TButton',
        )
        self.bbar.apply_button.grid(column=0, row=0, padx=5)

        self.bbar.close_button = ttk.Button(
            self.bbar, text='Close', command=self.close, style='normal.TButton'
        )
        self.bbar.close_button.grid(column=1, row=0, padx=5)

    def apply_reltol(self, value=''):
        # "value" is passed from binding callback but is not used
        have_reltol = self.do_reltol.get()
        if have_reltol:
            if not 'hints' in self.param:
                phints = {}
            else:
                phints = self.param['hints']
            phints['reltol'] = self.sframe.do_reltol.value.get()
            self.param['hints'] = phints
        else:
            if 'hints' in self.param:
                self.param['hints'].pop('reltol', None)

    def apply_rshunt(self, value=''):
        # "value" is passed from binding callback but is not used
        have_rshunt = self.do_rshunt.get()
        if have_rshunt:
            if not 'hints' in self.param:
                phints = {}
            else:
                phints = self.param['hints']
            phints['rshunt'] = self.sframe.do_rshunt.value.get()
            self.param['hints'] = phints
        else:
            if 'hints' in self.param:
                self.param['hints'].pop('rshunt', None)

    def apply_itl1(self, value=''):
        # "value" is passed from binding callback but is not used
        have_itl1 = self.do_itl1.get()
        if have_itl1:
            if not 'hints' in self.param:
                phints = {}
            else:
                phints = self.param['hints']
            phints['itl1'] = self.sframe.do_itl1.value.get()
            self.param['hints'] = phints
        else:
            if 'hints' in self.param:
                self.param['hints'].pop('itl1', None)

    def apply_nodeset(self, value=''):
        # "value" is passed from binding callback but is not used
        have_nodeset = self.do_nodeset.get()
        if have_nodeset:
            if not 'hints' in self.param:
                phints = {}
            else:
                phints = self.param['hints']
            phints['nodeset'] = self.sframe.do_nodeset.value.get()
            self.param['hints'] = phints
        else:
            if 'hints' in self.param:
                self.param['hints'].pop('nodeset', None)

    def apply_include(self, value=''):
        # "value" is passed from binding callback but is not used
        have_include = self.do_include.get()
        if have_include:
            if not 'hints' in self.param:
                phints = {}
            else:
                phints = self.param['hints']
            phints['include'] = self.sframe.do_include.value.get()
            self.param['hints'] = phints
        else:
            if 'hints' in self.param:
                self.param['hints'].pop('include', None)

    def apply_method(self, value=''):
        # "value" is passed from binding callback but is not used
        have_method = self.do_method.get()
        if have_method:
            if not 'hints' in self.param:
                phints = {}
            else:
                phints = self.param['hints']
            phints['method'] = self.sframe.do_method.value.get()
            self.param['hints'] = phints
        else:
            if 'hints' in self.param:
                self.param['hints'].pop('method', None)

    def apply_hints(self, value=''):
        self.apply_reltol(value)
        self.apply_rshunt(value)
        self.apply_itl1(value)
        self.apply_nodeset(value)
        self.apply_include(value)
        self.apply_method(value)

        # Update 'Simulate' button in characterization tool with a mark
        # indicating hints are present.  Also remove the hints record
        # from the parameter dictionary if it is empty.
        if 'method' in self.param:
            if 'hints' in self.param:
                if self.param['hints'] == {}:
                    self.param.pop('hints', None)
                    simtext = 'Simulate'
                else:
                    simtext = '\u2022Simulate'
            else:
                simtext = 'Simulate'
            self.simbutton.config(text=simtext)

    def grid_configure(self, padx, pady):
        pass

    def redisplay(self):
        pass

    def populate(self, param, simbutton=False):
        if 'display' in param:
            self.sframe.curparam.config(text=param['display'])
        else:
            self.sframe.curparam.config(text=param['method'])

        # Set the current parameter
        self.param = param

        # Remember the simulate button so we can mark or unmark hints
        self.simbutton = simbutton

        # Regenerate view and update for the indicated param.
        if 'hints' in param:
            phints = param['hints']
            if 'reltol' in phints:
                # (1) Reltol adjustment
                self.do_reltol.set(1)
                self.sframe.do_reltol.value.delete(0, 'end')
                self.sframe.do_reltol.value.insert(0, phints['reltol'])
            else:
                self.do_reltol.set(0)
            if 'rshunt' in phints:
                # (2) Gshunt option
                self.do_rshunt.set(1)
                self.sframe.do_rshunt.value.delete(0, 'end')
                self.sframe.do_rshunt.value.insert(0, phints['rshunt'])
            else:
                self.do_rshunt.set(0)
            if 'nodeset' in phints:
                # (3) Nodeset
                self.do_nodeset.set(1)
                self.sframe.do_nodeset.value.delete(0, 'end')
                self.sframe.do_nodeset.value.insert(0, phints['nodeset'])
            else:
                self.do_nodeset.set(0)
            if 'itl1' in phints:
                # (4) Gmin iterations (ITL1)
                self.do_itl1.set(1)
                self.sframe.do_itl1.value.delete(0, 'end')
                self.sframe.do_itl1.value.insert(0, phints['itl1'])
            else:
                self.do_itl1.set(0)
            if 'include' in phints:
                # (5) Include library (from dropdown list)
                self.do_include.set(1)
                self.sframe.do_include.value.delete(0, 'end')
                self.sframe.do_include.value.insert(0, phints['include'])
            else:
                self.do_include.set(0)
            if 'method' in phints:
                # (6) Alternative method (where indicated)
                self.do_method.set(1)
                self.sframe.do_method.value.delete(0, 'end')
                self.sframe.do_method.value.insert(0, phints['method'])
            else:
                self.do_method.set(0)
        else:
            # No hints, so set everything to unchecked
            self.do_reltol.set(0)
            self.do_rshunt.set(0)
            self.do_nodeset.set(0)
            self.do_itl1.set(0)
            self.do_include.set(0)
            self.do_method.set(0)

    def close(self):
        # pop down settings window
        self.withdraw()

    def open(self):
        # pop up settings window
        self.deiconify()
        self.lift()
