#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# PyKota : Print Quotas for CUPS
#
# (c) 2003-2009 Jerome Alet <alet@librelogiciel.com>
# 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 3 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, see <http://www.gnu.org/licenses/>.
#
# $Id$
#
#

"""A banner generator for PyKota"""

import sys
import os
import time
import cStringIO
import subprocess

try :
    from reportlab.pdfgen import canvas
    import reportlab.lib
    from reportlab.lib.units import cm
except ImportError :
    hasRL = False
else :
    hasRL = True

try :
    import PIL.Image
except ImportError :
    hasPIL = False
else :
    hasPIL = True

import pykota.appinit
from pykota.utils import run
from pykota.commandline import PyKotaOptionParser, \
                               checkandset_pagesize, \
                               checkandset_savetoner
from pykota.pdfutils import getPageSize
from pykota.errors import PyKotaToolError
from pykota.tool import Tool
from pykota import version

class PyKotaBanner(Tool) :
    """A class for pkbanner."""
    def getVar(self, varname) :
        """Extracts a variable from the environment and returns its value or 'Unknown' in the current locale."""
        return os.environ.get(varname) or _("Unknown")

    def printVar(self, canvas, x, y, label, value, size, savetoner) :
        """Outputs a variable onto the PDF canvas.

           Returns the number of points to substract to current Y coordinate.
        """
        canvas.saveState()
        canvas.setFont("Helvetica-Bold", size)
        (r, g, b) =  [ color + (savetoner * (1.0 - color)) for color in (0, 0, 0) ] # Black * savetoner
        canvas.setFillColorRGB(r, g, b)
        message = "%s :" % _(label)
        canvas.drawRightString(x, y, message)
        canvas.setFont("Courier-Bold", size)
        (r, g, b) =  [ color + (savetoner * (1.0 - color)) for color in (1, 0, 0) ] # Red * savetoner
        canvas.setFillColorRGB(r, g, b)
        canvas.drawString(x + 0.5*cm, y, value)
        canvas.restoreState()
        return (size + 4)

    def genPDF(self, pagesize, logo, url, text, savetoner) :
        """Generates the banner in PDF format, return the PDF document as a string."""
        document = cStringIO.StringIO()
        c = canvas.Canvas(document, pagesize=pagesize, pageCompression=1)

        c.setAuthor(self.effectiveUserName)
        c.setTitle(_("PyKota generated Banner"))
        c.setSubject(_("This is a print banner generated with PyKota"))

        xcenter = pagesize[0] / 2.0
        ycenter = pagesize[1] / 2.0

        ypos = pagesize[1] - (2 * cm)

        if logo :
            try :
                imglogo = PIL.Image.open(logo)
            except :
                self.printInfo("Unable to open image %s" % logo, "warn")
            else :
                (width, height) = imglogo.size
                multi = float(width) / (8 * cm)
                width = float(width) / multi
                height = float(height) / multi
                xpos = xcenter - (width / 2.0)
                ypos -= height
                c.drawImage(logo, xpos, ypos, width, height)

        # New top
        xpos = pagesize[0] / 5.0
        ypos -= (1 * cm) + 20

        printername = self.getVar("PYKOTAPRINTERNAME")
        username = self.getVar("PYKOTAUSERNAME")
        accountbanner = self.config.getAccountBanner(printername)

        # Outputs the username
        ypos -= self.printVar(c, xcenter, ypos, _("Username"), username, 20, savetoner)

        # Text
        if text :
            ypos -= self.printVar(c, xcenter, ypos, _("More Info"), text, 20, savetoner)

        # Printer and Job Id
        job = "%s - %s" % (printername, self.getVar("PYKOTAJOBID"))
        ypos -= self.printVar(c, xcenter, ypos, _("Job"), job, 14, savetoner)

        # Current date (TODO : at the time the banner was printed ! Change this to job's submission date)
        datetime = time.strftime("%c", time.localtime()).decode(self.charset, "replace")
        ypos -= self.printVar(c, xcenter, ypos, _("Date"), datetime, 14, savetoner)

        # Result of the print job
        action = self.getVar("PYKOTAACTION")
        if action == "ALLOW" :
            action = _("Allowed")
        elif action == "DENY" :
            action = _("Denied")
        elif action == "WARN" :
            action = _("Allowed with Warning")
        elif action == "PROBLEM" :
            # should never occur
            action = _("Problem")
        elif action == "CANCEL" :
            # should never occur
            action = _("Cancelled")
        ypos -= self.printVar(c, xcenter, ypos, _("Result"), action, 14, savetoner)

        # skip some space
        ypos -= 20

        # Outputs title and filename
        # We put them at x=0.25*pagewidth so that the line is long enough to hold them
        title = self.getVar("PYKOTATITLE")
        ypos -= self.printVar(c, xcenter / 2.0, ypos, _("Title"), title, 10, savetoner)

        filename = self.getVar("PYKOTAFILENAME")
        ypos -= self.printVar(c, xcenter / 2.0, ypos, _("Filename"), filename, 10, savetoner)

        # skip some space
        ypos -= 20

        # Now outputs the user's account balance or page counter
        ypos -= self.printVar(c, xcenter, ypos, _("Pages printed so far on %s") % printername, self.getVar("PYKOTAPAGECOUNTER"), 14, savetoner)
        limitby = self.getVar("PYKOTALIMITBY")
        if limitby == "balance" :
            ypos -= self.printVar(c, xcenter, ypos, _("Account balance"), self.getVar("PYKOTABALANCE"), 14, savetoner)
        elif limitby == "quota" :
            ypos -= self.printVar(c, xcenter, ypos, _("Soft Limit"), self.getVar("PYKOTASOFTLIMIT"), 14, savetoner)
            ypos -= self.printVar(c, xcenter, ypos, _("Hard Limit"), self.getVar("PYKOTAHARDLIMIT"), 14, savetoner)
            ypos -= self.printVar(c, xcenter, ypos, _("Date Limit"), self.getVar("PYKOTADATELIMIT"), 14, savetoner)
        else :
            if limitby == "noquota" :
                msg = _("No Limit")
            elif limitby == "nochange" :
                msg = _("No Accounting")
            elif limitby == "noprint" :
                msg = _("Forbidden")
            else :
                msg = _("Unknown")
            ypos -= self.printVar(c, xcenter, ypos, _("Printing Mode"), msg, 14, savetoner)

        # URL
        if url :
            c.saveState()
            c.setFont("Courier-Bold", 16)
            (r, g, b) =  [ color + (savetoner * (1.0 - color)) for color in (0, 0, 1) ] # Blue * savetoner
            c.setFillColorRGB(r, g, b)
            c.drawCentredString(xcenter, 2 * cm, url)
            c.restoreState()

        c.showPage()
        c.save()
        return document.getvalue()

    def main(self, arguments, options) :
        """Generates a banner."""
        if not hasRL :
            raise PyKotaToolError, "The ReportLab module is missing. Download it from http://www.reportlab.org"
        if not hasPIL :
            raise PyKotaToolError, "The Python Imaging Library is missing. Download it from http://www.pythonware.com/downloads"

        self.logdebug("Generating the banner in PDF format...")
        doc = self.genPDF(getPageSize(options.pagesize),
                          options.logo.strip().encode(sys.getfilesystemencoding(), "replace"),
                          options.url.strip(),
                          " ".join(arguments).strip(),
                          options.savetoner / 100.0)

        self.logdebug("Converting the banner to PostScript...")
        command = "gs -q -dNOPAUSE -dBATCH -dPARANOIDSAFER -sDEVICE=pswrite -sOutputFile=- -"
        subpr = subprocess.Popen(command,
                                 shell=True,
                                 stdin=subprocess.PIPE,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)
        try :
            (out, err) = subpr.communicate(doc)
        except OSError, msg :
            raise PyKotaToolError, _("Impossible to execute '%(command)s'") % locals()
        status = subpr.wait()
        if os.WIFEXITED(status) :
            status = os.WEXITSTATUS(status)
        self.logdebug("PDF to PostScript converter exit code is %s" % str(status))
        sys.stdout.write(out)
        sys.stdout.flush()
        self.logdebug("Banner completed.")
        return status

if __name__ == "__main__" :
    # TODO : --papertray : to print banners on a different paper (colored for example)
    parser = PyKotaOptionParser(description=_("Banner generator for PyKota."))
    parser.add_option("-l", "--logo",
                            dest="logo",
                            default=u"/usr/share/pykota/logos/pykota.jpeg",
                            help=_("The image to use as a logo. The logo will be drawn at the center top of the page. The default logo is %default."))
    parser.add_option("-p", "--pagesize",
                            type="string",
                            action="callback",
                            callback=checkandset_pagesize,
                            dest="pagesize",
                            default=u"A4",
                            help=_("Set the size of the page. Most well known page sizes are recognized, like 'A4' or 'Letter' to name a few. The default page size is %default."))
    parser.add_option("-s", "--savetoner",
                            type="float",
                            action="callback",
                            callback=checkandset_savetoner,
                            dest="savetoner",
                            default=0.0,
                            help=_("Set the text luminosity to this percent. This can be used to save toner. The default value is %default, which means that no toner saving will be done."))
    parser.add_option("-u", "--url",
                            dest="url",
                            default=u"http://www.pykota.com",
                            help=_("Set the url to write at the bottom of the banner page. The default url is %default."))
    parser.add_example('--logo="" --savetoner=75',
                       _("This would generate a banner in the default page size, with no logo, and text luminosity would be increased by 75%."))

    run(parser, PyKotaBanner)
