#!/usr/bin/env python
# -*- coding: utf-8 -*-

from sqlobject import connectionForURI, SQLObject, DateTimeCol, FloatCol, \
     StringCol

from datetime import datetime

# some monkey patching
import gtk
gtk.STOCK_EDIT = gtk.STOCK_CONVERT

from Kiwi2 import Delegates
from Kiwi2.initgtk import gtk, quit_if_last

# database connection
__connection__ = connectionForURI('postgres:/incidencias')
#__connection__.debug = 1

class AddEditDialog(Delegates.Delegate):

    def __init__(self, parent, slave, width=-1, height=-1):
        self.toplevel = gtk.Dialog(parent=parent,
                                   flags=(gtk.DIALOG_MODAL |
                                          gtk.DIALOG_NO_SEPARATOR |
                                          gtk.DIALOG_DESTROY_WITH_PARENT),
                                   buttons=(gtk.STOCK_CANCEL,
                                            gtk.RESPONSE_CANCEL,
                                            gtk.STOCK_OK,
                                            gtk.RESPONSE_OK))
        self.toplevel.set_default_size(width, height)

        self.placeholder = gtk.EventBox()
        self.toplevel.vbox.pack_start(self.placeholder)
        self.toplevel.set_default_response(gtk.RESPONSE_OK)
        
        Delegates.Delegate.__init__(self, toplevel=self.toplevel,
                                    delete_handler=self.only_hide)
        self.slave = slave
        self.attach_slave('placeholder', slave)

        self.ok = self.toplevel.action_area.get_children()[0]
        
    def only_hide(self, window, event):
        """This handler avoid the dialog to be destroyed when clicking in the
        'x' button."""
        self.toplevel.hide()

    def run(self, title=''):
        self.toplevel.set_title(title)
        result = self.toplevel.run()
        self.toplevel.hide()
        return result

    def validate(self, valid):
        self.ok.set_sensitive(valid)
        
class Message(gtk.MessageDialog):
    "Base class for several message boxes"
    def __init__(self, parent, msg, mtype, buttons):
        gtk.MessageDialog.__init__(self, parent,
                                   (gtk.DIALOG_MODAL |
                                    gtk.DIALOG_DESTROY_WITH_PARENT),
                                   mtype, buttons, msg)
        
    def run(self):
        result = gtk.MessageDialog.run(self)
        self.destroy()
        return result
    
class YesNoQuestion(Message):
    "Message box that present a question with two possible answer: yes and no"
    def __init__(self, parent, question):
        Message.__init__(self, parent, question,
                         gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO)

# Delegates
class CommonDelegate(Delegates.Delegate):
    def __init__(self, gladefile, widgets):
        Delegates.Delegate.__init__(self, gladefile=gladefile, widgets=widgets)
        self.proxy = self.add_proxy(None, self.widgets)

    def on_attach(self, parent):
        self.register_validate_function(parent.validate)


class ActivityReportDelegate(CommonDelegate):
    def __init__(self, companies=[]):
        CommonDelegate.__init__(self, 'activityreport.glade',
                                ['worker', 'client', 'project', 'date', 'time',
                                 'description'])
        self.client.prefill(companies)

class SupportRequestDelegate(CommonDelegate):
    def __init__(self, companies=[], reporters=[]):
        CommonDelegate.__init__(self, 'supportrequest.glade',
                                ['title', 'description', 'client', 'email',
                                 'date', 'reporter', 'subject', 'severity',
                                 'answer', 'answer_title', 'answer_date',
                                 'assigned_to', 'priority', 'state'])
        self.client.prefill(companies)
        self.reporter.prefill(reporters)
        self.severity.prefill(('Leve', 'Normal', 'Grave'))
        self.state.prefill(('Sin resolver', 'Resuelta', 'Pendiente'))
        self.priority.prefill(('Baja', 'Media', 'Alta'))

class PersonDelegate(CommonDelegate):
    def __init__(self):
        CommonDelegate.__init__(self, 'person.glade',
                                ['company', 'name', 'email'])

# Domain classes
def clone_model(src, dst):
    """Copy src attributes into dst.

    This function only works with SQLObject models"""
    props = [p[0] for p in src._reprItems()]
    d = {}
    for p in props:
        d[p] = getattr(src, p)

    dst.set(**d)

class ActivityReport(SQLObject):
    worker      = StringCol(default='')
    client      = StringCol(default='Sicem')
    project     = StringCol(default='')
    date        = DateTimeCol(default=datetime.now)
    time        = FloatCol(default=0)
    description = StringCol(default='')

    def __str__(self):
        return "%s[%d] %s - %s - %s" % (self.__class__.__name__, self.id,
                                        self.date, self.worker, self.client)
    
ActivityReport.createTable(ifNotExists=True)

class Person(SQLObject):
    company = StringCol(default='')
    name    = StringCol(default='')
    email   = StringCol(default='')

    def __str__(self):
        return "%s[%d] %s - %s" % (self.__class__.__name__, self.id,
                                   self.name, self.company)
    
Person.createTable(ifNotExists=True)

class SupportRequest(SQLObject):
    title        = StringCol(default='')
    client       = StringCol(default='Sicem')
    date         = DateTimeCol(default=datetime.now)
    severity     = StringCol(default='Leve')
    subject      = StringCol(default='')
    description  = StringCol(default='')
    answer_date  = DateTimeCol(default=None)
    answer_title = StringCol(default='')
    answer       = StringCol(default='')
    state        = StringCol(default='Sin resolver')
    priority     = StringCol(default='Media')
    assigned_to  = StringCol(default='')
    reporter     = StringCol(default=None)
    email        = StringCol(default='')

    def __str__(self):
        return "%s[%d] %s" % (self.__class__.__name__, self.id,
                              self.title)

SupportRequest.createTable(ifNotExists=True)
    
class Application(Delegates.Delegate):

    def __init__(self):
        Delegates.Delegate.__init__(self, gladefile='mainwindow.glade',
                                    widgets=('add', 'edit', 'remove', 'quit',
                                             'notebook',
                                             'activityreports',
                                             'supportrequests',
                                             'people', 'search'),
                                    delete_handler=quit_if_last)


        self.load_data()
        self.show_all()
        
        # setup the add/edit dialog for activity reports
        activity_report_delegate = ActivityReportDelegate(self.companies)
        self.activity_report_dialog = AddEditDialog(self.toplevel,
                                                    activity_report_delegate,
                                                    400, -1)

        # setup the add/edit dialog for support requests
        support_request_delegate = SupportRequestDelegate(self.companies,
                                                          self.reporters)
        self.support_request_dialog = AddEditDialog(self.toplevel,
                                                    support_request_delegate,
                                                    -1, 500)

        # setup the add/edit dialog for people
        person_delegate = PersonDelegate()
        self.person_dialog = AddEditDialog(self.toplevel, person_delegate)
        
    def load_data(self):
        self.activityreports.add_list(ActivityReport.select())

        support_requests = SupportRequest.select()
        self.supportrequests.add_list(support_requests)
        self.reporters = []
        for s in support_requests:
            if s.reporter and s.reporter not in self.reporters:
                self.reporters.append(s.reporter)
                
        people = Person.select()
        self.companies = []
        for p in people:
            if p.company and p.company not in self.companies:
                self.companies.append(p.company)

        self.people.add_list(people)
        
    def get_active_list(self):
        """Return a tuple with the active list, its matching  dialog and a
        factory to create objects for that list"""
        ACTIVITY_REPORTS, SUPPORT_REQUESTS, PEOPLE = range(3)
        page_map = {ACTIVITY_REPORTS: (self.activityreports,
                                       self.activity_report_dialog,
                                       ActivityReport),
                    SUPPORT_REQUESTS: (self.supportrequests,
                                       self.support_request_dialog,
                                       SupportRequest),
                    PEOPLE: (self.people,
                             self.person_dialog,
                             Person)
                    }
        return page_map[self.notebook.get_current_page()]
    
    # action handlers
    def on_quit__activate(self, action):
        gtk.main_quit()
        
    def on_add__activate(self, action):
        list_widget, dialog, factory = self.get_active_list()
        
        model = factory()
        delegate = dialog.slave
        delegate.proxy.new_model(model)
        result = dialog.run('Add %s' % factory.__name__)

        if result == gtk.RESPONSE_OK:
            list_widget.add_instance(model)
        else:
            factory.delete(model.id)

    def on_edit__activate(self, action):
        list_widget, dialog, factory = self.get_active_list()

        selected = list_widget.get_selected()
        if not selected:
            return

        if len(selected) > 1:
            raise ValueError("You can not edit more than one object "
                             "at the same time")

        model = selected[0]
        old_model = factory()
        clone_model(model, old_model)
        delegate = dialog.slave
        delegate.proxy.new_model(model)
        result = dialog.run('Edit %s' % factory.__name__)

        # revert changes if the user cancels the action
        if result == gtk.RESPONSE_OK:
            list_widget.update_instance(model)
        else:
            clone_model(old_model, model)

        factory.delete(old_model.id)
        
    def on_remove__activate(self, action):
        list_widget, dialog, factory = self.get_active_list()

        selected = list_widget.get_selected()
        if not selected:
            return

        for obj in selected:
            if gtk.RESPONSE_YES == YesNoQuestion(self.toplevel,
                                                 "Are you sure you want to remove "
                                                 "the object %s ?"
                                                 % obj).run():
                list_widget.remove_instance(obj)
                factory.delete(obj.id)

    def on_search__changed(self, entry):
        """Filter the active list"""
        list_widget, dialog, factory = self.get_active_list()
        text = entry.get_text()
        if text == '':
            items = factory.select()
        else:
            sql = ' OR '.join(["%s ILIKE '%%%s%%'" % (c.name, text) \
                               for c in factory._SO_columns])
            items = factory.select(sql)
        list_widget.clear()
        list_widget.add_list(items)
        
if __name__ == '__main__':
    app = Application()
    gtk.main()
