#!/usr/bin/python
''' handle display related work upon Tkinter '''

import Tkinter
import tkMessageBox
import sys
import ri_cmd
import ri_widget
import os.path

var_dict={}
# language should be all lower case letters
language='english'

def init(base_w):
    ''' base_w - base widget instance '''
    global root_window
    root_window = Tkinter.Tk()

    # grid management for root_window
    root_window.columnconfigure(0, weight=1)
    root_window.rowconfigure(0, weight=1)

    root_window.geometry("%sx%s+0+0" %(root_window.winfo_screenwidth(),root_window.winfo_screenheight()))
    root_window.title('Linx')

    # bind WM_DELETE_WINDOW
    root_window.protocol("WM_DELETE_WINDOW", root_window.quit)
    global base_widget
    base_widget = create_widget_sub(base_w, root_window)

def quit():
    ''' exit root window '''
    root_window.quit()

def set_step_info(s):
    ''' set some information in base widget '''
    # set step name which will be shown on base widget column 1 row 0
    title = translate_text(s)
    var_dict['main.step_name'].set(title)

def set_help_info(s):
    ''' set help information in base widget'''
    help = translate_text(s)
    w = ri_widget.Widget.dict['main.help'].tk_widget
    w.configure(state='normal')
    w.delete(1.0,Tkinter.END)
    w.insert(1.0,help)
    w.configure(state='disable')

def create_widget(w):
    ''' w - widget instance '''

    create_widget_sub(w, base_widget)

    # set step name
    if 'name' in dir(w):
        set_step_info(w.name)
        set_help_info('#'+w.name+'.help')

class MyImage:
    ''' MyImage - a dummy class to hold Image variable '''
    count = 0

def translate_text(txt):
    ''' multi-language support, translate t if needed '''
    key = txt.lower()
    if key in ri_widget.Text.dict.keys() and (txt[0] == '#' or language != 'english'):
        return getattr(ri_widget.Text.dict[key], language)
    else:
        return txt

def modify_attributes(attr_dict):
    ''' modify values in attr_dict to suit Tk usage '''
    for a in attr_dict.keys():
        if a == 'image':
            # I think there is a bug in Tkinter on Image processing
            # I have to bypass it in the following way:
            image_var = 'a' + str(MyImage.count)
            drt = os.path.join(os.path.dirname(__file__), '../data')
            setattr(MyImage, image_var, Tkinter.PhotoImage(file=os.path.join(drt, attr_dict[a])))
            MyImage.count += 1
            attr_dict[a] = getattr(MyImage, image_var)
        elif a == 'command':
            attr_dict[a] = getattr(sys.modules["ri_cmd"], attr_dict[a])
        elif a == "textvariable" or a == "listvariable" or a == 'variable':
            attr_dict[a] = var_dict[attr_dict[a]]
        elif a == "text":
            attr_dict[a] = translate_text(attr_dict[a])

def create_widget_sub(w, p_win):
    ''' 
    w - widget instance
    p_win - Tk parent window '''

    # name type and value
    for v_n, v_t, v_v in w.variables:
        if not v_n in var_dict.keys():
            # if not yet in dict, create it
            var_dict[v_n] = getattr(Tkinter, v_t)(value=v_v)

    # change attr, if needed to suit tk
    tk_attr = dict(w.attr)
    modify_attributes(tk_attr)

    tk_func = getattr(Tkinter, w.tp)
    w_win = tk_func(p_win, tk_attr)

    # save tk widget instance into w (widget instance)
    setattr(w, 'tk_widget', w_win)

    # display sub widgets
    for sub_w in w.widgets:
        create_widget_sub(sub_w, w_win)

    # process action init
    if 'action' in dir(w) and 'init' in w.action.dict:
        getattr(sys.modules['ri_cmd'], w.action.dict['init'])()

    # handle scroll bar for sub_widgets
    if 'action' in dir(w):
        # ing for scrolling, ed for scrolled
        for ing, ed in w.action.scrolls:
            ing_win = w.dict[ing].tk_widget
            ed_win  = w.dict[ed].tk_widget
            if ing_win['orient'] == 'vertical':
                ed_cmd_name = 'yscrollcommand'
                ing_cmd = getattr(ed_win, 'yview')
            else:
                ed_cmd_name = 'xscrollcommand'
                ing_cmd = getattr(ed_win, 'xview')

            ing_win.configure(command=ing_cmd)
            ed_win[ed_cmd_name]=ing_win.set
            
    # grid management
    if 'grid_management' in dir(w):
        for cf in w.grid_management.cf_list:
            cf_func = getattr(w_win, cf[0])
            cf_func( cf[1], weight=cf[2])

    # grid location
    if 'grid_location' in dir(w):
        w_win.grid(w.grid_location.dict)

    # handle bindings
    for bd in w.bindings:
        w_win.bind(bd[0], getattr(ri_cmd, bd[1]))


    return w_win

# When creating widget, I have to create each sub widget.
# while destroying a widget, only destroy one. Tk functions will
# destroy all descendant.
# so init is a little different from quit.
def process_action_quit(w):
    ''' process action quit '''
    if 'action' in dir(w) and 'quit' in w.action.dict:
        getattr(sys.modules['ri_cmd'], w.action.dict['quit'])()

    if 'widgets' in dir(w):
        for sub_w in w.widgets:
            process_action_quit(sub_w)
    
def destroy_widget(w):
    ''' w - Widget instance '''
    process_action_quit(w)
    w.tk_widget.destroy()

def create_message_box(w):
    ''' display MessageBox
 w - MessageBox instance'''
    disp = getattr(tkMessageBox, w.tp)
    code = disp(translate_text(w.title), translate_text(w.message))
    return code

def create_top_window(w):
    ''' display TopWindow
w - TopWindow instance '''
    tk_attr = dict(w.attr)
    modify_attributes(tk_attr)

    w_win = Tkinter.Toplevel(root_window, tk_attr)
    
    for sub_w in w.widgets:
        create_widget_sub(sub_w, w_win)

    if 'grid_management' in dir(w):
        for cf in w.grid_management.cf_list:
            cf_func = getattr(w_win, cf[0])
            cf_func(cf[1], weight=cf[2])

    # save tk widget instance into w (TopWindow instance)
    setattr(w, 'tk_widget', w_win)

    # 'bind' it to root window
    w_win.transient(root_window)
    # grab all events into it
    while True:
        try:
            w_win.grab_set()
        except Tkinter.TclError:
            pass
        else:
            break

    w_win.wait_window()

    return w_win

def destroy_top_window(w):
    ''' w - Toplevel instance '''
    w.tk_widget.destroy()

class SoftwarePackageWindow():
    ''' Toplevel window for a group of software packages 
 class member
 ---------------------------
 dict - class static member

 instance member
 ---------------------------
 group     - data layer group instance
 win       - Tk widget
 selection - StringVar variable for 'select_all'
 opt_vars  - a list to hold variables containing status for optional packages
 opt_chks  - a list to hold Checkbuttons for optional packages
   '''
    dict = {}
    def __init__(self, g):
        self.group = g
        self.opt_vars = []
        self.opt_chks = []
        SoftwarePackageWindow.dict[g.name] = self

    def select_all(self):
        ''' callback function for check button select_all '''
        if self.selection.get() == 'all':
            for ck in self.opt_chks:
                ck.configure(state='disable')
        else:
            for ck in self.opt_chks:
                ck.configure(state='normal')
            

    def ok(self):
        ''' callback function for button OK '''
        self.group.selection = self.selection.get()
        for i in range(len(self.group.optional)):
            # install field, yes or no
            self.group.optional[i][1] = self.opt_vars[i].get()
        # clear variables and check buttons
        self.opt_vars = []
        self.opt_chks = []
        self.win.destroy()

    def cancel(self):
        ''' callback function for button cancel '''
        # clear variables and check buttons
        self.opt_vars = []
        self.opt_chks = []
        self.win.destroy()

    def show(self):
        win = Tkinter.Toplevel(root_window)
        self.win = win
        win.geometry("%sx%s+%s+%s" %(int(root_window.winfo_screenwidth()*0.8),\
                                     int(root_window.winfo_screenheight()*0.8),\
                                     int(root_window.winfo_screenwidth()*0.1),\
                                     int(root_window.winfo_screenheight()*0.1)))
        win.columnconfigure(0, weight=1)
        win.rowconfigure(1, weight=1)
        win.rowconfigure(4, weight=1)
        
        text_mdt = translate_text('Mandatory')
        Tkinter.Label(win, text=text_mdt).grid(column=0, row=0)
        cnv1 = Tkinter.Canvas(win)
        hs1  = Tkinter.Scrollbar(win, orient='horizontal')
        hs1.configure(command=cnv1.xview)
        vs1  = Tkinter.Scrollbar(win, orient='vertical')
        vs1.configure(command=cnv1.yview)
        cnv1.grid(column=0, row=1, sticky='NWES')
        vs1.grid(column=1, row=1, sticky='NSW')
        hs1.grid(column=0, row=2, sticky='NWE')
        cnv1.configure(yscrollcommand=vs1.set, xscrollcommand=hs1.set)
        fr1 = Tkinter.Frame(cnv1)
        cnv1.create_window((0,0), window=fr1, anchor='nw')
        #column set to 5
        fr1.columnconfigure(0, weight=1)
        fr1.columnconfigure(1, weight=1)
        fr1.columnconfigure(2, weight=1)
        fr1.columnconfigure(3, weight=1)
        fr1.columnconfigure(4, weight=1)

        for i in range(len(self.group.mandatory)):
            Tkinter.Label(fr1, text=self.group.mandatory[i]).grid(column=i%5, row=i/5, sticky='NWES')
        
        text_opt = translate_text('Optional')
        Tkinter.Label(win, text=text_opt).grid(column=0, row=3)
        self.selection = Tkinter.StringVar(value=self.group.selection)
        text_sal = translate_text('Select all')
        chk_sa = Tkinter.Checkbutton(win, text=text_sal, command=self.select_all, variable=self.selection, onvalue='all', offvalue='manual')
        chk_sa.grid(column=1, row=3)
        if self.group.selection == 'all':
            chk_sa.select()
        else:
            chk_sa.deselect()

        cnv2 = Tkinter.Canvas(win)
        hs2  = Tkinter.Scrollbar(win, orient='horizontal')
        hs2.configure(command=cnv2.xview)
        vs2  = Tkinter.Scrollbar(win, orient='vertical')
        vs2.configure(command=cnv2.yview)
        cnv2.grid(column=0, row=4, sticky='NWES')
        vs2.grid(column=1, row=4, sticky='NSW')
        hs2.grid(column=0, row=5, sticky='NWE')
        cnv2.configure(yscrollcommand=vs2.set, xscrollcommand=hs2.set)
        fr2 = Tkinter.Frame(cnv2)
        cnv2.create_window((0,0), window=fr2, anchor='nw')
        #column set to 5
        fr2.columnconfigure(0, weight=1)
        fr2.columnconfigure(1, weight=1)
        fr2.columnconfigure(2, weight=1)
        fr2.columnconfigure(3, weight=1)
        fr2.columnconfigure(4, weight=1)

        for i in range(len(self.group.optional)):
            name, y_n = self.group.optional[i]
            v = Tkinter.StringVar(value='no')
            self.opt_vars.append(v)
            chk = Tkinter.Checkbutton(fr2, text=name, onvalue='yes', offvalue='no', variable=self.opt_vars[i])
            self.opt_chks.append(chk)
            chk.grid(column=i%5, row=i/5, sticky='NWS')
            if y_n == 'yes':
                chk.select()
            else:
                chk.deselect()

            if self.group.selection == 'all':
                chk.configure(state='disable')
            
        Tkinter.Button(win, text='OK', command=self.ok).grid(column=0, row=6, pady=2)
        Tkinter.Button(win, text='Cancel', command=self.cancel).grid(column=1, row=6, pady=2)

        # bind WM_DELETE_WINDOW
        win.protocol("WM_DELETE_WINDOW", self.cancel)

        win.after_idle(lambda : cnv1.configure(scrollregion=(0,0, fr1.winfo_width(), fr1.winfo_height())))
        win.after_idle(lambda : cnv2.configure(scrollregion=(0,0, fr2.winfo_width(), fr2.winfo_height())))

        win.transient(root_window)
        # grab all events into it
        while True:
            try:
                win.grab_set()
            except Tkinter.TclError:
                pass
            else:
                break

        win.wait_window()
