# -*- coding: utf-8 -*-
#
# (c) Copyright 2001-2006 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
#
# Authors: Don Welch


from qt import *
from printerform_base import PrinterForm_base
from base.g import *
from base.codes import *
from base import utils, device, magic
from prnt import cups
import glob


class RangeValidator(QValidator):
    def __init__(self, parent=None, name=None):
        QValidator.__init__(self, parent, name)

    def validate(self, input, pos):
        for x in str(input)[pos-1:]:
            if x not in '0123456789,-':
                return QValidator.Invalid, pos

        return QValidator.Acceptable, pos


class PrinterForm(PrinterForm_base):

    def __init__(self, sock, bus='cups', device_uri=None, printer_name=None, args=None, 
                 parent=None,name=None,modal=0,fl=0):

        PrinterForm_base.__init__(self,parent,name,modal,fl)
        self.sock = sock
        self.device_uri = device_uri
        self.printer_name = printer_name
        self.file_list = []
        self.auto_duplex_button_group = 0
        self.orientation_button_group = 0
        self.pages_button_group = 0
        self.init_failed = False

        self.pageRangeEdit.setValidator(RangeValidator(self.pageRangeEdit))

        icon = QPixmap(os.path.join(prop.image_dir, 'HPmenu.png'))
        self.setIcon(icon)
        pix = QPixmap(os.path.join(prop.image_dir, 'folder_open.png'))
        self.addFileButton.setPixmap(pix)
        pix = QPixmap(os.path.join(prop.image_dir, 'folder_remove.png'))
        self.delFileButton.setPixmap(pix)
        pix = QPixmap(os.path.join(prop.image_dir, 'status_refresh.png'))
        self.refreshToolButton.setPixmap(pix)
        self.fileListView.setSorting(-1)

        self.allowable_mime_types = cups.getAllowableMIMETypes()
        log.debug(self.allowable_mime_types)

        self.MIME_TYPES_DESC = \
        {
            "application/pdf" : self.__tr("PDF Document"),
            "application/postscript" : self.__tr("Postscript Document"),
            "application/vnd.hp-HPGL" : self.__tr("HP Graphics Language File"),
            "application/x-cshell" : self.__tr("C Shell Script"),
            "application/x-perl" : self.__tr("Perl Script"),
            "application/x-python" : self.__tr("Python Program"),
            "application/x-shell" : self.__tr("Shell Script"),
            "text/plain" : self.__tr("Plain Text"),
            "text/html" : self.__tr("HTML Dcoument"),
            "image/gif" : self.__tr("GIF Image"),
            "image/png" : self.__tr("PNG Image"),
            "image/jpeg" : self.__tr("JPEG Image"),
            "image/tiff" : self.__tr("TIFF Image"),
            "image/x-bitmap" : self.__tr("Bitmap (BMP) Image"),
            "image/x-photocd" : self.__tr("Photo CD Image"),
            "image/x-portable-anymap" : self.__tr("Portable Image (PNM)"),
            "image/x-portable-bitmap" : self.__tr("Portable B&W Image (PBM)"),
            "image/x-portable-graymap" : self.__tr("Portable Grayscale Image (PGM)"),
            "image/x-portable-pixmap" : self.__tr("Portable Color Image (PPM)"),
            "image/x-sgi-rgb" : self.__tr("SGI RGB"),
            "image/x-xbitmap" : self.__tr("X11 Bitmap (XBM)"),
            "image/x-xpixmap" : self.__tr("X11 Pixmap (XPM)"),
            "image/x-sun-raster" : self.__tr("Sun Raster Format"),
        }

        if args is not None:
            for f in args:
                self.addFile(f)

        icon = QPixmap(os.path.join(prop.image_dir, 'HPmenu.png'))
        self.setIcon(icon)

        pix = QPixmap(os.path.join(prop.image_dir, 'folder_open.png'))
        self.addFileButton.setPixmap(pix)

        pix = QPixmap(os.path.join(prop.image_dir, 'folder_remove.png'))
        self.delFileButton.setPixmap(pix)

        pix = QPixmap(os.path.join(prop.image_dir, 'status_refresh.png'))
        self.refreshToolButton.setPixmap(pix)

        self.fileListView.setSorting(-1)


        if self.device_uri and self.printer_name:
            log.error("You may not specify both a printer (-p) and a device (-d).")
            self.FailureUI(self.__tr("<p><b>You may not specify both a printer (-p) and a device (-d)."))
            self.device_uri, self.printer_name = None, None
            self.init_failed = True

        self.cups_printers = cups.getPrinters()
        log.debug(self.cups_printers)

        if not self.device_uri and not self.printer_name:
            t = device.probeDevices(self.sock, bus=bus, filter='none')
            probed_devices = []
            
            for d in t:
                if d.startswith('hp:'):
                    probed_devices.append(d)
            
            log.debug(probed_devices)

            max_deviceid_size, x, devices = 0, 0, {}

            for d in probed_devices:
                printers = []
                for p in self.cups_printers:
                    if p.device_uri == d:
                        printers.append(p.name)
                devices[x] = (d, printers)
                x += 1
                max_deviceid_size = max(len(d), max_deviceid_size)

            if x == 0:
                from nodevicesform import NoDevicesForm
                self.FailureUI(self.__tr("<p><b>No devices found.</b><p>Please make sure your device is properly installed and try again."))
                self.init_failed = True

            elif x == 1:
                log.info(utils.bold("Using device: %s" % devices[0][0]))
                self.device_uri = devices[0][0]


            else:
                from chooseprinterdlg import ChoosePrinterDlg
                dlg = ChoosePrinterDlg(self.cups_printers)
                
                if dlg.exec_loop() == QDialog.Accepted:
                    self.device_uri = dlg.device_uri
                else:
                    self.init_failed = True


        QTimer.singleShot(0, self.InitialUpdate)


    def InitialUpdate(self):
        if self.init_failed:
            #log.error("Init failed")
            self.close()
            return        

        self.printer_list = []

        self.dev = device.Device(device_uri=self.device_uri, 
                                 printer_name=self.printer_name, 
                                 hpssd_sock=self.sock)

        self.device_uri = self.dev.device_uri

        log.debug(self.device_uri)
        self.DeviceURIText.setText(self.device_uri)

        for p in self.cups_printers:
            if p.device_uri == self.device_uri:
                self.printer_list.append(p.name)

        for p in self.printer_list:
            self.printerNameComboBox.insertItem(p)

        self.UpdatePrinterStatus()

        if self.printer_name is None:
            self.printerNameComboBox.setCurrentItem(0)

        elif self.printer_name in self.printer_list:
            self.printerNameComboBox.setCurrentText(self.printer_name)

        self.current_printer = str(self.printerNameComboBox.currentText())

        self.UpdatePrinterInfo()

    def UpdatePrinterStatus(self):
        QApplication.setOverrideCursor(QApplication.waitCursor)
        
        try:
            try:
                self.dev.open()
            except Error, e:
                log.warn(e.msg)

            try:
                self.dev.queryDevice(quick=True)
            except Error, e:
                log.error("Query device error (%s)." % e.msg)
                self.dev.error_state = ERROR_STATE_ERROR

        finally:
            self.dev.close()
            QApplication.restoreOverrideCursor()
        
        
        if self.dev.device_state == DEVICE_STATE_NOT_FOUND:
            self.FailureUI(self.__tr("<b>Unable to communicate with device:</b><p>%s" % self.device_uri))

        try:
            self.StateText.setText(self.dev.status_desc)
        except AttributeError:
            pass
        


    def EventUI(self, event_code, event_type, error_string_short,
                error_string_long, retry_timeout, job_id,
                device_uri):

        log.debug("Event: device_uri=%s code=%d type=%s string=%s timeout=%d id=%d uri=%s" %
                 (device_uri, event_code, event_type,  
                  error_string_short, retry_timeout, job_id, device_uri))

        if device_uri == self.dev.device_uri:
            self.StateText.setText(error_string_short)


    def addFile(self, path):
        path = os.path.realpath(path)
        if os.path.exists(path):
            mime_type = magic.mime_type(path)
            mime_type_desc = mime_type

            try:
                mime_type_desc = self.MIME_TYPES_DESC[mime_type]
            except KeyError:
                self.WarningUI(self.__tr("<b>You are trying to add a file that cannot be directly printed with this utility.</b><p>To print this file, use the print command in the application that created it."))
            else:
                log.debug("Adding file %s (%s,%s)" % (path, mime_type, mime_type_desc))
                self.file_list.append((path, mime_type, mime_type_desc))
        else:
            self.FailureUI(self.__tr("<b>Unable to add file '%s' to file list.</b><p>Check the file name and try again." % path))

        self.UpdateFileList()

    def UpdateFileList(self):
        self.fileListView.clear()
        temp = self.file_list[:]
        temp.reverse()

        for p, t, d in temp:
            i = QListViewItem(self.fileListView, os.path.basename(p), d, p)
            #self.fileListView.setSelected( i, True )

        non_empty_file_list = self.fileListView.childCount() > 0
        ##self.delFileButton.setEnabled( non_empty_file_list )
        self.printPushButton.setEnabled(non_empty_file_list)

    def addFileButton_clicked(self):
        self.setFocus()

        log.debug("isTopLevel %d" % self.isTopLevel())
        log.debug("hasFocus %d" % self.hasFocus())
        log.debug("isEnabled %d" % self.isEnabled())

        workingDirectory = os.path.expanduser("~")

        log.debug("workingDirectory: %s" % workingDirectory)

        dlg = QFileDialog(workingDirectory, QString.null, None, None, True)

        dlg.setCaption("openfile")
        dlg.setMode(QFileDialog.ExistingFile)
        dlg.show()

        if dlg.exec_loop() == QDialog.Accepted:
                results = dlg.selectedFile()
                workingDirectory = dlg.url()
                log.debug("results: %s" % results)
                log.debug("workingDirectory: %s" % workingDirectory)

                if results:
                    self.addFile(str(results))



    def delFileButton_clicked(self):
        try:
            path = self.fileListView.currentItem().text(2)
        except AttributeError:
            return
        else:
            temp = self.file_list[:]
            index = 0
            for p, t, d in temp:
                if p == path:
                    del self.file_list[index]
                    break
                index += 1

            self.UpdateFileList()


    def fileListView_currentChanged(self,item):
        #print item
        pass

    def printerNameComboBox_highlighted(self,a0):
        self.current_printer = str(a0)
        self.UpdatePrinterInfo()

    def UpdatePrinterInfo(self):
        for p in self.cups_printers:
            if p.name == self.current_printer:

                try:
                    self.LocationText.setText(p.location)
                except AttributeError:
                    self.LocationText.setText('')

                try:
                    self.CommentText.setText(p.info)
                except AttributeError:
                    self.CommentText.setText('')

                cups.openPPD(p.name)
                self.UpdateDuplex()
                cups.closePPD()
                break

    def UpdateDuplex(self):
        duplex = cups.getPPDOption("Duplex")
        if duplex is not None:
            if duplex.startswith("long"):
                self.duplexButtonGroup.setButton(1)
                self.auto_duplex_button_group = 1
            elif duplex.startswith("short"):
                self.duplexButtonGroup.setButton(2)
                self.auto_duplex_button_group = 2
            else:
                self.duplexButtonGroup.setButton(0)
                self.auto_duplex_button_group = 0
        else:
            self.duplexButtonGroup.setEnabled(False)


    def pagesButtonGroup_clicked(self,item):
        self.pageRangeEdit.setEnabled(item == 1)

    def printPushButton_clicked(self):
        copies = int(self.copiesSpinBox.value())
        rev = bool(self.reverseCheckBox.isChecked())
        collate = bool(self.collateCheckBox.isChecked())
        all_pages = self.pages_button_group == 0
        page_range = str(self.pageRangeEdit.text())
        page_set = int(self.pageSetComboBox.currentItem())
        nup = int(str(self.nUpComboBox.currentText()))
        mirror = bool(self.mirrorCheckBox.isChecked())

        for p, t, d in self.file_list:
            
            alt_nup = (nup > 1 and t == 'application/postscript' and utils.which('psnup'))
                
            if alt_nup:
                cmd = ' '.join(['psnup', '-%d' % nup, ''.join(['"', p, '"']), '| lp -c -d', self.current_printer])
            else:
                cmd = ' '.join(['lp -c -d', self.current_printer])

            if copies > 1:
                cmd = ' '.join([cmd, '-n%d' % copies])

            if not all_pages and len(page_range) > 0:
                cmd = ' '.join([cmd, '-o page-ranges=%s' % page_range])

            if page_set > 0:
                if page_set == 1:
                    cmd = ' '.join([cmd, '-o page-set=even'])
                else:
                    cmd = ' '.join([cmd, '-o page-set=odd'])

            if rev:
                cmd = ' '.join([cmd, '-o outputorder=reverse'])
                
            if mirror:
                cmd = ' '.join([cmd, '-o mirror'])

            if collate and copies > 1:
                cmd = ' '.join([cmd, '-o Collate=True'])

            if t in ["application/x-cshell",
                     "application/x-perl",
                     "application/x-python",
                     "application/x-shell",
                     "text/plain",]:
                     
                cmd = ' '.join([cmd, '-o prettyprint'])

            if nup > 1 and not alt_nup:
                cmd = ' '.join([cmd, '-o number-up=%d' % nup])

            if self.auto_duplex_button_group == 1: # long
                cmd = ' '.join([cmd, '-o sides=two-sided-long-edge'])
            elif self.auto_duplex_button_group == 2: # short
                cmd = ' '.join([cmd, '-o sides=two-sided-short-edge'])
            else:
                cmd = ' '.join([cmd, '-o sides=one-sided'])

            if self.orientation_button_group == 1:
                cmd = ' '.join([cmd, '-o landscape'])

            if not alt_nup:
                cmd = ''.join([cmd, ' "', p, '"'])

            log.debug("Printing: %s" % cmd)

            if os.system(cmd) != 0:
                log.error("Print command failed.")
                self.FailureUI(self.__tr("Print command failed."))
        
        del self.file_list[:]
        self.UpdateFileList()

    def pagesButtonGroup_clicked(self,a0):
        self.pages_button_group = a0
        self.pageRangeEdit.setEnabled(a0 == 1)


    def duplexButtonGroup_clicked(self,a0):
        self.auto_duplex_button_group = a0

    def orientationButtonGroup_clicked(self,a0):
        self.orientation_button_group = a0

    def refreshToolButton_clicked(self):
        self.UpdatePrinterStatus()


    def SuccessUI(self):
        QMessageBox.information(self,
                             self.caption(),
                             self.__tr("<p><b>The operation completed successfully.</b>"),
                              QMessageBox.Ok,
                              QMessageBox.NoButton,
                              QMessageBox.NoButton)

    def FailureUI(self, error_text):
        QMessageBox.critical(self,
                             self.caption(),
                             error_text,
                              QMessageBox.Ok,
                              QMessageBox.NoButton,
                              QMessageBox.NoButton)

    def WarningUI(self, msg):
        QMessageBox.warning(self,
                             self.caption(),
                             msg,
                              QMessageBox.Ok,
                              QMessageBox.NoButton,
                              QMessageBox.NoButton)


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


