# -*- coding: utf-8 -*-
#
# (c) Copyright 2003-2009 Hewlett-Packard Development Company, L.P.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
#
# Author: Don Welch
#

# StdLib

# Local
from base.g import *
from ui_utils import *

# Qt
from PyQt4.QtCore import *
from PyQt4.QtGui import *

# Main window
from fabwindow_base import Ui_MainWindow

fax_avail = True
try:
    from fax import fax
except ImportError:
    # This can fail on Python < 2.3 due to the datetime module
    log.error("Fax address book disabled - Python 2.3+ required.")
    fax_avail = False



class FABWindow(QMainWindow,  Ui_MainWindow):
    def __init__(self, parent):
        QMainWindow.__init__(self, parent)
        self.setupUi(self)
        self.group = u'All' # current group
        self.name = None # current name
        self.updating_group = False
        self.updating_name = False

        self.user_settings = UserSettings()
        self.user_settings.load()
        self.user_settings.debug()

        self.initDB()
        self.initUi()

        QTimer.singleShot(0, self.updateUi)


    def initDB(self):
        self.db =  fax.FaxAddressBook()

        # Fixup data from old-style database
        data = self.db.get_all_records()
        for d in data:
            if u'All' not in data[d]['groups']:
                data[d]['groups'].append(u'All')

        if not data:
            self.db.set('__' + utils.gen_random_uuid(), '', '', '', '', [u'All'], '')


    def initUi(self):
        # Application icon
        self.setWindowIcon(QIcon(load_pixmap('hp_logo', '128x128')))

        self.NewGroupAction.setIcon(QIcon(load_pixmap('new_group', '24x24')))
        self.NewGroupFromSelectionAction.setIcon(QIcon(load_pixmap('new_group_from_selection', '24x24')))
        self.RenameGroupAction.setIcon(QIcon(load_pixmap('rename_group', '24x24')))
        self.RemoveGroupAction.setIcon(QIcon(load_pixmap('remove_group', '24x24')))
        self.NewNameAction.setIcon(QIcon(load_pixmap('new_user', '24x24')))
        self.RemoveNameAction.setIcon(QIcon(load_pixmap('remove_user', '24x24')))
        self.AddToGroupAction.setIcon(QIcon(load_pixmap('add_to_group', '24x24')))
        self.RemoveFromGroupAction.setIcon(QIcon(load_pixmap('remove_from_group', '24x24')))

        self.connect(self.QuitAction, SIGNAL("triggered()"), self.close)
        self.connect(self.NewGroupAction, SIGNAL("triggered()"), self.NewGroupAction_triggered)
        self.connect(self.NewGroupFromSelectionAction, SIGNAL("triggered()"), self.NewGroupFromSelectionAction_triggered)
        self.connect(self.RenameGroupAction, SIGNAL("triggered()"), self.RenameGroupAction_triggered)
        self.connect(self.RemoveGroupAction, SIGNAL("triggered()"), self.RemoveGroupAction_triggered)
        self.connect(self.NewNameAction, SIGNAL("triggered()"), self.NewNameAction_triggered)
        self.connect(self.RemoveNameAction, SIGNAL("triggered()"), self.RemoveNameAction_triggered)
        self.connect(self.ImportAction, SIGNAL("triggered()"), self.ImportAction_triggered)
        self.connect(self.RemoveFromGroupAction, SIGNAL("triggered()"), self.RemoveFromGroupAction_triggered)
        self.connect(self.AddToGroupAction, SIGNAL("triggered()"), self.AddToGroupAction_triggered)

        self.GroupTableWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.NameTableWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.connect(self.Splitter, SIGNAL("splitterMoved(int, int)"), self.Splitter_splitterMoved)
        self.Splitter.setChildrenCollapsible(False)
        self.Splitter.setHandleWidth(self.Splitter.handleWidth()+2)

        self.GroupTableWidget.verticalHeader().hide()
        self.GroupTableWidget.setShowGrid(False)
        self.GroupTableWidget.setSelectionMode(QAbstractItemView.SingleSelection)
        self.NameTableWidget.verticalHeader().hide()
        self.NameTableWidget.setShowGrid(False)

        self.NameTableWidget.setDragEnabled(True)
        self.GroupTableWidget.setAcceptDrops(True)
        self.GroupTableWidget.setDropIndicatorShown(True)

        self.connect(self.GroupTableWidget, SIGNAL("itemSelectionChanged()"),
                     self.GroupTableWidget_itemSelectionChanged)
        self.connect(self.NameTableWidget, SIGNAL("itemSelectionChanged()"),
                     self.NameTableWidget_itemSelectionChanged)
        self.connect(self.NameLineEdit, SIGNAL("editingFinished()"),
                     self.NameLineEdit_editingFinished)
        self.connect(self.FaxNumberLineEdit, SIGNAL("editingFinished()"),
                     self.FaxNumberLineEdit_editingFinished)
        self.connect(self.NotesTextEdit, SIGNAL("textChanged()"),
                     self.NotesTextEdit_textChanged)
        self.connect(self.NotesTextEdit, SIGNAL("editingFinished()"),
                     self.NotesTextEdit_editingFinished)
        self.connect(self.GroupTableWidget, SIGNAL("namesAddedToGroup"),
                     self.GroupTableWidget_namesAddedToGroup)

        self.FaxNumberLineEdit.setValidator(PhoneNumValidator(self.FaxNumberLineEdit))
        self.NameLineEdit.setValidator(AddressBookNameValidator(self.db, self.NameLineEdit))

        self.GroupTableWidget.setContextMenuPolicy(Qt.ActionsContextMenu)
        self.GroupTableWidget.addAction(self.NewGroupAction)
        self.GroupTableWidget.addAction(self.NewGroupFromSelectionAction)
        self.GroupTableWidget.addAction(self.RenameGroupAction)
        self.GroupTableWidget.addAction(self.RemoveGroupAction)

        self.NameTableWidget.setContextMenuPolicy(Qt.ActionsContextMenu)
        self.NameTableWidget.addAction(self.NewNameAction)
        self.NameTableWidget.addAction(self.AddToGroupAction)
        self.NameTableWidget.addAction(self.RemoveFromGroupAction)
        self.NameTableWidget.addAction(self.RemoveNameAction)
        self.NameTableWidget.addAction(self.NewGroupFromSelectionAction)

        self.GroupTableWidget.setDatabase(self.db)


    def updateUi(self):
        if not fax_avail:
            FailureUI(self, self.__tr("<b>Fax support disabled.</b><p>Fax support requires Python 2.3."))
            self.close()
            return

        self.updateGroupList()
        self.updateNameList()
        self.updateDetailsFrame()


    def closeEvent(self, e):
        #self.NameLineEdit.emit(SIGNAL("editingFinished()"))
        self.FaxNumberLineEdit.emit(SIGNAL("editingFinished()"))
        self.NotesTextEdit.emit(SIGNAL("editingFinished()"))
        e.accept()


    def Splitter_splitterMoved(self, pos, index):
        self.GroupTableWidget.setColumnWidth(0, self.GroupTableWidget.width())
        self.NameTableWidget.setColumnWidth(0, self.NameTableWidget.width())


    def updateGroupList(self):
        self.updating_group = True
        all, k = None, None
        try:
            headerItem = QTableWidgetItem()
            headerItem.setText(self.__tr("Group"))
            self.GroupTableWidget.clear()
            self.GroupTableWidget.setColumnCount(1)
            self.GroupTableWidget.setHorizontalHeaderItem(0, headerItem)
            self.GroupTableWidget.setColumnWidth(0, self.GroupTableWidget.width())

            groups = self.db.get_all_groups()
            groups.sort()
            self.GroupTableWidget.setRowCount(len(groups))

            # Force All group to top of table
            all = QTableWidgetItem(self.__tr("All"))
            all.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.GroupTableWidget.setItem(0, 0, all)

            j = 1
            for g in groups:
                if g == u'All':
                    continue

                i = QTableWidgetItem(QString(g))

                if g == self.group:
                    k = i

                i.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDropEnabled)
                self.GroupTableWidget.setItem(j, 0, i)
                j += 1


        finally:
            self.updating_group = False

            if k is not None:
                k.setSelected(True)

            elif all is not None:
                all.setSelected(True)


    def GroupTableWidget_itemSelectionChanged(self):
        if not self.updating_group:
            selected_items = self.GroupTableWidget.selectedItems()
            if selected_items:
                self.group = unicode(selected_items[0].text())
                self.RemoveGroupAction.setEnabled(self.group != u'All')
                self.RenameGroupAction.setEnabled(self.group != u'All')
            else: # shouldn't happen?!
                self.RemoveGroupAction.setEnabled(False)
                self.RenameGroupAction.setEnabled(False)
                self.group = None

            self.updateNameList()


    def NameTableWidget_itemSelectionChanged(self):
        if not self.updating_name:
            selected_items = self.NameTableWidget.selectedItems()
            num_selected_items = len(selected_items)

            if num_selected_items == 0:
                self.name = None
                self.RemoveNameAction.setEnabled(False)
                self.NewGroupFromSelectionAction.setEnabled(False)
                self.RemoveFromGroupAction.setEnabled(False)
                self.AddToGroupAction.setEnabled(False)

            elif num_selected_items == 1:
                self.name = unicode(selected_items[0].text())
                self.RemoveNameAction.setEnabled(True)
                self.NewGroupFromSelectionAction.setEnabled(True)

                self.RemoveFromGroupAction.setEnabled(self.group != u'All')
                self.AddToGroupAction.setEnabled(True) #self.group != u'All')

            else: # > 1
                self.RemoveNameAction.setEnabled(True)
                self.NewGroupFromSelectionAction.setEnabled(True)
                self.RemoveFromGroupAction.setEnabled(self.group != u'All')
                self.AddToGroupAction.setEnabled(True) #self.group != u'All')
                self.name = None

            self.updateDetailsFrame()


    def updateNameList(self):
        self.updating_name = True
        m, k = None, None
        try:
            headerItem = QTableWidgetItem()
            headerItem.setText(self.__tr("Name"))
            self.NameTableWidget.clear()
            self.NameTableWidget.setColumnCount(1)
            self.NameTableWidget.setHorizontalHeaderItem(0,headerItem)
            self.NameTableWidget.setColumnWidth(0, self.NameTableWidget.width())

            names = self.db.group_members(self.group)
            filtered_names = [n for n in names if not n.startswith('__')]
            filtered_names.sort()
            self.NameTableWidget.setRowCount(len(filtered_names))

            for j, n in enumerate(filtered_names):
                i = QTableWidgetItem(QString(n))
                i.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
                self.NameTableWidget.setItem(j, 0, i)

                if n == self.name:
                    m = i

                if j == 0:
                    k = i

        finally:
            self.updating_name = False

            if m is not None:
                m.setSelected(True)

            elif k is not None:
                k.setSelected(True)

            else: # no names, disable name frame and name actions
                self.name = None
                self.RemoveNameAction.setEnabled(False)
                self.NewGroupFromSelectionAction.setEnabled(False)
                self.RemoveFromGroupAction.setEnabled(False)
                self.AddToGroupAction.setEnabled(False)
                self.updateDetailsFrame()


    def selectByName(self, name):
        rows = self.NameTableWidget.rowCount()
        for r in range(rows):
            i = self.NameTableWidget.item(r, 0)
            i.setSelected(name == unicode(i.text()))


    def updateDetailsFrame(self):
        if self.name is None:
            self.NameFrame.setEnabled(False)
            self.NameLineEdit.setText(QString())
            self.FaxNumberLineEdit.setText(QString())
            self.NotesTextEdit.setText(QString())

        else:
            self.NameFrame.setEnabled(True)
            data = self.db.get(self.name)
            self.NameLineEdit.setText(self.name)
            self.FaxNumberLineEdit.setText(data['fax'])
            self.NotesTextEdit.setText(data['notes'])


    def NameLineEdit_editingFinished(self):
        if self.name is not None:
            new_name = unicode(self.NameLineEdit.text())
            if new_name != self.name:
                if QMessageBox.question(self, self.__tr("Rename?"), self.__tr("Rename '%1' to '%2'?").arg(self.name).arg(new_name), \
                                        QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:

                    self.db.rename(self.name, new_name)
                    log.debug("Rename %s to %s" % (self.name, new_name))
                    self.emit(SIGNAL("databaseChanged"), FAB_NAME_RENAME, self.name, new_name)
                    self.updateNameList()
                else:
                    self.NameLineEdit.setText(self.name)


    def FaxNumberLineEdit_editingFinished(self):
        if self.name is not None:
            self.db.set_key_value(self.name, 'fax', unicode(self.FaxNumberLineEdit.text()))
            self.emit(SIGNAL("databaseChanged"), FAB_NAME_DETAILS_CHANGED, self.name)


    def NotesTextEdit_textChanged(self):
        if self.name is not None:
            self.db.set_key_value(self.name, 'notes', unicode(self.NotesTextEdit.document().toPlainText()))


    def NotesTextEdit_editingFinished(self):
        if self.name is not None:
            self.emit(SIGNAL("databaseChanged"), FAB_NAME_DETAILS_CHANGED, self.name)


    def NewGroupAction_triggered(self):
        ok = False
        g, ok = QInputDialog.getText(self, self.__tr("Enter New Group Name"), self.__tr("Name for New Group:"))
        g = unicode(g)

        if g == u'All':
            FailureUI(self, self.__tr("<b>Sorry, the group name cannot be 'All'.</b><p>Please choose a different name."))
            ok = False

        if ok:
            self.db.set('__' + utils.gen_random_uuid(), '', '', '', '', [u'All', g], '')
            self.group = g
            log.debug("New empty group %s" % self.group)
            self.emit(SIGNAL("databaseChanged"), FAB_GROUP_ADD, self.group)
            self.updateGroupList()


    def NewGroupFromSelectionAction_triggered(self):
        selected_names = [unicode(n.text()) for n in self.NameTableWidget.selectedItems()]
        if selected_names:
            ok = False
            g, ok = QInputDialog.getText(self, self.__tr("Enter New Group Name"), self.__tr("Name for New Group:"))
            g = unicode(g)

            groups = self.db.get_all_groups()

            if g in groups:
                FailureUI(self, self.__tr("<b>Sorry, the group name cannot be the same as an existing group (or 'All').</b><p>Please choose a different name."))
                ok = False

            if ok:
                self.db.update_groups(g, selected_names)
                self.group = g
                log.debug("New group %s with names %s" % (self.group, ','.join(selected_names)))
                self.emit(SIGNAL("databaseChanged"), FAB_GROUP_ADD, self.group)
                self.updateGroupList()


    def RenameGroupAction_triggered(self):
        selected_items = self.GroupTableWidget.selectedItems()
        if selected_items:
            old_group = unicode(selected_items[0].text())
            ok = False
            new_group, ok = QInputDialog.getText(self, self.__tr("Rename Group"), self.__tr("New Name for Group '%1':").arg(old_group))
            new_group = unicode(new_group)
            groups = self.db.get_all_groups()

            if new_group in groups:
                FailureUI(self, self.__tr("<b>Sorry, the group name cannot be the same as an existing group (or 'All').</b><p>Please choose a different name."))
                ok = False

            if ok:
                self.db.rename_group(old_group, new_group)
                log.debug("Rename group %s to %s" % (old_group, new_group))
                self.emit(SIGNAL("databaseChanged"), FAB_GROUP_RENAME, old_group, new_group)
                self.group = new_group
                self.updateGroupList()


    def RemoveGroupAction_triggered(self):
        self.db.delete_group(self.group)
        log.debug("Remove group %s" % self.group)
        self.emit(SIGNAL("databaseChanged"), FAB_GROUP_REMOVE, self.group)
        self.group = None
        self.updateGroupList()


    def NewNameAction_triggered(self):
        ok = False
        t, ok = QInputDialog.getText(self, self.__tr("Enter New Name"), self.__tr("New Name:"))
        if ok:
            t = unicode(t)
            self.addName(t)


    def addName(self, name, fax=''):
        if self.group == u'All':
            g = [u'All']
        else:
            g = [u'All', self.group]

        self.db.set(name, '', '', '', fax, g, '')
        self.name = name
        log.debug("New name %s" % self.name)
        self.emit(SIGNAL("databaseChanged"), FAB_NAME_ADD, self.name)
        self.updateNameList()


    def RemoveNameAction_triggered(self):
        selected_names = [unicode(n.text()) for n in self.NameTableWidget.selectedItems()]
        if selected_names:
            for n in selected_names:
                self.db.delete(n)
                log.debug("Removing name %s" % n)
                self.emit(SIGNAL("databaseChanged"), FAB_NAME_REMOVE, n)

            self.name = None
            self.updateNameList()


    def RemoveFromGroupAction_triggered(self):
        selected_names = [unicode(n.text()) for n in self.NameTableWidget.selectedItems()]
        if selected_names:
            log.debug("%s leaving group %s" % (','.join(selected_names), self.group))
            self.db.remove_from_group(self.group, selected_names)
            self.emit(SIGNAL("databaseChanged"), FAB_GROUP_MEMBERSHIP_CHANGED, self.group)
            self.name = None
            self.updateGroupList()


    def GroupTableWidget_namesAddedToGroup(self, row, items): # drag n' drop handler
        self.group = unicode(self.GroupTableWidget.item(row, 0).text())
        self.db.add_to_group(self.group, items)
        log.debug("Adding %s to group %s" % (','.join(items), self.group))
        self.emit(SIGNAL("databaseChanged"), FAB_GROUP_MEMBERSHIP_CHANGED, self.group)
        self.updateGroupList()


    def AddToGroupAction_triggered(self):
        selected_names = [unicode(n.text()) for n in self.NameTableWidget.selectedItems()]
        if selected_names:
            ok = False
            all_groups = self.db.get_all_groups()

            if all_groups:
                all_groups = [g for g in all_groups if g != u'All']
                all_groups.sort()

                dlg = JoinDialog(self, all_groups)

                if dlg.exec_() == QDialog.Accepted:
                    group = dlg.group
                    if group:
                        self.db.add_to_group(group, selected_names)
                        self.group = group
                        log.debug("Adding %s to group %s" % (','.join(selected_names), self.group))
                        self.emit(SIGNAL("databaseChanged"), FAB_GROUP_MEMBERSHIP_CHANGED, self.group)
                        self.updateGroupList()

            else:
                FailureUI(self, self.__tr("<b>There are no groups to join.</b><p>Use <i>New Group from Selection</i> to create a new group using these name(s)."))


    def ImportAction_triggered(self):
        result = unicode(QFileDialog.getOpenFileName(self,
                         self.__tr("Import fax addresses from LDIF or vCard"),
                         #user_conf.workingDirectory(),
                         self.user_settings.working_dir,
                         "vCard (*.vcf);;LDIF (*.ldif *.ldi)"))

        if result:
            working_directory = unicode(os.path.dirname(result))
            log.debug("result: %s" % result)
            #user_conf.setWorkingDirectory(working_directory)
            self.user_settings.working_dir = working_directory
            self.user_settings.save()

            if result:
                if result.endswith('.vcf'):
                    ok, error_str = self.db.import_vcard(result)
                else:
                    ok, error_str = self.db.import_ldif(result)

                if not ok:
                    FailureUI(self, error_str)

                else:
                    self.updateUi()


    def __tr(self,s,c = None):
        return qApp.translate("FABWindow",s,c)




class JoinDialog(QDialog):
    def __init__(self, parent, groups):
        QDialog.__init__(self, parent)
        self.group = ''
        self.setupUi(groups)


    def setupUi(self, groups):
        self.setObjectName("Dialog")
        self.resize(QSize(QRect(0,0,271,107).size()).expandedTo(self.minimumSizeHint()))

        self.gridlayout = QGridLayout(self)
        self.gridlayout.setObjectName("gridlayout")

        self.hboxlayout = QHBoxLayout()
        self.hboxlayout.setObjectName("hboxlayout")

        self.label = QLabel(self)
        self.label.setObjectName("label")
        self.hboxlayout.addWidget(self.label)

        self.GroupJoinComboBox = QComboBox(self)

        sizePolicy = QSizePolicy(QSizePolicy.Expanding,QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.GroupJoinComboBox.sizePolicy().hasHeightForWidth())
        self.GroupJoinComboBox.setSizePolicy(sizePolicy)
        self.GroupJoinComboBox.setObjectName("comboBox")
        self.hboxlayout.addWidget(self.GroupJoinComboBox)
        self.gridlayout.addLayout(self.hboxlayout,0,0,1,3)

        spacerItem = QSpacerItem(20,40,QSizePolicy.Minimum,QSizePolicy.Expanding)
        self.gridlayout.addItem(spacerItem,1,0,1,1)

        spacerItem1 = QSpacerItem(231,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
        self.gridlayout.addItem(spacerItem1,2,0,1,1)

        self.JoinButton = QPushButton(self)
        self.JoinButton.setObjectName("pushButton_2")
        self.gridlayout.addWidget(self.JoinButton,2,1,1,1)

        self.CancelButton = QPushButton(self)
        self.CancelButton.setObjectName("pushButton")
        self.gridlayout.addWidget(self.CancelButton,2,2,1,1)

        self.connect(self.GroupJoinComboBox, SIGNAL("currentIndexChanged(int)"),
            self.GroupJoinComboBox_currentIndexChanged)

        for i, g in enumerate(groups):
            if i == 0:
                self.group = g
            self.GroupJoinComboBox.insertItem(i, g)

        self.connect(self.JoinButton, SIGNAL("clicked()"), self.accept)
        self.connect(self.CancelButton, SIGNAL("clicked()"), self.reject)

        self.retranslateUi()


    def GroupJoinComboBox_currentIndexChanged(self, i):
        self.group = unicode(self.GroupJoinComboBox.currentText())


    def retranslateUi(self):
        self.setWindowTitle(QApplication.translate("Dialog", "Join Group", None, QApplication.UnicodeUTF8))
        self.label.setText(QApplication.translate("Dialog", "Group to Join:", None, QApplication.UnicodeUTF8))
        self.JoinButton.setText(QApplication.translate("Dialog", "Join", None, QApplication.UnicodeUTF8))
        self.CancelButton.setText(QApplication.translate("Dialog", "Cancel", None, QApplication.UnicodeUTF8))
