from __future__ import nested_scopes
__author__  = "Hugo Liu <hugo@media.mit.edu>"
__version__ = "1.3.1"
title = 'OMCSNet Browser '+__version__
import sys,random
import Tkinter
import tkFont
from Tkinter import *
import Pmw
import string
import OMCSNetAPI
import HyperlinkManager
class OMCSNetBrowser:


    query_history = []
    query_history_index = -1
    def __init__(self, parent):

        self._createAboutDialog(parent)
        self._createOptionsDialog(parent)
	self.balloon = Pmw.Balloon(parent)
	menuBar = Pmw.MenuBar(parent,
		hull_relief = 'raised',
		hull_borderwidth = 1,
		balloon = self.balloon)
	menuBar.pack(fill = 'x')
	self.menuBar = menuBar
	menuBar.addmenu('File', 'Close this window or exit')
	menuBar.addmenuitem('File', 'command', 'Close this window',
		command = self.foo(),
		label = 'Close')
	menuBar.addmenuitem('File', 'separator')
	menuBar.addmenuitem('File', 'command', 'Exit the application',
		command = parent.destroy,
		label = 'Exit')
	menuBar.addmenu('Edit', 'Cut, copy or paste')
	menuBar.addmenuitem('Edit', 'command', 'Delete the current selection',
		command = self.foo(),
		label = 'Delete')
	menuBar.addmenu('Options', 'Set user preferences')
	menuBar.addmenuitem('Options', 'command', 'Set general preferences',
		command = self.options_dialog.show,
		label = 'General...')
        self.backedgeToggle = Tkinter.IntVar()
        self.backedgeToggle.set(0)
        menuBar.addmenuitem('Options', 'checkbutton', 'Toggle me on/off',
                label = 'Show Backward Edges',
                command = self._backedgeToggleRefresh,
                variable = self.backedgeToggle)
        self.fontSizeVar = Tkinter.IntVar()
        self.fontSizeVar.set(0)
        self.set_fontsize()
        menuBar.addmenuitem('Options', 'checkbutton', 'Toggle me on/off',
                label = 'Use Huge Font',
                command = self.set_fontsize,
                variable = self.fontSizeVar)
	menuBar.addmenu('Help', 'User manuals', side = 'right')
	menuBar.addmenuitem('Help', 'command', 'About this application',
		command = self.about.show,
		label = 'About...')
        panedWidgetOutside = Pmw.PanedWidget(parent,
                                             orient = 'vertical',
                                             hull_height = 500,
                                             hull_width = 600)
        panedWidgetOutside.add('navbar', min = 0.05)
        panedWidgetOutside.add('pathbar', min = 0.05)
        panedWidgetOutside.add('bigwindow',min = 0.05)
        panedWidgetOutside.pack(fill = 'both', expand = 1)
        navbar_pane = panedWidgetOutside.pane('navbar')
        self.searchbar = Pmw.EntryField(navbar_pane,
		labelpos = 'w',
                label_text = 'Lookup:',
		validate = None,
		command = self.execute_query)
        self.searchbar.pack(side='left')
        self.nav_back = Tkinter.Button(navbar_pane,text='<<',command=self.go_back_history,state='disabled')
        self.nav_back.pack(side='left')
        self.nav_forward = Tkinter.Button(navbar_pane,text='>>',command=self.go_forward_history,state='disabled')
        self.nav_forward.pack(side='left')
        self.nav_findcontext = Tkinter.Button(navbar_pane,text='Context',command=self.execute_contextquery)
        self.nav_findcontext.pack(side='left')
        self.nav_makeanalogy = Tkinter.Button(navbar_pane,text='Analogous',command=self.execute_analogousconceptsquery)
        self.nav_makeanalogy.pack(side='left')
        pathbar_pane = panedWidgetOutside.pane('pathbar')
        self.pathbar = Pmw.EntryField(pathbar_pane,
		labelpos = 'w',
                label_text = 'Paths to:',
		validate = None,
		command = self.execute_pathquery)
        self.pathbar.pack(side='left')
        self.results = Pmw.ScrolledText(panedWidgetOutside.pane('bigwindow'),
             text_wrap = 'word',
             text_state = 'normal'
             )
        self.results.pack(fill = 'both', expand = 1)
        self.results.component('text').bind('<BackSpace>',self.go_back_history)
        self.searchbar.bind('<Return>',self.execute_query)
        self.pathbar.bind('<Return>',self.execute_pathquery)
        self.hyperlink = HyperlinkManager.HyperlinkManager(self.results.component('text'))
        self.theOMCSNetAPI = OMCSNetAPI.OMCSNetAPI()
    def commitOptions(self,results):

        if results in ['OK','Apply']:
            pass
        if results in ['Cancel']:
            pass
        if results in ['OK','Cancel']:
            self.options_dialog.withdraw()
    def _createOptionsDialog(self,parent):

	self.options_dialog = Pmw.Dialog(parent,
	    buttons = ('OK', 'Apply','Cancel'),
	    defaultbutton = 'OK',

	    title = 'Options',
	    command = self.commitOptions)
	self.options_dialog.withdraw()
        notebook = Pmw.NoteBook(self.options_dialog.interior())
        notebook.pack(fill = 'both', expand = 1, padx = 10, pady = 10)
        page = notebook.add('')
        notebook.setnaturalsize()
    def _createAboutDialog(self,parent):

	Pmw.aboutversion(__version__)
	Pmw.aboutcopyright('Copyright 2002-2003 by authors')
        Pmw.aboutcopyright('')
	Pmw.aboutcontact(
	    'For information about OMCSNet contact:\n' +
	    '  Hugo Liu, Push Singh\n' +
	    '  {hugo,push}@media.mit.edu'
	)
	self.about = Pmw.AboutDialog(parent, applicationname = 'OMCSNet Browser')
	self.about.withdraw()
    def _backedgeToggleRefresh(self):

        self.execute_query()
    def set_fontsize(self):

        if self.fontSizeVar.get():
            pt = 20
        else:
            pt = 12
        mainFont.configure(size=pt)
        return
    def foo(self):

        pass
    def at_end_of_history_p(self,query=''):

        if self.query_history_index != len(self.query_history)-1 and self.query_history_index >= 0 and self.query_history[self.query_history_index] == query:
            return 0
        else:
            return 1
    def add_history(self,query):

        del self.query_history[self.query_history_index+1:]
        self.query_history.append(query)
        self.query_history_index = len(self.query_history)-1
        self.update_nav_buttons()
        return
    def go_back_history(self,*args):

        if self.query_history_index < 0:
            return
        self.query_history_index -= 1
        query = self.query_history[self.query_history_index]
        self.searchbar.setvalue(query)
        self.execute_query(query)
        self.update_nav_buttons()
        return
    def go_forward_history(self):

        if not self.at_end_of_history_p(self.searchbar.get()):
            self.query_history_index += 1
            query = self.query_history[self.query_history_index]
            self.searchbar.setvalue(query)
            self.execute_query(query)
        self.update_nav_buttons()
        return
    def update_nav_buttons(self):

        if self.query_history_index <= 0:
            self.nav_back.configure(state='disabled')
        else:
            self.nav_back.configure(state='normal')
        if self.at_end_of_history_p(self.searchbar.get()):
            self.nav_forward.configure(state='disabled')
        else:
            self.nav_forward.configure(state='normal')
        return
    def execute_analogousconceptsquery(self,*args):

        nodename = self.searchbar.get()
        nodes = self.theOMCSNetAPI.find_analogous_nodes(nodename)
        note = 'These are the top '+str(len(nodes))+' analogous concepts of ['+nodename+']. \n\n'
        self.results.clear()
        self.results.insert(END,note)
        self.pp_analogousconcepts_intotext(nodes,nodename,self.results,hyperlink_ptr=self.hyperlink)
        self.results.see('1.0')
        self.results.update_idletasks()
        return
    def execute_contextquery(self,*args):

        nodelist = self.searchbar.get()
        nodelist = nodelist.split(',')
        nodelist = map(lambda x:x.strip(),nodelist)
        nodelist = filter(lambda x:x!='',nodelist)
        context = self.theOMCSNetAPI.get_context(nodelist)
        note = 'These are the top '+str(len(context))+' concepts in the commonsense context of '+str(nodelist)+'. \n\n'
        self.results.clear()
        self.results.insert(END,note)
        self.pp_context_intotext(context,self.results,hyperlink_ptr=self.hyperlink)
        self.results.see('1.0')
        self.results.update_idletasks()
        return
    def execute_pathquery(self,*args):

        dir = 'both'
        targetconcept = self.pathbar.get()
        if len(targetconcept) > 2 and targetconcept[-2:] == '->':
            targetconcept = targetconcept[:-2]
            dir = 'fw'
        origin_node = self.searchbar.get()
        destination_node = targetconcept
        paths = self.theOMCSNetAPI.find_paths_from_a_to_b(origin_node,destination_node,sentences_p=1,max_node_visits=10000,max_number_of_results =200)
        note = 'We found '+str(len(paths))+' path(s) from the node ['+origin_node+'] to the node ['+destination_node+'].\n\n'
        self.results.clear()
        self.results.insert(END,note)
        self.pp_paths_intotext(paths,self.results,hyperlink_ptr=self.hyperlink)
        self.results.see('1.0')
        self.results.update_idletasks()
        return
    def execute_query(self,*args):

        query = self.searchbar.get()
        if self.at_end_of_history_p(query):
            if len(self.query_history) == 0:
                self.add_history(query)
            elif self.query_history[-1] != query:
                self.add_history(query)
        edges = self.theOMCSNetAPI.get_edges_by_origin_nodename(query,sentence_p=1)
        backedges = []
        if self.backedgeToggle.get():
            backedges = self.theOMCSNetAPI.get_edges_by_destination_nodename(query,sentence_p=1)
        cleaned_note = 'The concept: "'+query+'" has the following relationships:\n'
        text_ptr = self.results
        self.results.clear()
        text_ptr.insert(END,cleaned_note)
        if edges == [] and backedges == []:
            pp = '\nConcept did not return any results.'
            text_ptr.insert(END,pp)
        else:
            self.pp_edges_intotext(edges,backedges,text_ptr,hyperlink_ptr=self.hyperlink)
        text_ptr.see('1.0')
        text_ptr.update_idletasks()
        return
    def link_callback(self,args):

        print args
        self.searchbar.setvalue(args)
        self.execute_query()
        return
    def pp_context_intotext(self,contextconcepts,text_ptr,hyperlink_ptr):

        text_ptr.insert(END, '\n')
        for i in range(len(contextconcepts)):
            concept = contextconcepts[i]
            word = concept[0]
            weight = concept[1]
            confidence = str(round(weight*100,1))+'%'
            text_ptr.insert(END,'    ')
            text_ptr.insert(END,word,hyperlink_ptr.add([self.link_callback,word]))
            text_ptr.insert(END,'   ('+confidence + ')' + '\n')
        return
    def pp_analogousconcepts_intotext(self,nodes,originnode,text_ptr,hyperlink_ptr):

        text_ptr.insert(END, '\n')
        for i in range(len(nodes)):
            node = nodes[i]
            word = node[0]
            reasons = node[1]
            score = node[2]
            pp_score = str(round(score,2)*100)+'%'
            text_ptr.insert(END,'    ')
            text_ptr.insert(END,word,hyperlink_ptr.add([self.link_callback,word]))
            text_ptr.insert(END,' is like '+originnode+' ('+pp_score+') because both:\n')
            for reason in reasons:
                text_ptr.insert(END,'      ==['+reason[0]+']==> ')
                text_ptr.insert(END,reason[1],hyperlink_ptr.add([self.link_callback,reason[1]]))
                text_ptr.insert(END,'\n')
            text_ptr.insert(END,'\n')
        return
    def pp_paths_intotext(self,paths,text_ptr,hyperlink_ptr):

        text_ptr.insert(END, '\n')
        for i in range(len(paths)):
            path = paths[i]
            text_ptr.insert(END,'  PATH '+str(i+1)+' OF '+str(len(paths))+' (has '+str(len(path)-1)+' links):\n\n')
            prev_node = path[0][1]
            for j in range(1,len(path)):
                edge = path[j]
                pred,targetnode,sentence=edge
                text_ptr.insert(END,'    ')
                text_ptr.insert(END,prev_node,hyperlink_ptr.add([self.link_callback,prev_node]))
                if 0:
                    arrow1 = '<=='
                    arrow2 = '=='
                else:
                    arrow1 = '=='
                    arrow2 = '==>'
                text_ptr.insert(END,' '+arrow1+' ['+pred+'] '+arrow2+' ')
                text_ptr.insert(END,targetnode,hyperlink_ptr.add([self.link_callback,targetnode]))
                text_ptr.insert(END,'   ('+sentence + ')' + '\n')
                prev_node = targetnode
            text_ptr.insert(END,'\n')
        return
    def pp_edges_intotext(self,edges,backedges,text_ptr,hyperlink_ptr):

        text_ptr.insert(END, '\n')
        forward_edges = edges
        unique_preds_in_fe = filter(None,map(lambda i, d={}:(i,None)[d.has_key(i) or d.update({i:1}) or 0],map(lambda x:x[0],forward_edges)))
        backward_edges = backedges
        unique_preds_in_be = filter(None,map(lambda i, d={}:(i,None)[d.has_key(i) or d.update({i:1}) or 0],map(lambda x:x[0],backward_edges)))
        if forward_edges != []:
            text_ptr.insert(END,'FORWARD EDGES:\n\n')
        for pred in unique_preds_in_fe:
            text_ptr.insert(END,'  ['+pred+']\n')
            for edge in filter(lambda x:x[0]==pred,forward_edges):
                pred,originnode,destinationnode,sentence=edge
                text_ptr.insert(END,'    ==> ')
                text_ptr.insert(END,destinationnode,hyperlink_ptr.add([self.link_callback,destinationnode]))
                text_ptr.insert(END,' -- ('+sentence + ')' + '\n')
            text_ptr.insert(END,'\n')
        if not self.backedgeToggle.get():
            return
        if backward_edges != []:
            text_ptr.insert(END,'BACKWARD EDGES:\n\n')
        for pred in unique_preds_in_be:
            text_ptr.insert(END,'  ['+pred+']\n')
            for edge in filter(lambda x:x[0]==pred,backward_edges):
                pred,originnode,destinationnode,sentence=edge
                text_ptr.insert(END,'    <== ')
                text_ptr.insert(END,originnode,hyperlink_ptr.add([self.link_callback,originnode]))
                text_ptr.insert(END,' -- ('+sentence + ')' + '\n')
            text_ptr.insert(END,'\n')
        return
if __name__ == '__main__':
    root = Tkinter.Tk()
    Pmw.initialise(root, fontScheme = 'pmw')
    root.title(title)
    mainFont = tkFont.Font(size=14)
    root.option_add("*Font", mainFont)
    widget = OMCSNetBrowser(root)
    root.mainloop()