#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# (c) Copyright 2011-2014 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: Amarnath Chitumalla
#

__version__ = '1.0'
__title__ = 'AutoConfig Utility to check queues configuration'
__mod__ = 'hp-daignose-queues'
__doc__ = """Auto config utility for HPLIP supported multifunction Devices to diagnose queues configuration."""

# Std Lib
import sys
import os
import getopt
import commands
import re

# Local
from base.g import *
from base import utils, tui, models
from prnt import cups
from installer import core_install

# ppd type
HPCUPS = 1
HPIJS = 2
HPPS = 3
HPOTHER = 4

DEVICE_URI_PATTERN = re.compile(r"""(.*):/(.*?)/(\S*?)\?(?:serial=(\S*)|device=(\S*)|ip=(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}[^&]*)|zc=(\S+))(?:&port=(\d))?""", re.I)
NICKNAME_PATTERN = re.compile(r'''\*NickName:\s*\"(.*)"''', re.MULTILINE)
NET_PATTERN = re.compile(r"""(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})""")
NET_ZC_PATTERN = re.compile(r'''zc=(.*)''',re.IGNORECASE)
NET_OTHER_PATTERN = re.compile(r'''(.*)://(.*)''',re.IGNORECASE)
USB_PATTERN = re.compile(r'''serial=(.*)''',re.IGNORECASE)
LPSTAT_PATTERN = re.compile(r"""(\S*): (.*)""", re.IGNORECASE)
#BACK_END_PATTERN = re.compile(r'''(.*):(.*)''',re.IGNORECASE)

##### METHODS #####

def usage(typ='text'):
    if typ == 'text':
        utils.log_title(__title__, __version__)
    utils.format_text(USAGE, typ, __title__, __mod__, __version__)
    sys.exit(0)


# Checks 'lp' group is added o not
def check_user_groups():
    result = False
    sts,output = utils.run('groups')
    if sts != 0:
        log.error("Failed to get groups")
    else:
        output = output.rstrip('\r\n')
        log.debug("groups =%s "%output)
        grp_list= output.split(' ')
        cnt = 0
        while cnt < len(grp_list) :
            if grp_list[cnt] == 'lp':
                result = True
                break
            cnt += 1
            
    return result

# This function adds the groups ('lp') to user 
def add_group(core):
    result = False
    add_user_to_group = core.get_distro_ver_data('add_user_to_group', '')
    if add_user_to_group:
        usermod = os.path.join(utils.which("usermod"), "usermod") + " %s %s" % (add_user_to_group, prop.username)
    else:
        usermod = os.path.join(utils.which("usermod"), "usermod") + " %s %s" % ("-Glp", prop.username)

    su_sudo = utils.su_sudo()
    password_f = None
    if su_sudo is "su":
        name,version,is_su = utils.os_release()
        log.debug("name = %s version = %s is_su = %s" %(name,version,is_su))
        if ( name == 'Fedora' and version >= '14' and is_su == True):
           #using su opening GUI apps fail in Fedora 14. 
           #To run GUI apps as root, you need a root login shell (su -) in Fedora 14   
           su_sudo = 'su - -c "%s"'
        else:
           su_sudo = 'su -c "%s"'

        password_f = "get_password_ui"
#        password_f = utils.get_password
    cmd =su_sudo % usermod
    log.info("cmd = %s" %cmd)
#    sts, output = utils.run(cmd, True, password_f, -1,True,cmd)  
    sts, output = utils.run(cmd, True, password_f, 1, True, "Please enter root/superuser password to add 'lp' group")
    if sts == 0:
        result = True
        
    return result


# This parse the given Device URI. and provides the details.
def parseDeviceURI(device_uri):
    m = DEVICE_URI_PATTERN.match(device_uri)
    if m is None:
        raise Error(ERROR_INVALID_DEVICE_URI)

    back_end = m.group(1).lower() or ''
    is_hp = (back_end in ('hp', 'hpfax', 'hpaio'))
    bus = m.group(2).lower() or ''

    if bus not in ('usb', 'net', 'bt', 'fw', 'par'):
        raise Error(ERROR_INVALID_DEVICE_URI)

    model =m.group(3) or ''
    serial = m.group(4) or ''
    dev_file = m.group(5) or ''
    host = m.group(6) or ''
    zc = ''
    if not host:
        zc = host = m.group(7) or ''
    port = m.group(8) or 1

    if bus == 'net':
        try:
            port = int(port)
        except (ValueError, TypeError):
            port = 1

        if port == 0:
            port = 1

#    log.warning("++++: back_end '%s' is_hp '%s' bus '%s' model '%s' serial '%s' dev_file '%s' host '%s' zc '%s' port '%s' " %
#       ( back_end, is_hp, bus, model, serial, dev_file, host, zc, port))

    return back_end, is_hp, bus, model, serial, dev_file, host, zc, port


####### Device class ########
class DetectedDevice:
    def __init__(self, Printer_Name,Device_URI,Device_Type, ppdType, PPDFileError = False, IsEnabled=True ):
        self.PrinterName =Printer_Name
        self.DeviceURI = Device_URI
        self.DeviceType = Device_Type
        self.PPDFileType = ppdType
        self.PPDFileError = PPDFileError
        self.IsEnabled = IsEnabled




#########Main##########
USAGE = [(__doc__, "", "name", True),
         ("Usage: %s [OPTIONS]" % __mod__, "", "summary", True),
         utils.USAGE_OPTIONS,
         utils.USAGE_LOGGING1, utils.USAGE_LOGGING2, utils.USAGE_LOGGING3,
         utils.USAGE_HELP,
        ]

try:
    log.set_module(__mod__)

    try:
        opts, args = getopt.getopt(sys.argv[1:],  'hl:gsr',  ['help', 'help-rest', 'help-man', 'help-desc', 'logging='])

    except getopt.GetoptError, e:
        log.error(e.msg)
        usage()
        sys.exit(1)

    if os.getenv("HPLIP_DEBUG"):
        log.set_level('debug')

    log_level = 'info'
    Show_result=False
    quiet_mode = False
    for o, a in opts:
        if o in ('-h', '--help'):
            usage()

        elif o == '--help-rest':
            usage('rest')

        elif o == '--help-man':
            usage('man')

        elif o == '--help-desc':
            print __doc__,
            sys.exit(0)

        elif o in ('-l', '--logging'):
            log_level = a.lower().strip()

        elif o == '-g':
            log_level = 'debug'

        elif o == '-r':
            Show_result = True

        elif o == '-s':
            quiet_mode = True

    if not log.set_level(log_level):
        usage()
    if not quiet_mode:
        utils.log_title(__title__, __version__)

    log_file = os.path.normpath('/var/log/hp/hplip_queues.log')
    log.debug(log.bold("Saving output in log file: %s" % log_file))
    if os.path.exists(log_file):
        os.remove(log_file)
    log.set_logfile(log_file)
    log.set_where(log.LOG_TO_CONSOLE_AND_FILE)
    
    try:
        from base import device, pml
        # This can fail due to hpmudext not being present
    except ImportError:
        log.error("Device library is not avail.")
        sys.exit(1)

    # Only Qt4 is supported.
    try:
        from PyQt4.QtGui import QApplication, QMessageBox
        from ui4.queuesconf import QueuesDiagnose
    except ImportError:
        log.error("Unable to load Qt4 support. Is it installed?")
        sys.exit(1)

    app = QApplication(sys.argv)
    Error_Found = False
    if check_user_groups() is False:
        dialog = QueuesDiagnose(None, "","",QUEUES_MSG_SENDING)
        core = core_install.CoreInstall(core_install.MODE_CHECK)
        core.init()
        if add_group(core) is False:
            Error_Found = True
            #log.error("Failed to add lp group to user[%s]. Manually add 'lp' group to usergroups"%prop.username)
            dialog.showMessage("User must be part of 'lp' group.\nManually add 'lp' group to '%s' user. " %prop.username)
        else:
            dialog.showSuccessMessage("Groups added successfully and reboot is required. Please reboot system to take effect.")


    is_hpcups_installed = to_bool(sys_conf.get('configure', 'hpcups-install', '0'))
    is_hpijs_installed = to_bool(sys_conf.get('configure', 'hpijs-install', '0'))
#    tui.header("INSTALLED CUPS PRINTER QUEUES")

    
    status, output = utils.run('lpstat -v')

    cups_printers = []
    for p in output.splitlines():
        try:
            match = LPSTAT_PATTERN.search(p)
            printer_name = match.group(1)
            device_uri = match.group(2)
            cups_printers.append((printer_name, device_uri))
        except AttributeError:
            pass

    log.debug(cups_printers)
    log.debug("HPCups installation=%d  HPIJS installation =%d" %(is_hpcups_installed, is_hpijs_installed))
    if cups_printers:
        mapofDevices={}

        for p in cups_printers:
            printer_name, device_uri = p

            if device_uri.startswith("cups-pdf:/"):
                continue

            log.debug(log.bold(printer_name))
            log.debug(log.bold('-'*len(printer_name)))

            try:
                back_end, is_hp, bus, model, serial, dev_file, host, zc, port = parseDeviceURI(device_uri)
            except Error:
                back_end, is_hp, bus, model, serial, dev_file, host, zc, port = '', False, '', '', '', '', '', '', 1
                if 'HP' in device_uri:
                    is_hp = True

            log.debug("Device URI: %s" % device_uri)
            ppd_file = os.path.join('/etc/cups/ppd', printer_name + '.ppd')
            if os.path.exists(ppd_file):
                log.debug("PPD: %s" % ppd_file)

                fileptr = file(ppd_file, 'r').read(4096)
                try:
                    desc = NICKNAME_PATTERN.search(fileptr).group(1)
                except AttributeError:
                    desc = ''

                log.debug("PPD Description: %s" % desc)
                status, output = utils.run('lpstat -p%s' % printer_name)
                log.debug("Printer status: %s" % output.replace("\n", ""))

                #### checking for USb devices ####
                if USB_PATTERN.search(device_uri):                   
                    Key =USB_PATTERN.search(device_uri).group(1)
                #### checking for network devices ####
                elif NET_PATTERN.search(device_uri):
                    Key = NET_PATTERN.search(device_uri).group(1)
                elif NET_ZC_PATTERN.search(device_uri):
                    Key = NET_ZC_PATTERN.search(device_uri).group(1)
                elif NET_OTHER_PATTERN.search(device_uri):
                    part_1 = NET_OTHER_PATTERN.search(device_uri).group(1)
                    part_2 = NET_OTHER_PATTERN.search(device_uri).group(2)
                    if 'HP' in part_2:
                        Key = part_2
                    else:
                        log.info("unknown protocol device_uri=%s" %device_uri)
                        Key=None
                else:
                    log.info("unknown protocol device_uri=%s" %device_uri)
                    Key=None

                if Key is not None:
                    Is_Print_Q_Enabled= True
                    if output.find('Paused') !=  -1:
                        Is_Print_Q_Enabled= False
                    Key=Key+"_"+back_end
                    log.debug("Key'%s': deviceType '%s' is_hp '%s' bus '%s' model '%s' serial '%s' dev_file '%s' host '%s' zc '%s' port '%s' Enabled'%d'"\
                                               %( Key,back_end, is_hp, bus, model, serial, dev_file, host, zc, port,Is_Print_Q_Enabled))

                    PPDFileError = False
                    if back_end == 'hpfax' and not 'HP Fax' in desc:
                        log.error("Incorrect PPD file for fax queue '%s'. Fax queue must use 'HP-Fax-hplip.ppd'." % printer_name)
                        PPDFileError = True
                    elif back_end == 'hp' and 'HP Fax' in desc:
                        log.error("Incorrect PPD file for print queue '%s'. Print queue must not use 'HP-Fax-hplip.ppd'." % printer_name)
                        PPDFileError = True
                    elif back_end not in ('hp', 'hpfax'):
                        log.warn("Device %s is not HPLIP installed. Device must use the hp: or hpfax: to function in HPLIP."% printer_name)

                    ppd_fileType = None
                    if 'hpcups' in desc:
                        ppd_fileType = HPCUPS
                        if not is_hpcups_installed:
                            PPDFileError = True
                    elif 'hpijs' in desc:
                        ppd_fileType = HPIJS
                        if not is_hpijs_installed:
                            PPDFileError = True
                    elif 'Postscript' in desc:
                        ppd_fileType =HPPS
                    elif is_hp:
                        ppd_fileType =HPOTHER
                        PPDFileError = True

                    if ppd_fileType != None:
                        device1 =DetectedDevice(printer_name, device_uri,back_end, ppd_fileType,PPDFileError, Is_Print_Q_Enabled)
                        if Key in mapofDevices:
                            mapofDevices[Key].append(device1)
                        else:
                            deviceList=[device1]
                            mapofDevices[Key]=deviceList
                    else:
                        log.warn("%s is not HP Device." %(printer_name))

        for key,val in mapofDevices.items():
            if len(val) >1:
                log.debug("")
                log.warn('%d queues of same device %s is configured.  Remove unwanted queues.' %(len(val),val[0].PrinterName))
                if Show_result:
                    Error_Found = True
                    dialog = QueuesDiagnose(None, "","",QUEUES_MSG_SENDING)
                    dialog.showMessage("%d queues of same device %s is configured.\nRemove unwanted queues."%(len(val),val[0].PrinterName))
                 
                for que in val:
                    Error_msg =None
                    if 'hp' in que.DeviceType or 'hpfax' in que.DeviceType:
                        if que.PPDFileError ==  False:
                            log.debug("'%s' is configured correctly." %(que.PrinterName))
                        else:
                            log.error("PPD file for '%s' is not correct. Need to choose correct PPD file." %(que.PrinterName))
                            Error_msg = QUEUES_INCORRECT_PPD
                    else:
                        log.error("'%s' is not configured using HPLIP. Need to remove and re-cofigure from hp-setup." %(que.PrinterName))
                        Error_msg =QUEUES_CONFIG_ERROR

                    if Error_msg ==None and que.IsEnabled == False:
                        Error_msg = QUEUES_PAUSED

                    if Error_msg != None:
                        Error_Found = True
                        dialog = QueuesDiagnose(None, que.PrinterName,que.DeviceURI,Error_msg)
                        dialog.show()
                        log.debug("Starting GUI loop...")
                        app.exec_()
            else:
                Error_msg =None
                log.debug("")
                log.debug("Single print queue is configured for '%s'. " %val[0].PrinterName)
                if 'hp' in val[0].DeviceType or 'hpfax' in val[0].DeviceType:
                    if val[0].PPDFileError == False:
                        log.debug("'%s' is configured correctly." %(val[0].PrinterName))
                    else:
                        log.error("PPD file for '%s' is not correct. Need to choose correct PPD file." %(val[0].PrinterName))
                        Error_msg = QUEUES_INCORRECT_PPD
                else:
                    log.error("'%s' is not configured using HPLIP. Need to remove and re-configure using hp-setup." %(val[0].PrinterName))
                    Error_msg = QUEUES_CONFIG_ERROR

                if Error_msg ==None and val[0].IsEnabled == False:
                    Error_msg = QUEUES_PAUSED

                if Error_msg != None:
                    Error_Found = True
                    name = val[0].PrinterName
                    dialog = QueuesDiagnose(None, name, val[0].DeviceURI, Error_msg)
                    dialog.show()
                    log.debug("Starting GUI loop...")
                    app.exec_()
    else:
        log.debug("No queues found.")
   
    if Show_result and (Error_Found is False):
        dialog = QueuesDiagnose(None, "","",QUEUES_MSG_SENDING)
        dialog.showSuccessMessage("Queue(s) configured correctly using HPLIP.")
    elif not quiet_mode and (Error_Found is False):
        log.info("Queue(s) configured correctly using HPLIP.")
       

except KeyboardInterrupt:
    log.error("User exit")

log.debug("Done.")
