#!/usr/bin/python
"""
Import and Export model
"""
#  Copyright (C) 2004  Henning Jacobs <henning@srcco.de>
#
#  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.
#
#  $Id: converters.py 82 2004-07-11 13:01:44Z henning $

import vcard
import debug
import types

class EncodedFileWriter:
    "Takes Unicode Strings and writes them encoded to the real file."
    def __init__(self, realfile, encoding):
        self._fd = realfile
        self._encoding = encoding
    def write(self, val):
        if isinstance(val, types.UnicodeType):
            self._fd.write(val.encode(self._encoding, 'replace'))
        else:
            self._fd.write(val)
    def close(self):
        self._fd.close()

def export2vcard(model, outfile, encoding, cardhandles=None):
    "Simply fetch raw vCard Data from Server and write to File"
    if cardhandles is None: cardhandles=model.ListHandles()
    cardlist = vcard.vCardList()
    for handle in cardhandles:
        cardlist.add(model.GetContact(handle))
    outfile.write(cardlist.VCF_repr())
        
def export2xmlrdf(model, outfile, encoding, cardhandles=None):
    "Export to vCard-RDF (XML) Data"
    if cardhandles is None: cardhandles=model.ListHandles()
    cardlist = vcard.vCardList()
    for handle in cardhandles:
        cardlist.add(model.GetContact(handle))
    outfile.write(cardlist.XML_repr())

def export2csv(model, outfile, encoding, cardhandles=None, fieldnames=vcard.FIELDNAMES):
    "Export to Comma Separated Values"
    import csv 
    writer = csv.writer(outfile)
    # local copy:
    fieldnames = fieldnames[:]
    # Hack:
    for field in ["Phone", "Email"]:
	idx = fieldnames.index(field)
	for i in range(3):
	    fieldnames.insert(idx+i+1, "%s %d" % (field, i+2))
    writer.writerow(fieldnames)
    if cardhandles is None: cardhandles=model.ListHandles()
    for handle in cardhandles:
        row = []
        for field in fieldnames:
            val = model.GetContact(handle).getFieldValueStr(field).encode(encoding, 'replace')
            # Newline break things:
            val = val.replace('\n', r'\n').replace('\r', r'\r')
            row.append(val)
        writer.writerow(row)

def export2latex(model, outfile, encoding='latin-1', cardhandles=None):
    "Export to Fancy LaTeX Pages"
    # Subset of vcard.FIELDNAMES:
    fields2export = [
        "FormattedName",
        #"DisplayName",
        #"Family",
        #"Given",
        #"Additional",
        #"Prefixes",
        #"Suffixes",
        #"NickName",
        "Birthday",
        "Organization",
        "Units",
        "Title",
        "Role",
        "Phone",
        "Email",
        #"Mailer",
        "POBox",
        "Extended",
        "Street",
        "PostalCode",
        "City",
        "Region",
        "Country",
        #"Label",
        "Note",
        "Categories",
        #"SortName",
        #"SortString",
        "URL",
        #"Key",
        "TimeZone",
        "GlobalPosition",
        #"Rev",
        "UID"
        ]
    template = r"""\documentclass[10pt]{article}
\usepackage[a4paper]{geometry}
\usepackage[latin1]{inputenc}
\usepackage{color}
\definecolor{light-gray}{gray}{0.75}
\usepackage{multicol}
\usepackage{fancyhdr}
\usepackage{times}
% Select Helvetica as default font:
\renewcommand{\familydefault}{phv}
\pagestyle{fancy}
\rhead{\tt\scriptsize vCards converted to LaTeX by PyCoCuMa v<?=Version?> on <?=DateTime?>}
\cfoot{\thepage}
\def\begincard#1{
        \noindent\begin{minipage}{\linewidth}
        \parindent = 0mm
        \dimen1=\linewidth
        \advance\dimen1 by -2\fboxsep
        \setbox0=\hbox to \dimen1{\small #1
        \hfill}
        \dp0=0pt
        \colorbox{light-gray}{\box0}
        \vskip 1mm
        \bgroup\scriptsize}
\def\endcard{\egroup
        \end{minipage}\vskip 2mm}
\begin{document}
    \begin{multicols}{3}
    <?while NextCard(){?>
\begincard{<?=GetFieldValue('FormattedName')?>}
<?=PrintLines()?>
\endcard
        
    <?}?>
    \end{multicols}
\end{document}
""".splitlines(True)
    def texescape(str):
        "Escape special TeX characters"
        return str.replace('&','\&').replace('_','\_').replace('$','\$').replace('#','\#')
    if cardhandles is None: cardhandles=model.ListHandles()
    globals = {'ret':'', 'cardidx':0}
    def nextCard(globals=globals, cardhandles=cardhandles):
        idx = globals['cardidx']
        if idx >= len(cardhandles): return False
        else:
            globals['card'] = model.GetContact(cardhandles[idx])
            globals['cardidx'] += 1
            return True
    def getFieldValue(str, globals=globals):
        return globals['card'].getFieldValueStr(str)
    def printLines(globals=globals):    
        card = globals['card']
        ret = ''
        addresses = []
        for field in fields2export:
            valcount = 0
            val = card.getFieldValueStr(field, default=None)
            while val is not None:
                valcount += 1
                if field in vcard.ADRFIELDS:
                    if len(addresses) < valcount: addresses.append({})
                    adr = addresses[valcount-1]
                    adr[field] = val
                    if len(adr) >= len(vcard.ADRFIELDS):
                        zeilen = [adr["POBox"],
                            adr["Extended"],
                            adr["Street"],
                            " ".join(filter(None, [adr["PostalCode"], adr["City"]])),
                            ", ".join(filter(None, [adr["Region"], adr["Country"]]))]
                        for z in filter(None, zeilen):
                            ret = ret + "    "+ z + "\par\n"
                elif val:
                    ret = ret + "    "+ val + "\par\n"
                val = card.getFieldValueStr(field, default=None)
        return ret        
    import __version__
    import time
    globals.update({'Version': __version__.__version__,
        'DateTime':time.asctime(),
        'NextCard':nextCard,
        'GetFieldValue':getFieldValue,
        'PrintLines':printLines})
    def outfunc(str, outfile=outfile):
        outfile.write(str)
    from TemplateProcessor import TemplateProcessor
    proc = TemplateProcessor(postproc=texescape)            
    proc.process(template, outfunc, globals)                     
    
def export2pine(model, outfile, encoding, cardhandles=None):
    "Export to Pine Mail Addressbook"
    if cardhandles is None: cardhandles=model.ListHandles()
    # Support up to three defined email-addresses per contact:
    rows = model.QueryAttributes(cardhandles,
        ('NickName','DisplayName','Email 1','Email 2','Email 3'))
    for row in rows:
        # rows are immutable tuples, make them mutable:
        row = list(row)
        if row[2]:
            # do split()[0] on each email, because there may follow
            # params: (pref, internet) for example:
            emails = map(lambda x: x.split()[0], filter(None, row[2:]))
            row[2] = ', '.join(emails)
            # More than one Email-Addr, so enclose with brackets:
            if len(emails)>1: row[2] = "(%s)" % row[2]
            outfile.write('\t'.join(row[:3]) + '\n')

def importFvcard(model, infile, encoding):
    "Import from VCF-File"
    importcards = vcard.vCardList()
    importcards.LoadFromStream(infile, encoding)
    for cardhdl in importcards.sortedlist():
        handle = model.NewContact()
        model.PutContact(handle, importcards[cardhdl].VCF_repr())

def importFcsv(model, infile, encoding):
    "Import from CSV-Table"
    import csv
    reader = csv.reader(infile)
    firstrow = None
    for row in reader:
        if firstrow is None:
            firstrow = row
            unknownfields = filter(lambda x: x[0] not in vcard.FIELDNAMES,
                zip(firstrow, range(len(firstrow))))
            if unknownfields:
                from FieldMappingDialog import FieldMappingDialog
                dlg = FieldMappingDialog(None, [field for field, no in unknownfields],
                    title='Map Fields',
                    headline='Please map the unknown fields to vCard fields.\n'+
                    'Unmapped fields will be ignored.')
                dlg.activate()
                for mappedto, i in zip(dlg.getvalue(), [no for field, no in unknownfields]):
                    firstrow[i] = mappedto
        else:
            handle = model.NewContact()
            try:
                card = model.GetContact(handle)
                for i in range(len(row)):
                    # firstrow must have at least len(row) elements:
                    if row[i] and firstrow[i]:
                        card.setFieldValueStr(firstrow[i],
                            unicode(row[i], encoding, 'replace'))
                model.PutContact(handle, card.VCF_repr())    
            except:
                # Something bad happened, don't leave our DB inconsistent:
                model.DelContact(handle)
                raise

exportfunctions = {
    "vcard":export2vcard,
    "xmlrdf":export2xmlrdf,
    "csv":  export2csv,
    "latex":export2latex,
    "pine": export2pine}
def exportvcards(model, targetformat, outfile, encoding, cardhandles=None):
    "Chooses the right exportfunction according to targetformat"
    func = exportfunctions.get(targetformat)
    if func:
        return func(model, outfile, encoding, cardhandles)
    else:
        return None
        
exportfiletypes = {
    "vcard":("vCard", "*.vcf"),
    "xmlrdf":("XML Files", "*.xml"),
    "csv":  ("Comma Separated Text", "*.csv"),
    "latex":("LaTeX Document", "*.tex"),
    "pine": ("Pine Addressbook", "*")}
def askexportfile(targetformat, master=None):
    "Ask for Export Filename"
    import tkFileDialog, os
    try:
        dir =  os.getcwd()
    except:
        dir = ""
    dlg = tkFileDialog.SaveAs(master, filetypes=[exportfiletypes[targetformat]])
    fname = dlg.show(initialdir=dir, initialfile="")
    if fname:
        root, ext = os.path.splitext(fname)
        if root and not ext: ext = exportfiletypes[targetformat][1][1:]
        return root+ext
    else:    
        return None
        
importfunctions = {
    "vcard":importFvcard,
    "csv":  importFcsv}
def importvcards(model, srcformat, infile, encoding):
    "Chooses the right importfunction according to srcformat"
    func = importfunctions.get(srcformat)
    if func:
        return func(model, infile, encoding)
    else:
        return None
     
importfiletypes = {
    "vcard":("vCard", "*.vcf"),
    "csv":  ("Comma Separated Text", "*.csv")}
def askimportfile(srcformat, master=None):
    "Ask for Import Filename"
    import tkFileDialog, os
    try:
        dir =  os.getcwd()
    except:
        dir = ""
    dlg = tkFileDialog.Open(master, filetypes=[importfiletypes[srcformat], ("All Files", "*")],
        initialdir=dir, initialfile="")
    return dlg.show()

importformats = [
    ("vcard","vCard 3.0 File (*.vcf)"),
    ("csv",  "Comma Separated Values (*.csv)")]
    
def Import(master, model, srcformat=None):
    "Show Import Dialog and do importing"
    if srcformat is None:
        from ImportExportDialog import ImportExportDialog
        dlg = ImportExportDialog(master, importformats, title="Import Contacts",
            headline="Import from:")
        res = dlg.activate()
        if res == 'Ok':
            dlg.deactivate()
            srcformat, srcencoding = dlg.getvalue()
        else: return
    filename = askimportfile(srcformat, master)
    if filename:
        fd = file(filename, 'rb')
        importvcards(model, srcformat, fd, srcencoding)
        fd.close()
        return True
    else:
        return False

exporttargets = [
    ("vcard","vCard 3.0 File (*.vcf)"),
    ("xmlrdf","XML-RDF (*.xml)"),
    ("csv",  "Comma Separated Values (*.csv), MSExcel compatible"),
    ("latex","LaTeX Document (*.tex)"),
    ("pine", "Pine Addressbook")]
        
def Export(master, model, targetformat=None, cardhandles=None):
    "Show Export Dialog and do exporting"
    if targetformat is None:
        from ImportExportDialog import ImportExportDialog
        if cardhandles and len(cardhandles) == 1:
            title = "Export Single Contact"
        else:
            title = "Export Contacts"
        dlg = ImportExportDialog(master, exporttargets, title=title, headline="Export to:")
        res = dlg.activate()
        if res == 'Ok':
            dlg.deactivate()
            targetformat, targetencoding = dlg.getvalue()
        else: return
    # For py2exe ModuleFinder:
    import codecs
    from encodings import latin_1
    filename = askexportfile(targetformat, master)
    if filename:
        fd = open(filename, "wb")
        outfile = EncodedFileWriter(fd, targetencoding)
        exportvcards(model, targetformat, outfile, targetencoding, cardhandles)
        fd.close()
        
