#! /usr/bin/env python
# -*- coding: ISO-8859-15 -*-

# PyKota Invoice generator
#
# PyKota - Print Quotas for CUPS and LPRng
#
# (c) 2003, 2004, 2005, 2006 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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# $Id$
#
#

import sys
import os
import pwd
import time
import cStringIO

try :
    from reportlab.pdfgen import canvas
    from reportlab.lib import pagesizes
    from reportlab.lib.units import cm
except ImportError :    
    hasRL = 0
else :    
    hasRL = 1
    
try :
    import PIL.Image 
except ImportError :    
    hasPIL = 0
else :    
    hasPIL = 1

from pykota.tool import Percent, PyKotaToolError, PyKotaCommandLineError, crashed, N_
from pykota.dumper import DumPyKota

__doc__ = N_("""pkinvoice v%(__version__)s (c) %(__years__)s %(__author__)s

An invoice generator for PyKota.

command line usage :

  pkinvoice [options] user1 user2 ... userN

options :

  -v | --version       Prints pkinvoice's version number then exits.
  -h | --help          Prints this message then exits.
  
  -l | --logo img      Use the image as the invoice's logo. The logo will
                       be drawn at the center top of the page. The default
                       logo is /usr/share/pykota/logos/pykota.jpeg
                       
  -p | --pagesize sz   Sets sz as the page size. Most well known
                       page sizes are recognized, like 'A4' or 'Letter'
                       to name a few. The default size is A4.
                       
  -n | --number N      Sets the number of the first invoice. This number
                       will automatically be incremented for each invoice.
                       
  -o | --output f.pdf  Defines the name of the invoice file which will
                       be generated as a PDF document. If not set or
                       set to '-', the PDF document is sent to standard
                       output. 
                       
  -u | --unit u        Defines the name of the unit to use on the invoice.                       
                       The default unit is 'Credits', optionally translated
                       to your native language if it is supported by PyKota.
  
  -V | --vat p         Sets the percent value of the applicable VAT to be
                       exposed. The default is 0.0, meaning no VAT
                       information will be included.
                       
  -s | --start date    Sets the starting date for the print jobs invoiced.
  
  -e | --end date      Sets the ending date for the print jobs invoiced.
                       
  user1 through userN can use wildcards if needed. If no user argument is
  used, a wildcard of '*' is assumed, meaning include all users.
  
  Dates formating with --start and --end :
  
    YYYY : year boundaries
    YYYYMM : month boundaries
    YYYYMMDD : day boundaries
    YYYYMMDDhh : hour boundaries
    YYYYMMDDhhmm : minute boundaries
    YYYYMMDDhhmmss : second boundaries
    yesterday[+-NbDays] : yesterday more or less N days (e.g. : yesterday-15)
    today[+-NbDays] : today more or less N days (e.g. : today-15)
    tomorrow[+-NbDays] : tomorrow more or less N days (e.g. : tomorrow-15)
    now[+-NbDays] : now more or less N days (e.g. now-15)

  'now' and 'today' are not exactly the same since today represents the first
  or last second of the day depending on if it's used in a start= or end=
  date expression. The utility to be able to specify dates in the future is
  a question which remains to be answered :-)
                                        
examples :                       

  $ pkinvoice --unit EURO --output invoices.pdf --start=now-30
  
  Will generate a PDF document containing invoices for all users
  who have spent some credits last month. Invoices will be done in
  EURO.  No VAT information will be included.
""") 
        
class PKInvoice(DumPyKota) :        
    """A class for pkinvoice."""
    def getPageSize(self, pgsize) :
        """Returns the correct page size or None if not found."""
        try :
            return getattr(pagesizes, pgsize.upper())
        except AttributeError :    
            try :
                return getattr(pagesizes, pgsize.lower())
            except AttributeError :
                pass
                
    def printVar(self, label, value, size) :
        """Outputs a variable onto the PDF canvas.
        
           Returns the number of points to substract to current Y coordinate.
        """   
        xcenter = (self.pagesize[0] / 2.0) - 1*cm
        self.canvas.saveState()
        self.canvas.setFont("Helvetica-Bold", size)
        self.canvas.setFillColorRGB(0, 0, 0)
        self.canvas.drawRightString(xcenter, self.ypos, "%s :" % label)
        self.canvas.setFont("Courier-Bold", size)
        self.canvas.setFillColorRGB(0, 0, 1)
        self.canvas.drawString(xcenter + 0.5*cm, self.ypos, value)
        self.canvas.restoreState()
        self.ypos -= (size + 4)
        
    def pagePDF(self, invoicenumber, entry, vat, start, end, unitname) :
        """Generates a new page in the PDF document."""
        extractonly = { "username" : entry.Name }
        if start :
            extractonly["start"] = start
        if end :    
            extractonly["end"] = end
        records = self.storage.extractHistory(extractonly)
        amount = vatamount = 0.0
        vatamount = 0.0
        if records :
            self.canvas.doForm("background")
            self.ypos = self.yorigine - (cm + 20)
            records = self.summarizeDatas(records, "history", extractonly, True)
            fieldnames = records[0]
            fields = {}
            for i in range(len(fieldnames)) :
                fields[fieldnames[i]] = i
            numberofbytes = records[1][fields["jobsizebytes"]]
            numberofpages = records[1][fields["jobsize"]]
            amount = records[1][fields["jobprice"]]
            if amount > 0.0 :
                # There's something due !
                ht = ((amount * 10000.0) / (100.0 + vat)) / 100.0
                vatamount = amount - ht
                self.printVar(_("Invoice"), "#%s" % invoicenumber, 22)
                self.printVar(_("Username"), entry.Name, 22)
                self.ypos -= 20
                if start : 
                    self.printVar(_("Since"), start, 14)
                if end :
                    self.printVar(_("Until"), end, 14)
                self.printVar(_("Edited on"), time.strftime("%c", time.localtime()), 14)
                    
                self.ypos -= 20
                # self.printVar(_("Number of bytes"), str(numberofbytes), 14)
                self.printVar(_("Number of pages printed"), str(numberofpages), 14)
                self.ypos -= 20
                self.printVar(_("Amount due"), "%.2f %s" % (amount, unitname), 22)
                if vat :
                    self.ypos += 8
                    self.printVar("%s (%.2f%%)" % (_("Included VAT"), vat), "%.2f %s" % (vatamount, unitname), 14)
                self.canvas.showPage()
                return 1
        return 0    
        
    def initPDF(self, logo) :
        """Initializes the PDF document."""
        self.pdfDocument = cStringIO.StringIO()        
        self.canvas = c = canvas.Canvas(self.pdfDocument, \
                                        pagesize=self.pagesize, \
                                        pageCompression=1)
        
        c.setAuthor(pwd.getpwuid(os.geteuid())[0])
        c.setTitle("PyKota invoices")
        c.setSubject("Invoices generated with PyKota")
        
        
        self.canvas.beginForm("background")
        self.canvas.saveState()
        
        self.ypos = self.pagesize[1] - (2 * cm)            
        
        xcenter = self.pagesize[0] / 2.0
        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
                self.ypos -= height
                c.drawImage(logo, xcenter - (width / 2.0), \
                                  self.ypos, \
                                  width, height)
        
        self.ypos -= (cm + 20)
        self.canvas.setFont("Helvetica-Bold", 14)
        self.canvas.setFillColorRGB(0, 0, 0)
        self.canvas.drawCentredString(xcenter, self.ypos, "%s :" % _("Here's the invoice for your printouts"))
        
        self.yorigine = self.ypos
        self.canvas.restoreState()
        self.canvas.endForm()
        
    def endPDF(self, fname) :    
        """Flushes the PDF generator."""
        self.canvas.save()
        if fname != "-" :        
            outfile = open(fname, "w")
            outfile.write(self.pdfDocument.getvalue())
            outfile.close()
        else :    
            sys.stdout.write(self.pdfDocument.getvalue())
            sys.stdout.flush()
        
    def main(self, names, options) :
        """Generate invoices."""
        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"
            
        if not self.config.isAdmin :
            raise PyKotaCommandLineError, "%s : %s" % (pwd.getpwuid(os.geteuid())[0], _("You're not allowed to use this command."))
        
        try :    
            vat = float(options["vat"])
            if not (0.0 <= vat < 100.0) :
                raise ValueError
        except :    
            raise PyKotaCommandLineError, _("Incorrect value '%s' for the --vat command line option") % options["vat"]
            
        try :    
            firstnumber = number = int(options["number"])
            if number <= 0 :
                raise ValueError
        except :    
            raise PyKotaCommandLineError, _("Incorrect value '%s' for the --number command line option") % options["number"]
            
        self.pagesize = self.getPageSize(options["pagesize"])
        if self.pagesize is None :
            self.pagesize = self.getPageSize("a4")
            self.printInfo(_("Invalid 'pagesize' option %s, defaulting to A4.") % options["pagesize"], "warn")
            
        if not names :
            names = [ "*" ]
            
        percent = Percent(self)
        outfname = options["output"]    
        if outfname != "-" :
            percent.display("%s..." % _("Extracting datas"))
        entries = self.storage.getMatchingUsers(",".join(names))
        percent.setSize(len(entries))
        if entries :
            percent.display("\n%s\n" % _("Generating invoices"))
            self.initPDF(options["logo"].strip())
            for entry in entries :
                number += self.pagePDF(number, entry, vat, options["start"], options["end"], options["unit"])
                if outfname != "-" :
                    percent.oneMore()
                    
            if number > firstnumber :
                self.endPDF(outfname)
            
        if outfname != "-" :
            percent.done()
                     
if __name__ == "__main__" : 
    retcode = 0
    try :
        defaults = { "vat" : "0.0",
                     "unit" : N_("Credits"),
                     "output" : "-",
                     "pagesize" : "a4", \
                     "logo" : "/usr/share/pykota/logos/pykota.jpeg",
                     "number" : "1",
                   }
        short_options = "vho:r:u:V:p:l:n:s:e:"
        long_options = ["help", "version", "start=", "end=", \
                        "reference=", "unit=", "output=", \
                        "pagesize=", "logo=", "vat=", "number="]
        
        # Initializes the command line tool
        invoiceGenerator = PKInvoice(doc=__doc__)
        invoiceGenerator.deferredInit()
        
        # parse and checks the command line
        (options, args) = invoiceGenerator.parseCommandline(sys.argv[1:], short_options, long_options, allownothing=True)
        
        # sets long options
        options["help"] = options["h"] or options["help"]
        options["version"] = options["v"] or options["version"]
        
        options["start"] = options["s"] or options["start"]
        options["end"] = options["e"] or options["end"]
        options["vat"] = options["V"] or options["vat"] or defaults["vat"]
        options["unit"] = options["u"] or options["unit"] or defaults["unit"]
        options["output"] = options["o"] or options["output"] or defaults["output"]
        options["pagesize"] = options["p"] or options["pagesize"] or defaults["pagesize"]
        options["number"] = options["n"] or options["number"] or defaults["number"]
        options["logo"] = options["l"] or options["logo"]
        if options["logo"] is None : # Allows --logo="" to disable the logo entirely
            options["logo"] = defaults["logo"]  
        
        if options["help"] :
            invoiceGenerator.display_usage_and_quit()
        elif options["version"] :
            invoiceGenerator.display_version_and_quit()
        else :
            retcode = invoiceGenerator.main(args, options)
    except KeyboardInterrupt :        
        sys.stderr.write("\nInterrupted with Ctrl+C !\n")
        retcode = -3
    except PyKotaCommandLineError, msg :     
        sys.stderr.write("%s : %s\n" % (sys.argv[0], msg))
        retcode = -2
    except SystemExit :        
        pass
    except :
        try :
            invoiceGenerator.crashed("pkinvoice failed")
        except :    
            crashed("pkinvoice failed")
        retcode = -1

    try :
        invoiceGenerator.storage.close()
    except (TypeError, NameError, AttributeError) :    
        pass
        
    sys.exit(retcode)