#! /usr/bin/env python

from cStringIO import StringIO
import os
import sys

import libxml2
import libxslt
import zipfile
from optparse import OptionParser

try:
    import xml.etree.ElementTree as ET
except ImportError:
    import elementtree.ElementTree as ET


base_dir = os.path.dirname(os.path.abspath(__file__))

OFFICE_NS = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'
TEXT_NS = 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'
TABLE_NS = 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'
STYLE_NS = 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'

def get_as_pt(value):
    unit = value[-2:]
    qty = float(value[:-2])
    if unit == 'cm':
        return qty / 0.03514598035
    elif unit == 'pt':
        return qty

def get_as_cm(value):
    unit = value[-2:]
    qty = float(value[:-2])
    if unit == 'cm':
        return qty
    elif unit == 'pt':
        return qty * 0.03514598035

def convert(input, output):
    extra_files = []
    try:
        z = zipfile.ZipFile(input)
    except zipfile.BadZipfile:
        content = file(input).read()
    else:
        content_zfile = None
        for zfile in z.namelist():
            if zfile == 'contents.xml':
                content_zfile = zfile
            else:
                # extra files
                extra_files.append((zfile, z.read(zfile)))
        if content_zfile is None:
            # missing contents.xml
            sys.exit(1)
        content = z.read(content_zfile)

    styledoc = libxml2.parseFile(os.path.join(base_dir, 'legi2odf.xsl'))
    style = libxslt.parseStylesheetDoc(styledoc)
    doc = libxml2.parseMemory(content, len(content))
    result = style.applyStylesheet(doc, None)
    data = style.saveResultToString(result)
    style.freeStylesheet()
    result.freeDoc()

    styledoc = libxml2.parseFile(os.path.join(base_dir, 'legi2odf-meta.xsl'))
    style = libxslt.parseStylesheetDoc(styledoc)
    result = style.applyStylesheet(doc, None)
    meta = style.saveResultToString(result)
    style.freeStylesheet()
    result.freeDoc()

    doc.freeDoc()

    # second pass, for things that are too difficult to handle in xsl
    content_tree = ET.ElementTree(ET.fromstring(data))

    styles = content_tree.findall('.//{%s}automatic-styles' % OFFICE_NS)[0]

    for i, table in enumerate(content_tree.findall('.//{%s}table' % TABLE_NS)):
        table.attrib['{%s}style-name' % TABLE_NS] = 'Tab.%s' % i
        colspecs = table.findall('{urn:tabellio}colspec')
        for j, colspec in enumerate(reversed(colspecs)):
            style = ET.SubElement(styles, '{%s}style' % STYLE_NS)
            style_name = 'Tab.%s.%s' % (i, j)
            style.attrib['{%s}name' % STYLE_NS] = style_name
            style.attrib['{%s}family' % STYLE_NS] = 'table-column'
            props = ET.SubElement(style, '{%s}table-column-properties' % STYLE_NS)
            props.attrib['{%s}column-width' % STYLE_NS] = colspec.attrib['{urn:tabellio}colwidth']
            tablecol = ET.Element('{%s}table-column' % TABLE_NS,
                    {'{%s}style-name' % TABLE_NS: style_name})
            table.insert(0, tablecol)

        style = ET.SubElement(styles, '{%s}style' % STYLE_NS)
        style_name = 'Tab.%s' % i
        style.attrib['{%s}name' % STYLE_NS] = style_name
        style.attrib['{%s}family' % STYLE_NS] = 'table'
        props = ET.SubElement(style, '{%s}table-properties' % STYLE_NS)
        props.attrib['{%s}align' % TABLE_NS] = 'left'

        def sum_pt(colspecs):
            total = 0
            for colspec in colspecs:
                width = colspec.attrib['{urn:tabellio}colwidth']
                total += get_as_pt(width)
            return '%spt' % total

        props.attrib['{%s}width' % STYLE_NS] = sum_pt(colspecs)


    out = StringIO()
    content_tree.write(out)
    data = out.getvalue()

    if output == '-':
        print data
        return

    z = zipfile.ZipFile(output, 'w')
    z.write(os.path.join(base_dir, '../template-pcf/styles.xml'), 'styles.xml')
    z.write(os.path.join(base_dir, '../template-pcf/Configurations2/menubar/menubar.xml'),
            'Configurations2/menubar/menubar.xml')
    z.writestr('content.xml', data)
    z.writestr('meta.xml', meta)
    for filename, filecontent in extra_files:
        z.writestr('Pictures/%s' % filename, filecontent)
    extra_manifest_entries = '\n'.join([' <manifest:file-entry manifest:media-type="" manifest:full-path="Pictures/%s"/>\n' % x[0] for x in extra_files])
    z.writestr('META-INF/manifest.xml', '''<?xml version="1.0" encoding="UTF-8"?>
<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
 <manifest:file-entry manifest:media-type="application/vnd.oasis.opendocument.text" manifest:full-path="/"/>
 <manifest:file-entry manifest:media-type="text/xml" manifest:full-path="content.xml"/>
 <manifest:file-entry manifest:media-type="text/xml" manifest:full-path="styles.xml"/>
 <manifest:file-entry manifest:media-type="" manifest:full-path="Configurations2/menubar/menubar.xml"/>
 %s
</manifest:manifest>''' % extra_manifest_entries)
    z.close()


def main():
    parser = OptionParser()
    options, args = parser.parse_args()
    if len(args) == 2:
        convert(args[0], args[1])
    else:
        if args[0].endswith('.legi'):
            dest = args[0].replace('.legi', '.odt')
        elif args[0].endswith('.xml'):
            dest = args[0].replace('.xml', '.odt')
        else:
            dest = args[0] + '.odt'
        convert(args[0], dest)


if __name__ == '__main__':
    main()

