# you really want to read the templates.py files before
# hacking this file

from copy import copy
from gtk import *
import string


from specific import *
from entry_classes import *
import joins
import log
import checks

################ row widgets


class select_navigate_widget:
    "provides facility for doing a SELECT and for navigating it"

    __cursor=None

    #button_next button_prev may be accessed from outside
    def __init__(self):  
        hbox=GtkHBox()
        self.widget=hbox
        self.__widget=hbox
        
        hbox.show()

        prev=GtkButton()
        a=GtkArrow(ARROW_LEFT, SHADOW_OUT)
        a.show()
        prev.add(a)
        prev.show()
        hbox.pack_start(prev, FALSE, FALSE, 2)
        #this may be overridden
        prev.connect("clicked", self.cursor_prev)
        self.button_prev=prev
        
        next=GtkButton()
        a=GtkArrow(ARROW_RIGHT, SHADOW_OUT)
        a.show()
        next.add(a)
        next.show()
        hbox.pack_start(next, FALSE, FALSE, 2)
        #this may be overridden
        next.connect("clicked", self.cursor_next)
        self.button_next=next

        button_=GtkButton("select where")
        button_.show()
        hbox.pack_start(button_, TRUE, TRUE, 2)
        button_.set_flags(CAN_FOCUS)
        self.button_select=button_
        button_.connect("clicked",self.select)
        
        self.entry_key=GtkEntry()
        if self.n_key!=None:
            l=GtkLabel(self.fields[self.n_key]+"=")
            hbox.pack_start(l,FALSE, FALSE, 2)
            l.show()
            self.entry_key.connect("changed", self.select)
            #FIXME this is not in 'characters':self.entry_key.set_usize(80,10)
            hbox.pack_start(self.entry_key,FALSE, FALSE, 2)        
            self.entry_key.show()        
            l=GtkLabel("&&")
            hbox.pack_start(l,FALSE, FALSE, 2)
            l.show()
            
        entry=GtkEntry()
        hbox.pack_start(entry, TRUE, TRUE, 2)
        entry.show()
        entry.set_flags(CAN_FOCUS)
        entry.connect("activate",self.select) 
        self.entry_where=entry
        

        
    #useful methods to use the cursor in a consistent way

    def select(self,button):
        self.cursor_del()
        #FIXME THIS DOESNT WORK
        #sql="SELECT ("+self.keyfield+", id) FROM " + self.table_name
        sql="SELECT * FROM " + self.table_name
        selkey=self.entry_key.get_text()
        selwhere=self.entry_where.get_text()
        if selkey or selwhere:
            sql=sql+" WHERE "
        if selkey != "":            
            sql=sql + self.fields[self.n_key] + " LIKE '"+selkey+"%' "
        if selwhere != "": 
            if  selkey != "":
                sql=sql+" && "
            sql=sql + selwhere
        if self.cursor_query(sql):
            r=self.cursor_next(None)
            if not r:
                self.log(1,"no matches for:" +sql)
                self.cursor_del()

    def has_cursor(self):
        return not not self.__cursor
    
    def cursor_query(self,query):
        #prepares the cursor for a SELECT query
        if self.__cursor:
            del self.__cursor
            
        self.__cursor=self.db.cursor()
        if self.__cursor == None:
            self.log(3, "E: could not get a cursor (please retry)")
            return FALSE
                   
        self.log(3, "SQL :"+query)

        try:
            self.__cursor.execute(query)
        except self.dbapi_exceptions.Warning, a:
            self.log(1,"WARNING: "+str(a))
            self.__cursor=None
            return FALSE
        except self.dbapi_exceptions.Error, a:
            self.log(1,"ERROR: "+str(a))
            self.__cursor=None
            return FALSE
        else:
            self.__cursor.backlist=[]
            self.__cursor.position=-1
            return TRUE

        
    def cursor_next(self,b):
        if len(self.__cursor.backlist) > self.__cursor.position+1:
            self.__cursor.position = 1 + self.__cursor.position
            r=self.__cursor.backlist[self.__cursor.position]
        else:
            r=self.__cursor.fetchone()
            if r:
               self.__cursor.backlist.append(r)
               self.__cursor.position = 1 + self.__cursor.position
        if r:
            self.set_row(r)
        else:
            #if called from a button...
            if b:
                self.log(1,"no more matches")
            self.set_row(None)
            #self.cursor=None
        self.button_next.set_sensitive(not not r)
        self.button_prev.set_sensitive(self.__cursor.position>0)
        #print "NEXT"+str(self.__cursor.position)
        return r

    def cursor_prev(self,b):
        if self.__cursor.position>0:
            self.__cursor.position = self.__cursor.position - 1
        else:        
            self.log(1,"already on first row")        
        r=self.__cursor.backlist[self.__cursor.position]
        self.set_row(r)
        self.button_prev.set_sensitive(self.__cursor.position>0)
        self.button_next.set_sensitive(TRUE)
        #print "PREV"+str(self.__cursor.position)
        return r

    def cursor_del(self):
        if self.__cursor:
            del self.__cursor
            self.__cursor=None
        self.button_next.set_sensitive(FALSE)
        self.button_prev.set_sensitive(FALSE)


    
class fast_search_widget(table_info,row_widget_template,
                         select_navigate_widget):
    __row=None
    __popup_window=None
    __primary_value=None
    def __init__(self,db,db_name,table_name,
                 #which field to use to select when doing set() or get()
                 join_field=None ):

        table_info.__init__(self,db,db_name,table_name)
        # there is no __init__
        #row_widget_template.__init__(self,db,db_name,table_name)

        if join_field != None:
            self.n_primary=self.fields.index(join_field)

        select_navigate_widget.__init__(self)
        #save it for future references
        self.select_navigate_thewidget=self.widget


        self.db_name=db_name
        self.table_name=table_name
        self.db=db

        #main widget
        vbox=GtkVBox()

        self.label_current=GtkLabel()
        self.label_current.show()

        b=GtkButton()
        b.show()
        b.add(self.label_current)
        b.connect("pressed",self.__popup)
        b.connect("released",self.__popdown)

        vbox.pack_start(b, FALSE, FALSE, 2)
        
        vbox.pack_start(self.select_navigate_thewidget, FALSE, FALSE, 2)

        vbox.show()
        self.widget=vbox
        self.__widget=vbox

    def __popup(self,b):
        if self.__row==None:
            return
        if not self.__popup_window:    
            f=form_widget(self.db,self.db_name,self.table_name)
            f.set_editable(FALSE)
            self.__popup_form=f
            window1=GtkWindow(WINDOW_POPUP)
            window1.set_policy(FALSE, TRUE, FALSE)
            window1.add(f.widget)
            window1.set_position(WIN_POS_MOUSE)
            self.__popup_window=window1
        self.__popup_window.show()
        self.__popup_form.set_row(self.__row)
        
    def __popdown(self,b):
        if self.__popup_window:
            self.__popup_window.hide()
            
        
    def get(self):
        return self.__primary_value
    
    def set(self,data):
        self.cursor_del()
        
        if self.n_primary == None:
            self.log(3,'INTERNAL ERROR: cant "set" into table ' + self.table_name)
        if data != None:
            sql="SELECT * FROM " + self.table_name +\
                " WHERE " + self.fields[self.n_primary] +   " = "+str(data)
            cursor=self.db.cursor()
            cursor.execute(sql)        
            self.set_row(cursor.fetchone())
            del cursor
        else:
            self.set_row(None)
        #whatever it is... save it!
        self.__primary_value=data


    def set_row(self,r):
        self.__row=r
        if r != None:
            self.__primary_value=r[self.n_primary]
            self.label_current.set_text(str(r[self.n_key:])[:60])
        else:
            #FIXME: is this the right thing to do?
            self.__primary_value=None
            self.label_current.set_text("NULL")

    def get_row(self):
        return self.__row
    
    def set_editable(self,v):        
        self.select_navigate_thewidget.set_sensitive(v)            
        #self.select_navigate.set_editable(v)










        
class form_widget(table_info,row_widget_template):
    __current_row=None
    
    def __init__(self,db,db_name,table_name,
                 #these are used for automatic joins
                 join_rules=[],table_fields={}):
        self.field_entry={}

        table_info.__init__(self,db,db_name,table_name)
        #row_widget_template.__init__(self,db,db_name,table_name)

        self.__table_name=table_name
        
        frame=GtkFrame(table_name)
        frame.show()

       #export this
        self.widget=frame
        #and keep a copy
        self.__widget=frame
        
        vbox2=GtkVBox()
        vbox2.show()
        
        frame.add(vbox2)

 
        ########fields
        for f in self.fields:
            columninfo=self.field_columninfo[f]
            description=self.field_description[f]
            (Field, Type, Null, Key, Default, Extra) = columninfo

            hbox1=GtkHBox()
            vbox2.pack_start(hbox1, FALSE, TRUE, 0)
            hbox1.set_usize(-1, -1)
            hbox1.set_homogeneous(FALSE)
            hbox1.set_spacing(0)

            eventbox1=GtkEventBox()
            hbox1.pack_start(eventbox1, FALSE, FALSE, 0)
            eventbox1.set_usize(-1, -1)
            label1=GtkLabel()
            eventbox1.add(label1)
            tooltip = GtkTooltips();            
            tooltip.set_tip(eventbox1,repr(columninfo) +"\n" + repr(description ))
            eventbox1.show()


            label1.set_justify(JUSTIFY_CENTER)
            label1.set_alignment(0.5, 0.5)
            label1.set_padding(0, 0)
            label1.show()

            join=None
            if join_rules != []:
                join=joins.find_join(self.table_name, Field,
                                     #these are used for automatic joins
                                     join_rules,table_fields)
            if join != None:
                (t2, f2)=join
                #print "join " + repr(join)
                entry1=fast_search_widget(self.db,self.db_name,t2,f2)
                entry1.dbapi_exceptions=self.dbapi_exceptions
        
                label1.set_text(Field+'=\n='+t2+'.'+f2)
            else:
                label1.set_text(Field)
                entry1=appropriate_entry_widget(columninfo,description)
            #redirect log messages                
            entry1.log=self.log
            hbox1.pack_start(entry1.widget, TRUE, TRUE, 0)
            hbox1.show()
            self.field_entry[Field]=entry1

    #template methods
    def set_editable(self,v):
        for f in self.fields:
            e=self.field_entry[f]
            if f == "id":
                #e.entry_widget
                e.set_editable(FALSE)
            else:
                #e.entry_widget
                e.set_editable(v)
                
    def get_row(self):
        r=[]
        for i in range(self.n_fields):
            entry=self.field_entry[ self.fields[i] ]
            r.append(entry.get())
        return r
    
    def get_current_row(self):
        """this is the row that was set into this class, not the one that the
        user edits"""
        return self.__current_row

    def set_row(self,row):
        if row != None:
            for i in  range(len(row)):
                entry=self.field_entry[ self.fields[i] ]
                entry.set(row[i])
            
                self.__current_row=copy(row)
        #else:
        #    self.log(1,"Can't set row to None in form for table "+self.__table_name)






class editor_widget(log.log_widget,
                    form_widget, select_navigate_widget):
    def __init__(self,db,db_name,table_name,
                 #api
                 dbapi,dbapi_exceptions,
                 #these are used for automatic joins
                 join_rules=[],table_fields={},
                 #list of functions that will check the row (see checks.py)
                 checks=[checks.extra_spaces,checks.empty_null,
                         checks.not_null_not_empty]):
        self.dbapi_exceptions=dbapi_exceptions
        
        self.checks=checks

        ###the form widget
        form_widget.__init__(self,db,db_name,table_name,
                             #these are used for automatic joins
                             join_rules,table_fields)
        #save it for future references
        formwidget=self.widget

        ### the select navigate widget
        select_navigate_widget.__init__(self)
        #save it for future references
        self.select_navigate_thewidget=self.widget

        ###the log widget (it is in log.py)
        log.log_widget.__init__(self)
        #self.log=logwidget.log
        #use it also for the form widget
        #formwidget.log=logwidget.log
        #save it for future references
        self.log_thewidget=self.widget
        
        vbox1=GtkVBox()

        vbox1.set_spacing(2)

        
        #hbox4=GtkHBox()
        handlebox=GtkHandleBox()
        vbox1.pack_start(handlebox, FALSE, FALSE, 2)
        handlebox.show()

        vbox4=GtkVBox()
        handlebox.add(vbox4)
        vbox4.show()

        ### toolbar of buttons
        tb=GtkToolbar()
        tb.show()
        tb.set_space_style(TOOLBAR_SPACE_LINE)
        tb.set_space_size(20)
        
        vbox4.pack_start(tb,FALSE,FALSE,2)



        button_=GtkButton("insert")
        tb.append_widget(button_, "","")
        button_.show()
        button_.set_flags(CAN_FOCUS)
        self.button_insert=button_
        self.button_insert.connect("clicked",self.insert)
                         
        button_=GtkButton("edit")
        button_.show()
        tb.append_widget(button_, "","")
        button_.set_flags(CAN_FOCUS)
        self.button_edit=button_
        button_.show()                
        self.button_edit.connect("clicked",self.edit)
                         
        button_=GtkButton("update")
        button_.show()
        tb.append_widget(button_, "","")
        button_.set_flags(CAN_FOCUS)
        self.button_update=button_
        self.button_update.connect("clicked",self.update)

        button_=GtkButton("cancel")
        button_.show()
        tb.append_widget(button_, "","")
        button_.set_flags(CAN_FOCUS)
        #button_.show()
        self.button_cancel=button_
        self.button_cancel.connect("clicked",self.cancel)

        tb.append_space()

        button_=GtkButton("default")
        button_.show()
        tb.append_widget(button_, "sets entry to default values","")
        button_.set_flags(CAN_FOCUS)
        #button_.show()
        self.button_default=button_
        self.button_default.connect("clicked",self.default)

        tb.append_space()

        button_=GtkButton("reset")
        tb.append_widget(button_, "returns this program to the starting state","")
        button_.set_flags(CAN_FOCUS)
        button_.show()
        self.button_reset=button_
        self.button_reset.connect("clicked",self.reset)


        vbox4.pack_start(self.select_navigate_thewidget, FALSE,FALSE, 2)


        ###select stuff
        hbox=GtkHBox()        
        vbox4.pack_start(hbox, FALSE,FALSE, 2)
        hbox.show()

        #so we can show/hide
        self.widget_select=hbox

        self.button_prev.set_usize(20,20)
        self.button_next.set_usize(20,20)

        #add another callback
        self.button_prev.connect("clicked", self.editable)

        self.button_next.connect("clicked", self.editable)


        vbox1.pack_start(formwidget, FALSE, FALSE, 2)


        #the log widget (it is in log.py)
        vbox1.pack_start(self.log_thewidget, FALSE, FALSE, 2)

        vbox1.show()
        #export this
        self.widget=vbox1
        #and keep a copy
        self.__widget=vbox1

        #bring to initial state
        self.reset(None)


    ############################
    def edit(self,button):
        self.set_editable(TRUE)
        if self.has_cursor():
            self.button_update.set_sensitive(TRUE)
        else:
            self.button_insert.set_sensitive(TRUE)
        self.button_default.set_sensitive(TRUE)
        self.button_cancel.set_sensitive(TRUE)

        self.button_next.set_sensitive(FALSE)
        self.button_prev.set_sensitive(FALSE)

        self.button_edit.set_sensitive(FALSE)
        #self.button_select.set_sensitive(FALSE)
        #self.entry_select.set_sensitive(FALSE)
        self.select_navigate_thewidget.set_sensitive(FALSE)
        
    def default(self,button):        
        self.set_row(self.default_row)

    def cancel(self,button):            
        self.button_next.set_sensitive(self.has_cursor() )
        self.button_prev.set_sensitive(self.has_cursor() )
        self.select_navigate_thewidget.set_sensitive( self.has_cursor() )
        
        self.button_edit.set_sensitive(TRUE)        
        
        self.set_editable(FALSE)
        self.set_row(self.get_current_row())
        self.button_update.set_sensitive(FALSE)
        self.button_insert.set_sensitive(FALSE)
        self.button_cancel.set_sensitive(FALSE)

    def editable(self,button):
        r=self.get_row()
        if r:
            self.button_edit.set_sensitive(None != r[self.n_primary])        
    
    def update(self,b):
        r= self.get_row()
        (r, msgs,msg) = checks.check_list_of_checks(self.checks,self,r)
        if r == None:
            self.log(1,msg)
            return

        sql="UPDATE %s SET "  % self.table_name
        for i in range(self.n_fields):
            sql=sql+self.fields[i]+"=%s, "
        sql=sql[:-2]+" WHERE "

        cr=self.get_current_row()
        
        #FIXME: horrible fix for strange "cant update" problem
        if len(self.primary_key_fields)==1:
            sql=sql+self.fields[self.n_primary]+ "=%s && "
            longrow=tuple(list(r) + [cr[self.n_primary]] )
        else:
            # this should work perfectly... but it does not!
            for i in range(self.n_fields):
                sql=sql+self.fields[i]+ "=%s && "
            longrow=tuple(list(r) + list(cr))
        sql=sql[:-4]
        #WHAT THE HELL IS WRONG
        #print "R " + repr (r)
        #print "CR " + repr(cr)
        #print "SQL " + repr(sql)

        self.db.select_db(self.db_name)
        self.db.paramstyle='format'
        c=self.db.cursor()
        try:
            try:
                c.execute(sql, longrow )
            except self.dbapi_exceptions.Warning, b:
                msg=msg+"\nSQL WARNING:"+str(b)
        except self.dbapi_exceptions.Error, b:
            self.log(1,"ERROR: "+str(b)+msg)
        else:          
            self.set_row(r)
            #simple hack to show the new line
            self.cancel(None)
            if  c.rowcount == 1:
                self.log(3,msg+"\nupdate was succesfull")
            else:
                self.log(3,msg+"\nWARNING update was done on %s rows" % c.rowcount )
        del c
        
    def insert(self,bla):
        r= self.get_row()
        (r, msgs,msg) = checks.check_list_of_checks(self.checks,self,r)
        if r == None:
            self.log(1,msg)
            return
        
        self.db.select_db(self.db_name)
        sql= "INSERT INTO " + self.table_name + " VALUES ( " + " %s, " *  (self.n_fields-1) + "%s )"
                
        
        self.db.select_db(self.db_name)
        self.db.paramstyle='format'
        c=self.db.cursor()
        
        try:
            c.execute(sql ,  r)
        except self.dbapi_exceptions.Warning, b:
            self.log(3,"SQL WARNING: " + str(b)+"\n"+msg)
        except self.dbapi_exceptions.Error, b:
            self.log(3,"SQL ERROR: " + str(b)+"\n"+msg)
        else:
            self.log(1,"INSERT was succeful\n"+msg) 
        del c

    def reset(self, bla):
        self.set_row(self.default_row)

        self.select_navigate_thewidget.set_sensitive(TRUE)
        
        self.set_editable(FALSE)
        self.cursor_del()

        self.button_prev.set_sensitive(FALSE)
        self.button_next.set_sensitive(FALSE)

        self.button_update.set_sensitive(FALSE)
        self.button_edit.set_sensitive(TRUE)
        self.button_insert.set_sensitive(FALSE)

        self.button_cancel.set_sensitive(FALSE)

        self.log(1,"TABLE "+self.table_name)

        


def editor(db, db_name, table_name,
           #api
           dbapi,dbapi_exceptions,
           #these are used for automatic joins
           join_rules=[],table_fields={}):
    x=editor_widget(db, db_name, table_name,
                    #api
                    dbapi,dbapi_exceptions,
                    #
                    join_rules, table_fields)

    window1=GtkWindow(WINDOW_TOPLEVEL)
    window1.set_title("sql:/"+db_name+"/"+table_name)
    #window1.set_usize(-1, -1)
    window1.set_policy(FALSE, TRUE, FALSE)
    
    #window1.connect("destroy", close_window, self)
    #window1.GTKPY_MAIN = self
    
    window1.add(x.widget)
    window1.show()
    return window1

