#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# cdemu: command line CDEmu client
# Copyright (C) 2006-2012 Rok Mandeljc
#
# 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.

import getopt
import sys
import os.path
import getpass
import dbus

try:
    import configparser
except ImportError:
    import ConfigParser as configparser

from gettext import gettext as _
from gettext import bindtextdomain, textdomain


# *** Globals ***
app_name = "cdemu-client"
app_version = "2.0.0"
supported_daemon_interface_version = 4


# I18n
bindtextdomain(app_name)
textdomain(app_name)


########################################################################
#                     CDEmu: main client class                         #
########################################################################
class CDEmu (object):
    # D-BUS
    dbus_bus = None
    dbus_proxy = None
    dbus_iface = None

    bus_type = "session" # Use session bus as hard-coded default

    parser_params = {} # Parser parameters (for load command)

    def __init__ (self):
        # Load options; Try "~/.cdemu-client" first, then try "/etc/cdemu-client.conf" next.
        paths = (os.path.expanduser("~/.cdemu-client"), "/etc/cdemu-client.conf")
        path = "(not found)"
        for path_inst in paths:
            if os.path.exists(path_inst):
                path = path_inst
                break

        try:
            config = configparser.ConfigParser()
            config.read([path])

            if config.has_section("defaults"):
                # Read default bus type
                if config.has_option("defaults","bus"):
                    self.bus_type = config.get("defaults","bus")
        except configparser.Error as e:
            # No harm, just print a warning
            self.print_error(_("Failed to load configuration from file '%s': %s") % (path, e))

    # attrs is a comma separated list of integers, where valid values include
    # foreground: 30-37, background: 40-47, reset: 0, bold: 1. (ECMA-48 / VT102)
    def set_color(self, *attrs):
        if not sys.stdout.isatty():
            return

        attrs_str = ";".join(map(str, attrs))
        sys.stdout.write("\x1b[%sm" % (attrs_str))

    def process_command (self, argv):
        print_usage = False

        # Separate options from arguments
        try:
            options, arguments = getopt.gnu_getopt(argv, "hvb:", ["help", "version", "bus=", "password=", "encoding=", "dvd-report-css="])
        except getopt.GetoptError:
            self.print_error(_("Unknown option"))
            self.print_full_usage()
            return False

        # Go through options
        for opt, arg in options:
            if opt in ("-h", "--help"):
                print_usage = True
            elif opt in ("-v", "--version"):
                self.print_version()
                return True
            elif opt in ("-b", "--bus"):
                # Bus type; don't check the value here, connect() will do it
                # for us
                self.bus_type = arg
            # Parser parameters options
            elif opt in ("--password"):
                self.parser_params["password"] = arg
            elif opt in ("--encoding"):
                self.parser_params["encoding"] = arg
            elif opt in ("--dvd-report-css"):
                dictionary = { "true": True, "1": True, "yes": True, "enable": True, "false": False,  "0": False, "no": False, "disable": False }
                word = arg.lower()

                if word not in dictionary:
                    self.print_error(_("Invalid argument to --dvd-report-css: '%s'") % (arg))
                    self.print_full_usage()
                    return False

                self.parser_params["dvd-report-css"] = dictionary.get(word)


        if len(arguments) == 0:
            self.print_full_usage()
            # If we were called with print_usage set, then this technically succeeded
            return print_usage

        # Connect
        if not self.connect():
            self.print_error(_("Failed to connect to daemon (bus: '%s')!") % (self.bus_type))
            return False

        # Command switch
        for command in self.commands:
            if command[0] == arguments[0]:
                if print_usage:
                    self.print_command_usage(command[0])
                else:
                    return command[3](self, arguments[1:])

        self.print_error(_("Unknown command: %s") % (arguments[0]))
        self.print_full_usage()

        return False

    # Device loading with password query support
    def load_device (self, device, filenames, params={}):
        # Try to load it
        try:
            self.dbus_iface.DeviceLoad(device, filenames, params)
            return True
        except dbus.DBusException as e:
            if e.get_dbus_name() == "net.sf.cdemu.CDEmuDaemon.errorMirage.EncryptedImage":
                # We need password
                print(_("The image you are trying to load is encrypted."))
                params["password"] = getpass.getpass(_("Password: ")) # Append password to params
                return self.load_device(device, filenames, params)
            else:
                self.print_error(_("Failed to load image: %s") % (e))
                return False


    # Command handlers
    def cmd_load_device (self, arguments):
        if len(arguments) < 2:
            self.print_invalid_number_of_parameters("load")
            return False

        # We need to pass absolute filenames to daemon
        filenames = map(os.path.abspath, arguments[1:])

        # Particular device vs. any device
        if arguments[0] == "any":
            try:
                nr_devices = self.dbus_iface.GetNumberOfDevices()
            except dbus.DBusException as e:
                self.print_error(_("Failed to get number of devices: %s") % (e))
                return False

            for device in range (0, nr_devices):
                # Device's status
                try:
                    status = self.dbus_iface.DeviceGetStatus(device)
                except dbus.DBusException as e:
                    self.print_error(_("Failed to get status of device %i: %s") % (device, e))
                    continue

                # If device is already loaded, skip it
                if status[0]:
                    continue

                # Load device
                return self.load_device(device, filenames, self.parser_params)

            # If we're here, it means we didn't get an empty device
            self.print_error(_("No empty device found"))
            return False
        else:
            try:
                device = int(arguments[0], 0)
            except ValueError:
                self.print_error(_("String '%s' is not a number") % (arguments[0]))
                return False
            return self.load_device(device, filenames, self.parser_params)

        return True

    def cmd_unload_device (self, arguments):
        if len(arguments) != 1:
            self.print_invalid_number_of_parameters("unload")
            return False

        # Particular device vs. all devices
        if arguments[0] == "all":
            try:
                nr_devices = self.dbus_iface.GetNumberOfDevices()
            except dbus.DBusException as e:
                self.print_error(_("Failed to get number of devices: %s") % (e))
                return False

            unload_fail = False
            for device in range(0, nr_devices):
                try:
                    self.dbus_iface.DeviceUnload(device)
                except dbus.DBusException as e:
                    self.print_error(_("Failed to unload device %i: %s") % (device, e))
                    unload_fail = True
                    continue
            if unload_fail:
                return False

        else:
            try:
                device = int(arguments[0], 0)
                self.dbus_iface.DeviceUnload(device)
            except dbus.DBusException as e:
                self.print_error(_("Failed to unload device %i: %s") % (device, e))
                return False
            except ValueError:
                self.print_error(_("String '%s' is not a number") % (arguments[0]))
                return False

        return True

    def cmd_display_status (self, arguments):
        # Print status for all devices
        try:
            nr_devices = self.dbus_iface.GetNumberOfDevices()
        except dbus.DBusException as e:
            self.print_error(_("Failed to get number of devices: %s") % (e))
            return False

        self.print_header (_("Devices' status:"))
        print("%-5s %-10s %s" % (_("DEV"), _("LOADED"), _("FILENAME")))
        for device in range (0, nr_devices):
            try:
                [loaded, filenames] = self.dbus_iface.DeviceGetStatus(device)
            except dbus.DBusException as e:
                self.print_error(_("Failed to get status of device %i: %s") % (device, e))
                continue

            if not loaded:
                filenames = [ "" ]

            # First line is for all device's data, the rest are for additional filenames
            print("%-5s %-10s %s" % (device, loaded, filenames[0]))
            for filename in filenames[1:]:
                print("%-5s %-10s %s" % ("", "", filename))

        return True

    def cmd_device_mapping (self, arguments):
        # Print device mapping for all devices
        try:
            nr_devices = self.dbus_iface.GetNumberOfDevices()
        except dbus.DBusException as e:
            self.print_error(_("Failed to get number of devices: %s") % (e))
            return False

        self.print_header (_("Device mapping:"))
        print("%-5s %-15s %-15s" % (_("DEV"), _("SCSI CD-ROM"), _("SCSI generic")))
        for device in range (0, nr_devices):
            try:
                [dev_sr, dev_sg] = self.dbus_iface.DeviceGetMapping(device)
            except dbus.DBusException as e:
                self.print_error(_("Failed to get device mapping of device %i: %s") % (device, e))
                continue

            print("%-5s %-15s %-15s" % (device, dev_sr, dev_sg))

        return True

    def cmd_daemon_debug_mask (self, arguments):
        # Get daemon debug mask
        if len(arguments) == 1:
            # Particular device vs. all devices
            if arguments[0] == "all":
                try:
                    nr_devices = self.dbus_iface.GetNumberOfDevices()
                except dbus.DBusException as e:
                    self.print_error(_("Failed to get number of devices: %s") % (e))
                    return False

                print(_("Devices' daemon debug masks:"))
                print("%-5s %-10s" % (_("DEV"), _("DEBUG MASK")))

                for device in range(0, nr_devices):
                    try:
                        mask = self.dbus_iface.DeviceGetOption(device, "daemon-debug-mask")
                    except dbus.DBusException as e:
                        self.print_error(_("Failed to get daemon debug mask of device %i: %s") % (device, e))
                        continue

                    print("%-5s 0x%08X" % (device, mask))
            else:
                try:
                    device = int(arguments[0], 0)
                    mask = self.dbus_iface.DeviceGetOption(device, "daemon-debug-mask")
                except dbus.DBusException as e:
                    self.print_error(_("Failed to get daemon debug mask of device %i: %s") % (device, e))
                    return False
                except ValueError:
                    self.print_error(_("String '%s' is not a number") % (arguments[0]))
                    return False

                print(_("Daemon debug mask of device %i: 0x%X") % (device, mask))

        # Set daemon debug mask
        elif len(arguments) == 2:
            try:
                mask = int(arguments[1], 0)
            except ValueError:
                self.print_error(_("String '%s' is not a number") % (arguments[1]))
                return False

            if arguments[0] == "all":
                try:
                    nr_devices = self.dbus_iface.GetNumberOfDevices()
                except dbus.DBusException as e:
                    self.print_error(_("Failed to get number of devices: %s") % (e))
                    return False

                print(_("Setting daemon debug mask of all devices to 0x%X.") % (mask))
                for device in range(0, nr_devices):
                    try:
                        self.dbus_iface.DeviceSetOption(device, "daemon-debug-mask", mask)
                    except dbus.DBusException as e:
                        self.print_error(_("Failed to set daemon debug mask of device %i to 0x%X: %s") % (device, mask, e))
                        continue
            else:
                try:
                    device = int(arguments[0], 0)
                except ValueError:
                    self.print_error(_("String '%s' is not a number") % (arguments[0]))
                    return False
                print(_("Setting daemon debug mask of device %i to 0x%X.") % (device, mask))
                try:
                    self.dbus_iface.DeviceSetOption(device, "daemon-debug-mask", mask)
                except dbus.DBusException as e:
                    self.print_error(_("Failed to set daemon debug mask of device %i to 0x%X: %s") % (device, mask, e))
                    return False
        else:
            self.print_invalid_number_of_parameters("daemon-debug-mask")
            return False

        return True

    def cmd_library_debug_mask (self, arguments):
        # Get debug mask
        if len(arguments) == 1:
            # Particular device vs. all devices
            if arguments[0] == "all":
                try:
                    nr_devices = self.dbus_iface.GetNumberOfDevices()
                except dbus.DBusException as e:
                    self.print_error(_("Failed to get number of devices: %s") % (e))
                    return False

                print(_("Devices' library debug masks:"))
                print("%-5s %-10s" % (_("DEV"), _("DEBUG MASK")))

                for device in range(0, nr_devices):
                    try:
                        mask = self.dbus_iface.DeviceGetOption(device, "library-debug-mask")
                    except dbus.DBusException as e:
                        self.print_error(_("Failed to get library debug mask of device %i: %s") % (device, e))
                        continue

                    print("%-5s 0x%08X" % (device, mask))
            else:
                try:
                    device = int(arguments[0], 0)
                    mask = self.dbus_iface.DeviceGetOption(device, "library-debug-mask")
                except dbus.DBusException as e:
                    self.print_error(_("Failed to get library debug mask of device %i: %s") % (device, e))
                    return False
                except ValueError:
                    self.print_error(_("String '%s' is not a number") % (mask))
                    return False

                print(_("Library debug mask of device %i: 0x%X") % (device, mask))

        # Set debug mask
        elif len(arguments) == 2:
            try:
                mask = int(arguments[1], 0)
            except ValueError:
                self.print_error(_("String '%s' is not a number") % (arguments[1]))
                return False
            if arguments[0] == "all":
                try:
                    nr_devices = self.dbus_iface.GetNumberOfDevices()
                except dbus.DBusException as e:
                    self.print_error(_("Failed to get number of devices: %s") % (e))
                    return False

                print(_("Setting library debug mask of all devices to 0x%X.") % (mask))
                for device in range(0, nr_devices):
                    try:
                        self.dbus_iface.DeviceSetOption(device, "library-debug-mask", mask)
                    except dbus.DBusException as e:
                        self.print_error(_("Failed to set library debug mask of device %i to 0x%X: %s") % (device, mask, e))
                        continue
            else:
                try:
                    device = int(arguments[0], 0)
                except ValueError:
                    self.print_error(_("String '%s' is not a number") % (arguments[0]))
                    return False
                print(_("Setting library debug mask of device %i to 0x%X.") % (device, mask))
                try:
                    self.dbus_iface.DeviceSetOption(device, "library-debug-mask", mask)
                except dbus.DBusException as e:
                    self.print_error(_("Failed to set library debug mask of device %i to 0x%X: %s") % (device, mask, e))
                    return False
        else:
            self.print_invalid_number_of_parameters("library-debug-mask")
            return False

        return True

    def cmd_dpm_emulation (self, arguments):
        # Get DPM emulation flag
        if len(arguments) == 1:
            # Particular device vs. all devices
            if arguments[0] == "all":
                try:
                    nr_devices = self.dbus_iface.GetNumberOfDevices()
                except dbus.DBusException as e:
                    self.print_error(_("Failed to get number of devices: %s") % (e))
                    return False

                print(_("Devices' DPM emulation flag:"))
                print("%-5s %-10s" % (_("DEV"), _("ENABLED")))

                for device in range(0, nr_devices):
                    try:
                        enabled = self.dbus_iface.DeviceGetOption(device, "dpm-emulation")
                    except dbus.DBusException as e:
                        self.print_error(_("Failed to get DPM emulation flag of device %i: %s") % (device, e))
                        continue

                    print("%-5s %i" % (device, enabled))
            else:
                try:
                    device = int(arguments[0], 0)
                    enabled = self.dbus_iface.DeviceGetOption(device, "dpm-emulation")
                except dbus.DBusException as e:
                    self.print_error(_("Failed to get DPM emulation flag of device %i: %s") % (device, e))
                    return False
                except ValueError:
                    self.print_error(_("String '%s' is not a number") % (arguments[0]))
                    return False

                print(_("DPM emulation flag of device %i: %i") % (device, enabled))

        # Set DPM emulation flag
        elif len(arguments) == 2:
            try:
                enabled = int(arguments[1], 0) and True or False
            except ValueError:
                self.print_error(_("String '%s' is not a number") % (arguments[1]))
                return False
            if arguments[0] == "all":
                try:
                    nr_devices = self.dbus_iface.GetNumberOfDevices()
                except dbus.DBusException as e:
                    self.print_error(_("Failed to get number of devices: %s") % (e))
                    return False

                print(_("Setting DPM emulation flag of all devices to %i.") % (enabled))
                for device in range(0, nr_devices):
                    try:
                        self.dbus_iface.DeviceSetOption(device, "dpm-emulation", enabled)
                    except dbus.DBusException as e:
                        self.print_error(_("Failed to set DPM emulation flag of device %i to %i: %s") % (device, enabled, e))
                        continue
            else:
                try:
                    device = int(arguments[0], 0)
                except ValueError:
                    self.print_error(_("String '%s' is not a number") % (arguments[0]))
                    return False
                print(_("Setting DPM emulation flag of device %i to %i.") % (device, enabled))
                try:
                    self.dbus_iface.DeviceSetOption(device, "dpm-emulation", enabled)
                except dbus.DBusException as e:
                    self.print_error(_("Failed to set DPM emulation flag of device %i to %i: %s") % (device, enabled, e))
                    return False
        else:
            self.print_invalid_number_of_parameters("dpm-emulation")
            return False

        return True

    def cmd_tr_emulation (self, arguments):
        # Get TR emulation flag
        if len(arguments) == 1:
            # Particular device vs. all devices
            if arguments[0] == "all":
                try:
                    nr_devices = self.dbus_iface.GetNumberOfDevices()
                except dbus.DBusException as e:
                    self.print_error(_("Failed to get number of devices: %s") % (e))
                    return False

                print(_("Devices' transfer rate emulation flag:"))
                print("%-5s %-10s" % (_("DEV"), _("ENABLED")))

                for device in range(0, nr_devices):
                    try:
                        enabled = self.dbus_iface.DeviceGetOption(device, "tr-emulation")
                    except dbus.DBusException as e:
                        self.print_error(_("Failed to get transfer rate emulation flag of device %i: %s") % (device, e))
                        continue

                    print("%-5s %i" % (device, enabled))
            else:
                try:
                    device = int(arguments[0], 0)
                    enabled = self.dbus_iface.DeviceGetOption(device, "tr-emulation")
                except dbus.DBusException as e:
                    self.print_error(_("Failed to get transfer rate emulation flag of device %i: %s") % (device, e))
                    return False
                except ValueError:
                    self.print_error(_("String '%s' is not a number") % (arguments[0]))
                    return False

                print(_("Transfer rate emulation flag of device %i: %i") % (device, enabled))

        # Set DPM emulation flag
        elif len(arguments) == 2:
            try:
                enabled = int(arguments[1], 0) and True or False
            except ValueError:
                self.print_error(_("String '%s' is not a number") % (arguments[1]))
                return False
            if arguments[0] == "all":
                try:
                    nr_devices = self.dbus_iface.GetNumberOfDevices()
                except dbus.DBusException as e:
                    self.print_error(_("Failed to get number of devices: %s") % (e))
                    return False

                print(_("Setting transfer rate emulation flag of all devices to %i.") % (enabled))
                for device in range(0, nr_devices):
                    try:
                        self.dbus_iface.DeviceSetOption(device, "tr-emulation", enabled)
                    except dbus.DBusException as e:
                        self.print_error(_("Failed to set transfer rate emulation flag of device %i to %i: %s") % (device, enabled, e))
                        continue
            else:
                try:
                    device = int(arguments[0], 0)
                except ValueError:
                    self.print_error(_("String '%s' is not a number") % (arguments[0]))
                    return False
                print(_("Setting transfer rate emulation flag of device %i to %i.") % (device, enabled))
                try:
                    self.dbus_iface.DeviceSetOption(device, "tr-emulation", enabled)
                except dbus.DBusException as e:
                    self.print_error(_("Failed to set transfer rate emulation flag of device %i to %i: %s") % (device, enabled, e))
                    return False
        else:
            self.print_invalid_number_of_parameters("tr-emulation")
            return False

        return True

    def cmd_device_id (self, arguments):
        # Get device ID
        if len(arguments) == 1:
            # Particular device vs. all devices
            if arguments[0] == "all":
                try:
                    nr_devices = self.dbus_iface.GetNumberOfDevices()
                except dbus.DBusException as e:
                    self.print_error(_("Failed to get number of devices: %s") % (e))
                    return False

                print(_("Devices' IDs:"))
                print("%-5s %s" % (_("DEV"), _("DEVICE ID")))

                for device in range(0, nr_devices):
                    try:
                        values = self.dbus_iface.DeviceGetOption(device, "device-id")
                        device_id = map(str, values)
                    except dbus.DBusException as e:
                        self.print_error(_("Failed to get device ID of device %i: %s") % (device, e))
                        continue

                    print("%-5s %s" % (device, device_id))
            else:
                try:
                    device = int(arguments[0], 0)
                    values = self.dbus_iface.DeviceGetOption(device, "device-id")
                    device_id = map (str, values)
                except dbus.DBusException as e:
                    self.print_error(_("Failed to get device ID of device %i: %s") % (device, e))
                    return False
                except ValueError:
                    self.print_error(_("String '%s' is not a number") % (arguments[0]))
                    return False

                print(_("Device ID of device %i: %s") % (device, device_id))

        # Set device ID
        elif len(arguments) == 5:
            device_id = ( arguments[1], arguments[2], arguments[3], arguments[4] )

            if arguments[0] == "all":
                try:
                    nr_devices = self.dbus_iface.GetNumberOfDevices()
                except dbus.DBusException as e:
                    self.print_error(_("Failed to get number of devices: %s") % (e))
                    return False

                print(_("Setting device ID of all devices to %s.") % (device_id))
                for device in range(0, nr_devices):
                    try:
                        self.dbus_iface.DeviceSetOption(device, "device-id", device_id)
                    except dbus.DBusException as e:
                        self.print_error(_("Failed to set device ID of device %i to %s: %s") % (device, device_id, e))
                        continue
            else:
                try:
                    device = int(arguments[0], 0)
                except ValueError:
                    self.print_error(_("String '%s' is not a number") % (arguments[0]))
                    return False
                print(_("Setting device ID of device %i to %s.") % (device, device_id))
                try:
                    self.dbus_iface.DeviceSetOption(device, "device-id", device_id)
                except dbus.DBusException as e:
                    self.print_error(_("Failed to set device ID of device %i to %s: %s") % (device, device_id, e))
                    return False
        else:
            self.print_invalid_number_of_parameters("device-id")
            return False

        return True

    def cmd_enum_parsers (self, arguments):
        # Display supported parsers
        try:
            parsers = self.dbus_iface.EnumSupportedParsers()
        except dbus.DBusException as e:
            self.print_error(_("Failed to enumerate supported parsers: %s") % (e))
            return False

        if len(arguments) == 0:
            # Print all parsers
            self.print_header (_("Supported parsers:"))
            for info in parsers:
                for description, mime_type in info[2]:
                    print("  %s: %s: %s" % (info[0], mime_type, description))
        else:
            self.print_invalid_number_of_parameters("enum-parsers")
            return False

        return True

    def cmd_enum_file_filters (self, arguments):
        # Display supported file filters
        try:
            filters = self.dbus_iface.EnumSupportedFileFilters()
        except dbus.DBusException as e:
            self.print_error(_("Failed to enumerate supported file filters: %s") % (e))
            return False

        if len(arguments) == 0:
            # Print all file filters
            self.print_header (_("Supported file filters:"))
            for info in filters:
                for description, mime_type in info[2]:
                    print("  %s: %s: %s" % (info[0], mime_type, description))
        else:
            self.print_invalid_number_of_parameters("enum-file-filters")
            return False

        return True

    def cmd_enum_daemon_debug_masks (self, arguments):
        if len(arguments) != 0:
            self.print_invalid_number_of_parameters("enum-daemon-debug-masks")
            return False

        # Print module's debug masks
        try:
            debug_masks = self.dbus_iface.EnumDaemonDebugMasks()
        except dbus.DBusException as e:
            self.print_error(_("Failed to enumerate supported daemon debug masks: %s") % (e))
            return False

        self.print_header (_("Supported daemon debug masks:"))
        for debug_mask in debug_masks:
            print("  %-25s: 0x%04X" % (debug_mask[0], debug_mask[1]))

        return True

    def cmd_enum_library_debug_masks (self, arguments):
        if len(arguments) != 0:
            self.print_invalid_number_of_parameters("enum-library-debug-masks")
            return False

        # Print module's debug masks
        try:
            debug_masks = self.dbus_iface.EnumLibraryDebugMasks()
        except dbus.DBusException as e:
            self.print_error(_("Failed to enumerate supported library debug masks: %s") % (e))
            return False

        self.print_header (_("Supported library debug masks:"))
        for debug_mask in debug_masks:
            print("  %-25s: 0x%04X" % (debug_mask[0], debug_mask[1]))

        return True

    def cmd_version (self, arguments):
        # Print version information
        try:
            library_version = self.dbus_iface.GetLibraryVersion()
        except dbus.DBusException as e:
            self.print_error(_("Failed to get library version: %s") % (e))
            return False
        try:
            daemon_version = self.dbus_iface.GetDaemonVersion()
        except dbus.DBusException as e:
            self.print_error(_("Failed to get daemon version: %s") % (e))
            return False

        print(_("Library version: %s") % (str(library_version)))
        print(_("Daemon version: %s")  % (str(daemon_version)))

        return True

    def connect (self):
        try:
            if self.bus_type == "system":
                self.dbus_bus = dbus.SystemBus()
            elif self.bus_type == "session":
                self.dbus_bus = dbus.SessionBus()
            else:
                self.print_warning(_("Invalid bus parameter '%s', using default!") % (self.bus_type))
                # Use system bus by default
                self.bus_type = "system"
                self.dbus_bus = dbus.SystemBus()

            self.dbus_proxy = self.dbus_bus.get_object("net.sf.cdemu.CDEmuDaemon", "/Daemon")
            self.dbus_iface = dbus.Interface(self.dbus_proxy, "net.sf.cdemu.CDEmuDaemon")
        except dbus.DBusException as e:
            self.print_error(_("Failed to connect to CDEmu daemon: %s") % (e))
            return False

        # Get daemon interface version
        try:
            interface_version = self.dbus_iface.GetDaemonInterfaceVersion()
        except dbus.DBusException as e:
            self.print_error(_("Failed to acquire daemon interface version (this most likely means your daemon is out-of-date): %s") % (e))
            return False

        # Check daemon interface version
        if interface_version != supported_daemon_interface_version:
            self.print_error(_("CDEmu daemon interface version %i detected, but version %i is required!") % (interface_version, supported_daemon_interface_version))
            return False

        return True

    def print_command_usage (self, command):
        for cur_command in self.commands:
            if cur_command[0] == command:
                # We need to make sure we're not trying to 'translate' empty
                # string for the third argument
                self.print_header (_("Usage:")),
                print(_("  %s %s %s") % ("cdemu", cur_command[0], cur_command[1] != "" and _(cur_command[1]) or ""))

    def print_full_usage (self):
        self.print_header (_("Usage:")),
        print(_("  %s [options] <command> <command parameters>") % ("cdemu"))
        print("")
        self.print_header (_("Commands:"))
        for cur_command in self.commands:
            print("  %-25s %s" % (cur_command[0], _(cur_command[2])))
        print("")
        self.print_header (_("Options:"))
        print("  %-25s %s" % ("-h, --help", _("displays help message")))
        print("  %-25s %s" % ("-v, --version", _("displays program version")))
        print("  %-25s %s" % ("-b, --bus", _("sets D-BUS bus type to use; valid values are 'session' and 'system'")))
        print("")
        self.print_header (_("Optional parser parameters (valid only for 'load' command):"))
        print("  %-30s %s" % ("--password=<string>", _("password for encrypted images")))
        print("  %-30s %s" % ("--encoding=<string>", _("encoding for text-based images")))
        print("  %-30s %s" % ("--dvd-report-css=<true/false>", _("flag the DVD disc as CSS-encoded")))

    def print_invalid_number_of_parameters (self, command):
        self.print_error(_("Invalid number of parameters for command '%s'!") % (command))
        self.print_command_usage(command)

    def print_version (self):
        print("%s %s - (C) Rok Mandeljc" % (app_name, app_version))

    def print_header (self, message):
        self.set_color(1, 34)
        print (message)
        self.set_color(0)

    def print_error (self, message):
        self.set_color(1, 31)
        print(_("ERROR: %s") % (message))
        self.set_color(0)

    def print_warning (self, message):
        self.set_color(1, 33)
        print(_("WARNING: %s") % (message))
        self.set_color(0)

    # Commands
    commands = [
        [
            "load",
            _("<device> <image file> [...]"),
            _("loads the device"),
            cmd_load_device
        ], [
            "unload",
            _("<device>"),
            _("unloads the device"),
            cmd_unload_device
        ], [
            "status",
            "",
            _("displays the devices' status"),
            cmd_display_status
        ], [
            "device-mapping",
            "",
            _("displays the device mapping information"),
            cmd_device_mapping
        ], [
            "daemon-debug-mask",
            _("<device> [new value]"),
            _("displays/sets daemon debug mask"),
            cmd_daemon_debug_mask
        ], [
            "library-debug-mask",
            _("<device> [new value]"),
            _("displays/sets library debug mask"),
            cmd_library_debug_mask
        ], [
            "dpm-emulation",
            _("<device> [new value]"),
            _("displays/sets DPM emulation flag"),
            cmd_dpm_emulation
        ], [
            "tr-emulation",
            _("<device> [new value]"),
            _("displays/sets transfer rate emulation flag"),
            cmd_tr_emulation
        ], [
            "device-id",
            _("<device> [new vendor_id] [new product_id] [new revision] [new vendor_specific]"),
            _("displays/sets device ID"),
            cmd_device_id
        ], [
            "enum-parsers",
            "",
            _("enumerates supported parsers"),
            cmd_enum_parsers
        ], [
            "enum-file-filters",
            "",
            _("enumerates supported file filters"),
            cmd_enum_file_filters
        ], [
            "enum-daemon-debug-masks",
            "",
            _("enumerates valid daemon debug masks"),
            cmd_enum_daemon_debug_masks
        ], [
            "enum-library-debug-masks",
            "",
            _("enumerates valid library debug masks"),
            cmd_enum_library_debug_masks
        ], [
            "version",
            "",
            _("displays version information"),
            cmd_version
        ]
    ]


########################################################################
#                               Main                                   #
########################################################################
if __name__ == '__main__':
    cdemu = CDEmu()
    ret = cdemu.process_command(sys.argv[1:])

    # ret is either True or False; we need to return 0 or -1, though...
    sys.exit(ret - 1)
