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

import os
import re
import sys
import dbus
import shutil
import gettext
import urllib2
import tempfile
import subprocess
from BeautifulSoup import BeautifulSoup

SUPPORT_PAGE = "http://welcome.solutions.brother.com/bsc/public_s/id/linux/en/download_prn.html"
LICENSE_FILE = "/usr/share/brother-driver-installer/LICENSE.driver"
DEBUG = False

########
# i18n #
########

__trans = gettext.translation('install-brother-printer', fallback=True)
_ = __trans.ugettext


def find_binary(name):
    """Search for a binary in $PATH and in /opt."""
    for path in os.environ["PATH"].split(":"):
        if os.path.exists(os.path.join(path, name)):
            return os.path.join(path, name)

def chunk_read(_response, _output_file, progress_handle, chunk_size=8192):
    """Read and write down the downloaded file."""
    total_size = _response.info().getheader('Content-Length').strip()
    total_size = int(total_size)
    bytes_so_far = 0

    output = open(_output_file, "w")

    while 1:
        chunk = _response.read(chunk_size)
        bytes_so_far += len(chunk)

        if not chunk:
            break

        output.write(chunk)

        percent = float(bytes_so_far) / total_size
        percent = round(percent*100, 2)
        progress_handle.setProgressValue(percent)

    output.close()

def exit_with_error(msg):
    """Exit with an error message."""
    print "Error: %s" % msg
    sys.exit(1)

def debug(msg):
    """Dump a debug message."""
    if DEBUG:
        print "Debug: %s" % msg

def ask_for_model(models):
    """Ask for printer model."""
    cmd = ["kdialog", "--title", _("Brother Printer Driver Installer"),
                      "--menu",  _("Please select your printer model:")]

    for index, model in enumerate(models):
        cmd.extend([str(index+1), model])

    stdout = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout
    if stdout:
        index = int(stdout.read().strip())
        return models[index-1]
    else:
        exit_with_error(_("Can't determine model"))

def show_file_list(file_list):
    """Show installed file list."""
    cmd = ["kdialog", "--title", _("List of succesfully installed files"),
                      "--textbox", file_list, "500", "400"]

    subprocess.call(cmd)

def parse_url(url):
    """Parse URL to find out the download URL."""
    return re.sub("^.*dlfile=(.*)&.*$", "\\1", url)

def get_progress_dbus_proxy(title, first_label, totalsteps=None):
    """Get a D-Bus proxy for kdialog proxy."""
    cmd = ["kdialog", "--title", title, "--progressbar", first_label]
    if totalsteps:
        cmd.append(totalsteps)
    stdout = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout
    if stdout:
        bus_name, object_path = stdout.read().strip().split()
        bus = dbus.SessionBus()
        proxy = bus.get_object(bus_name, object_path)
        proxy.setProgressValue = lambda x: proxy.get_dbus_method("Set", dbus_interface="org.freedesktop.DBus.Properties")("org.kde.kdialog.ProgressDialog", "value", x)
        return proxy
    else:
        exit_with_error(_("Can't show progress dialog."))

def main(args):
    """Main program."""

    clean_up = True
    if "--keep-tmp" in args:
        clean_up = False

    if "--debug" in args:
        global DEBUG
        DEBUG = True

    progress = get_progress_dbus_proxy(_("Brother Printer Driver Installer"),
                                       _("Getting driver list from <b><i>http://welcome.solutions.brother.com</i></b>"), "0")
    try:
        html_file = urllib2.urlopen(SUPPORT_PAGE)
    except urllib2.URLError:
        exit_with_error(_("Error: Make sure that the URL '%s' is valid and in-use.") % SUPPORT_PAGE)

    progress.setLabelText(_("Generating the list of supported printers..."))
    soup = BeautifulSoup(html_file.read())
    progress.close()

    # Parse the IndexListB <div> to fetch the supported models
    models = [drv.string for drv in \
              soup.find("div", id="IndexListB").findAll("a", href=re.compile('^#[DMHF].*'))]

    # Generate a dictionary model->driver
    model_dict = dict([(model, model) for model in models])

    # Parse the list of alternative drivers and update the dictionary
    for alt_driver in soup.findAll("div", id="AltDriver"):
        children = alt_driver.findChildren(name="a")
        for_model = children[0].get("name")
        use_this = children[1].b.string
        model_dict[for_model] = use_this

    # Let the user pick the printer model and find out the driver
    model = ask_for_model(sorted(model_dict.keys()))
    driver = model_dict[model]

    # Determine what to download for this driver
    urls = [parse_url(a_tag.get("href")) for a_tag in \
            soup.find("div", id="DownloadList").find("a", attrs={"name":driver}).findNext().findNext().findAll("a", href=re.compile("^.*\.i386\.rpm"))]

    # Create a temporary directory
    temp_dir = tempfile.mkdtemp(prefix="brother-")

    # Download them
    progress = get_progress_dbus_proxy(_("Brother Printer Driver Installer"), ".")

    for url in urls:
        progress.setProgressValue(0)
        progress.setLabelText(_("Downloading <b><i>%s</i></b>...") % url)
        response = urllib2.urlopen(url)
        output_file = os.path.join(temp_dir, os.path.basename(url))
        chunk_read(response, output_file, progress)

    progress.setProgressValue(0)
    progress.setLabelText(_("Please wait while installing <b>Brother %s</b> driver...") % model)

    os.chdir(temp_dir)
    files = None

    # Make sure /var/spool/lpd exists
    if not os.path.exists("/var/spool/lpd"):
        os.makedirs("/var/spool/lpd")

    # Find RPM/DPKG
    rpm = find_binary("rpm")
    dpkg = find_binary("dpkg")
    if not rpm and os.path.exists("/opt/rpm/bin/rpm"):
        # Pardus installs RPM to /opt for avoiding confusion
        rpm = "/opt/rpm/bin/rpm"

    if rpm:
        # RPM is found
        cmd = [rpm, "--force", "-v", "-i", "--nodeps"]
        rpms = [os.path.basename(url) for url in urls]
        cmd.extend(rpms)
        subprocess.call(cmd)

        # Get the list of files that are installed
        cmd = [rpm, "-ql"]
        cmd.extend([rpm.replace(".rpm", "") for rpm in rpms])
        files = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.read().split("\n")

        if files:
            open(os.path.join(temp_dir, "files"), "w").write("\n".join(files))
            show_file_list(os.path.join(temp_dir, "files"))

    elif dpkg:
        # TODO: Add dpkg support
        pass

    else:
        # No way to install the drivers, quiting.
        pass

    # Run ldconfig in any case
    os.system("ldconfig -X &> /dev/null")

    # Restart CUPS service if OS is Pardus
    # This is already done by cupswrapper using /etc/init.d/cups
    if os.path.exists("/etc/pardus-release"):
        progress.setLabelText(_("Restarting CUPS service..."))
        os.system("/bin/service -q cups restart")
        progress.setProgressValue(100)

    # Close progress dialog
    progress.close()

    # Cleanup /tmp/brother-*
    if clean_up:
        try:
            shutil.rmtree(temp_dir)
        except OSError:
            pass
    else:
        debug(temp_dir)

if __name__ == "__main__":
    sys.exit(main(sys.argv[1:]))
