"""A Ttk Notebook with close buttons.

Based on an example by patthoyts, http://paste.tclers.tk/896
"""
import datetime
import os
import Tkinter as tk
import ttk
import os.path
from tkFileDialog import askopenfilename
import tkSimpleDialog
import configuration
import pcwg_tool
import Analysis
from gui.utils import *
import gui.dataset_tab
import gui.analysis_tab

columnSeparator = "|"
filterSeparator = "#"
datePickerFormat = "%Y-%m-%d %H:%M"# "%d-%m-%Y %H:%M"
datePickerFormatDisplay = "[dd-mm-yyyy hh:mm]"

version = "0.5.13"
ExceptionType = Exception
#ExceptionType = None #comment this line before release

pcwg_inner_ranges = {'A': {'LTI': 0.08, 'UTI': 0.12, 'LSh': 0.05, 'USh': 0.25},
                     'B': {'LTI': 0.05, 'UTI': 0.09, 'LSh': 0.05, 'USh': 0.25},
                     'C': {'LTI': 0.1, 'UTI': 0.14, 'LSh': 0.1, 'USh': 0.3}}

class OpenRecent:

    def __init__(self, fileOpener, path):
        self.path = path
        self.fileOpener = fileOpener
        
    def __call__(self):
        self.fileOpener.loadFile(self.path)

class ConfirmClose(tkSimpleDialog.Dialog):

    def __init__(self, parent, name):

        self.name = name

        self.close = False
        self.save = False

        imgdir = os.path.join(os.path.dirname(__file__), 'img')

        self.img_logo = tk.PhotoImage("img_logo", file=os.path.join(imgdir, 'logo.gif'))

        tkSimpleDialog.Dialog.__init__(self, parent, "Confirm File Close")

    def body(self, master):

        tk.Label(master, image = self.img_logo).grid(column=0, row=0)
        tk.Label(master, text="Do you want to save the changes you made to {0}?".format(self.name)).grid(column=1, row=0)

    def buttonbox(self):
        
        try:
            self.attributes("-toolwindow",1) #only works on windows
        except:
            #self.overrideredirect(1) #removes whole frame
            self.resizable(0,0) #stops maximising and resizing but can still be minimised

        box = tk.Frame(self)

        w = tk.Button(box, text="Don't Save", width=10, command=self.close_dont_save)
        w.pack(side=tk.LEFT, padx=5, pady=5)
        
        w = tk.Button(box, text="Cancel", width=10, command=self.cancel)
        w.pack(side=tk.LEFT, padx=5, pady=5)
        
        w = tk.Button(box, text="Save", width=10, command=self.close_and_save, default=tk.ACTIVE)
        w.pack(side=tk.LEFT, padx=5, pady=5)

        self.bind("<Return>", self.close_and_save)
        self.bind("<Escape>", self.cancel)

        box.pack()

    def close_dont_save(self, event=None):
        self.close = True
        self.save = False
        self.close_window()

    def close_and_save(self, event=None):
        self.close = True
        self.save = True
        self.close_window()

    def cancel(self, event=None):
        self.close = False
        self.save = False
        self.close_window()

    def close_window(self):
        self.parent.focus_set()
        self.destroy()

class FileOpener:

    def __init__(self, root, tabs, preferences):
        self.root = root
        self.tabs = tabs
        self.preferences = preferences

    def openFile(self):

        fileName = self.SelectFile(parent=self.root, defaultextension=".xml")
        self.loadFile(fileName)

    def SelectFile(self, parent, defaultextension=None):
            if len(self.preferences.workSpaceFolder) > 0:
                    return askopenfilename(parent=parent, initialdir=self.preferences.workSpaceFolder, defaultextension=defaultextension)
            else:
                    return askopenfilename(parent=parent, defaultextension=defaultextension)
    
    def loadFile(self, fileName):

        if len(fileName) > 0:
            
            detector = configuration.TypeDetector(fileName)
            
            if detector.file_type == "analysis":
                self.tabs.addAnalysis(fileName)
            elif detector.file_type == "dataset":
                self.tabs.addDataset(fileName)
            else:
                raise Exception("Unkown file type: {0}".format(detector.file_type))

            self.preferences.addRecent(fileName)
            
def openMaximized(root):

    w, h = root.winfo_screenwidth(), root.winfo_screenheight()
    root.geometry("%dx%d+0+0" % (w, h))
        
def hello():
    print "hello!"

def getTabID(notebook):
    my_tabs = notebook.tabs()
    tab_id = my_tabs[len(my_tabs) - 1]
    return tab_id

class ClosableTab:

    def __init__(self, notebook, fileName, console):

        self.console = console
        self.name = os.path.basename(fileName)
        self.frame = tk.Frame(notebook)

        notebook.add(self.frame, text=self.name, padding=3)
        self.index = self.getTabIndex(notebook)

        #self.status = status
        self.isNew = False

        self.titleColumn = 0
        self.labelColumn = 1
        self.inputColumn = 2
        self.buttonColumn = 3
        self.secondButtonColumn = 4
        self.tipColumn = 5
        self.messageColumn = 6
        
        self.validations = []

        self.row = 0
        self.listboxEntries = {}

    def close(self):

        d = ConfirmClose(root, self.name)

        if d.save:
            self.save()
            self.console.write("{0} saved".format(self.name))

        return d.close

    def getTabIndex(self, notebook):
        my_tabs = notebook.tabs()
        return len(my_tabs) - 1

    def save(self):
        pass

    def addDatePickerEntry(self, master, title, validation, value, width = None):

        if value != None:
            if type(value) == str:
                textValue = value
            else:
                textValue = value.strftime(datePickerFormat)
        else:
            textValue = None
      
        entry = self.addEntry(master, title + " " + datePickerFormatDisplay, validation, textValue, width = width)
        entry.entry.config(state=tk.DISABLED)
        
        pickButton = tk.Button(master, text=".", command = DatePicker(self, entry, datePickerFormat), width=3, height=1)
        pickButton.grid(row=(self.row-1), sticky=tk.N, column=self.inputColumn, padx = 160)

        clearButton = tk.Button(master, text="x", command = ClearEntry(entry), width=3, height=1)
        clearButton.grid(row=(self.row-1), sticky=tk.W, column=self.inputColumn, padx = 133)
                
        entry.bindPickButton(pickButton)

        return entry
        
    def addPickerEntry(self, master, title, validation, value, width = None):

            entry = self.addEntry(master, title, validation, value, width = width)
            pickButton = tk.Button(master, text=".", command = ColumnPicker(self, entry), width=5, height=1)
            pickButton.grid(row=(self.row-1), sticky=tk.E+tk.N, column=self.buttonColumn)
                    
            entry.bindPickButton(pickButton)

            return entry
    
    def addOption(self, master, title, options, value):

            label = tk.Label(master, text=title)
            label.grid(row=self.row, sticky=tk.W, column=self.labelColumn)

            variable = tk.StringVar(master, value)

            option = apply(tk.OptionMenu, (master, variable) + tuple(options))
            option.grid(row=self.row, column=self.inputColumn, sticky=tk.W)

            self.row += 1

            return variable
            
    def addListBox(self, master, title):
            
            scrollbar =  tk.Scrollbar(master, orient=tk.VERTICAL)
            tipLabel = tk.Label(master, text="")
            tipLabel.grid(row = self.row, sticky=tk.W, column=self.tipColumn)                
            lb = tk.Listbox(master, yscrollcommand=scrollbar, selectmode=tk.EXTENDED, height=3)  
            
            self.listboxEntries[title] = ListBoxEntry(lb,scrollbar,tipLabel)              
            self.row += 1
            self.listboxEntries[title].scrollbar.configure(command=self.listboxEntries[title].listbox.yview)
            self.listboxEntries[title].scrollbar.grid(row=self.row, sticky=tk.W+tk.N+tk.S, column=self.titleColumn)
            return self.listboxEntries[title]

    def addCheckBox(self, master, title, value):

            label = tk.Label(master, text=title)
            label.grid(row=self.row, sticky=tk.W, column=self.labelColumn)
            variable = tk.IntVar(master, value)

            checkButton = tk.Checkbutton(master, variable=variable)
            checkButton.grid(row=self.row, column=self.inputColumn, sticky=tk.W)

            self.row += 1

            return variable

    def addTitleRow(self, master, title):

            tk.Label(master, text=title).grid(row=self.row, sticky=tk.W, column=self.titleColumn, columnspan = 2)

            #add dummy label to stop form shrinking when validation messages hidden
            tk.Label(master, text = " " * 70).grid(row=self.row, sticky=tk.W, column=self.messageColumn)

            self.row += 1

    def addEntry(self, master, title, validation, value, width = None):

            variable = tk.StringVar(master, value)

            label = tk.Label(master, text=title)
            label.grid(row = self.row, sticky=tk.W, column=self.labelColumn)

            tipLabel = tk.Label(master, text="")
            tipLabel.grid(row = self.row, sticky=tk.W, column=self.tipColumn)

            if validation != None:
                    validation.messageLabel.grid(row = self.row, sticky=tk.W, column=self.messageColumn)
                    validation.title = title
                    self.validations.append(validation)
                    validationCommand = validation.CMD
            else:
                    validationCommand = None

            entry = tk.Entry(master, textvariable=variable, validate = 'key', validatecommand = validationCommand, width = width)

            entry.grid(row=self.row, column=self.inputColumn, sticky=tk.W)

            if validation != None:
                validation.link(entry)

            self.row += 1

            return VariableEntry(variable, entry, tipLabel)

    def addFileSaveAsEntry(self, master, title, validation, value, width = 60):

            variable = self.addEntry(master, title, validation, value, width, showHideCommand)

            button = tk.Button(master, text="...", command = SetFileSaveAsCommand(master, variable), height=1)
            button.grid(row=(self.row - 1), sticky=tk.E+tk.W, column=self.buttonColumn)

            return variable

    def addFileOpenEntry(self, master, title, validation, value, basePathVariable = None, width = 60):

            variable = self.addEntry(master, title, validation, value, width)

            button = tk.Button(master, text="...", command = SetFileOpenCommand(master, variable, basePathVariable), height=1)
            button.grid(row=(self.row - 1), sticky=tk.E+tk.W, column=self.buttonColumn)

            return variable

    def validate(self):

            valid = True
            message = ""

            for validation in self.validations:
                    
                    if not validation.valid:
                            if not isinstance(validation,ValidateDatasets):
                                    message += "%s (%s)\r" % (validation.title, validation.messageLabel['text'])
                            else:
                                    message += "Datasets error. \r"
                            valid = False
            if not valid:

                    tkMessageBox.showwarning(
                            "Validation errors",
                            "Illegal values, please review error messages and try again:\r %s" % message
                            )
                            
                    return 0

            else:
    
                    return 1


class ClosableTabs:

    def __init__(self, parent, console):

        self.console = console
        self.loadImages()

        self.style = self.createClosableTabStyle()

        parent.bind_class("TNotebook", "<ButtonPress-1>", self.btn_press, True)
        parent.bind_class("TNotebook", "<ButtonRelease-1>", self.btn_release)

        #add notebook (holds tabs)
        self.nb = ttk.Notebook(parent, style="ButtonNotebook")
        self.nb.pressed_index = None

        self.tabs = {}

    def addAnalysis(self, fileName):

        closableTab = gui.analysis_tab.AnalysisTab(self.nb, fileName, self.console)

        self.tabs[closableTab.index] = closableTab

        return closableTab

    def addDataset(self, fileName):

        closableTab = gui.dataset_tab.DatasetTab(self.nb, fileName, self.console)

        self.tabs[closableTab.index] = closableTab

        return closableTab

    def loadImages(self):

        imgdir = os.path.join(os.path.dirname(__file__), 'img')

        self.i1 = tk.PhotoImage("img_close", file=os.path.join(imgdir, 'close.gif'))
        self.i2 = tk.PhotoImage("img_closeactive",
            file=os.path.join(imgdir, 'close_active.gif'))
        self.i3 = tk.PhotoImage("img_closepressed",
            file=os.path.join(imgdir, 'close_pressed.gif'))

    def btn_press(self, event):

        x, y, widget = event.x, event.y, event.widget
        elem = widget.identify(x, y)

        try:

            index = widget.index("@%d,%d" % (x, y))

            if "close" in elem:
                widget.state(['pressed'])
                widget.pressed_index = index
        
        except:

            pass

    def close_tab(self, widget, index):

        tab = self.tabs[index]

        if tab.close():
            widget.forget(index)
            widget.event_generate("<<NotebookClosedTab>>")

    def btn_release(self, event):
        x, y, widget = event.x, event.y, event.widget

        if not widget.instate(['pressed']):
            return

        elem =  widget.identify(x, y)
        index = widget.index("@%d,%d" % (x, y))

        if "close" in elem and widget.pressed_index == index:
            self.close_tab(widget, index)

        widget.state(["!pressed"])
        widget.pressed_index = None

    def createClosableTabStyle(self):

        style = ttk.Style()

        style.element_create("close", "image", "img_close",
            ("active", "pressed", "!disabled", "img_closepressed"),
            ("active", "!disabled", "img_closeactive"), border=8, sticky='')

        style.layout("ButtonNotebook", [("ButtonNotebook.client", {"sticky": "nswe"})])
        style.layout("ButtonNotebook.Tab", [
            ("ButtonNotebook.tab", {"sticky": "nswe", "children":
                [("ButtonNotebook.padding", {"side": "top", "sticky": "nswe",
                                             "children":
                    [("ButtonNotebook.focus", {"side": "top", "sticky": "nswe",
                                               "children":
                        [("ButtonNotebook.label", {"side": "left", "sticky": ''}),
                         ("ButtonNotebook.close", {"side": "left", "sticky": ''})]
                    })]
                })]
            })]
        )

        return style

class ValidationTabs:

    def __init__(self, parent):

        self.loadImages()

        #add notebook (holds tabs)
        self.nb = ttk.Notebook(parent)
        self.nb.pressed_index = None

    def add(self, name):

        my_frame = tk.Frame(self.nb)
        self.nb.add(my_frame, text=name, padding=3)

        tab_id = getTabID(self.nb)

        validationTab = ValidationTab(self.nb, tab_id, my_frame, self.img_invalid)

        return validationTab

    def loadImages(self):

        imgdir = os.path.join(os.path.dirname(__file__), 'img')

        self.img_valid = tk.PhotoImage("img_valid", file=os.path.join(imgdir, 'valid.gif'))
        self.img_invalid = tk.PhotoImage("img_invalid", file=os.path.join(imgdir, 'invalid.gif'))

    def pack(self):

        self.nb.pack(expand=1, fill='both')

class ValidationTab:

    def __init__(self, notebook, tab_id, frame, img_invalid):

        self.notebook = notebook
        self.tab_id = tab_id
        self.frame = frame
        self.img_invalid = img_invalid

    def validate(self, valid):
        
        if not valid:
            self.notebook.tab(self.tab_id, image = self.img_invalid, compound=tk.RIGHT)
        else:
            self.notebook.tab(self.tab_id, image = None)

class Console:

    def __init__(self, parent):

        scrollbar = tk.Scrollbar(parent, orient=tk.VERTICAL)
        self.listbox = tk.Listbox(parent, yscrollcommand=scrollbar.set, selectmode=tk.EXTENDED)
        scrollbar.configure(command=self.listbox.yview)

        self.listbox.pack(side=tk.LEFT,fill=tk.BOTH, expand=1, pady=5)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y, pady=5)

    def write(self, line):

        self.listbox.insert(tk.END, str(line))

class PCWG:

    def __init__(self, root):

        self.root = root

        self.added_recents = []
                
        tab_frame = tk.Frame(root)
        console_frame = tk.Frame(root, background="grey")
        
        tab_frame.grid(row=0, column=0, sticky="nsew")
        console_frame.grid(row=1, column=0, sticky="nsew")
        
        root.grid_rowconfigure(0, weight=1)
        root.grid_columnconfigure(0, weight=1)
        
        console = Console(console_frame)
        tabs = ClosableTabs(tab_frame, console)

        self.preferences = configuration.Preferences()
        self.fileOpener = FileOpener(root, tabs, self.preferences)

        self.addMenus(root)

        self.preferences.onRecentChange += self.addRecents

        
    def addMenus(self, root):

        #http://effbot.org/tkbook/menu.htm

        #add menu
        self.menubar = tk.Menu(root)

        # create a pulldown menu, and add it to the menu bar
        filemenu = tk.Menu(self.menubar)

        new_menu = tk.Menu(self.menubar)
        new_menu.add_command(label="Analysis")
        new_menu.add_command(label="Dataset")
        new_menu.add_command(label="Portfolio")

        self.menubar.add_cascade(label="File", menu=filemenu)
        filemenu.add_cascade(label="New", menu=new_menu)

        filemenu.add_command(label="Open", command=self.fileOpener.openFile)

        self.recent_menu = tk.Menu(self.menubar)
        filemenu.add_cascade(label="Open Recent", menu=self.recent_menu)
        self.addRecents()
        
        filemenu.add_command(label="Save")
        filemenu.add_command(label="Save As")
        filemenu.add_separator()
        filemenu.add_command(label="Exit", command=root.quit)

        #analysis_menu.add_command(label="Analysis")
        #filemenu.add_command(label="Dataset")
        #filemenu.add_command(label="Portfolio")
        #filemenu.add_cascade(label="Analysis", menu=filemenu)

        # create more pulldown menus
        editmenu = tk.Menu(self.menubar, tearoff=0)
        editmenu.add_command(label="Cut", command=hello)
        editmenu.add_command(label="Copy", command=hello)
        editmenu.add_command(label="Paste", command=hello)
        self.menubar.add_cascade(label="Edit", menu=editmenu)

        helpmenu = tk.Menu(self.menubar, tearoff=0)
        helpmenu.add_command(label="About", command=hello)
        self.menubar.add_cascade(label="Help", menu=helpmenu)

        # display the menu
        root.config(menu=self.menubar)

    def addRecents(self):
    
        for recent in self.preferences.recents:
            if recent not in self.added_recents:
                self.added_recents.append(recent)
                self.recent_menu.add_command(label=recent, command = OpenRecent(self.fileOpener, recent))


#start of main code

root = tk.Tk()

menu = PCWG(root)

openMaximized(root)

root.mainloop()

menu.preferences.save()