#!/usr/bin/env python3
"""This script fetches from
http://freedict.svn.sf.net/svnroot/freedict/trunk/freedict-database.xml
all the available databases. It accepts the following options:

    -dc    generate debian/control an debian/dictionarylist
    -f    fetch new source from SourceForge and save it into the file
          ../freedict_VER.orig.tar.gz (where VER is date in format yyyy.mm.dd)
    -x    fetch new xml freedict database
    -na <database>   output name and author of <database> (e. g. lat-deu)
    -a    perform all actions (except fetching new xml database)

IMPORTANT: you must run this script from the root of the freedict source,
otherwise the operations will fail.

Dependencies:
    tar, unzip, svn

NOTE: this program works with python 3, legacy code for support will be removed.

NOTE[2]: This code has been written with the strategy pattern in mind.
"""

import sys, collections
import os, datetime, codecs
import xml.etree.ElementTree as ET

# py2/3-stuff
if(sys.version[0] == '3'):
    from urllib.request import urlopen
else:
    from urllib import urlopen
def Print(*args):
    """Dummy Print."""
    sys.stdout.write(' '.join(args)+'\n')

XML_URL = 'http://freedict.svn.sf.net/svnroot/freedict/trunk/freedict-database.xml'
LANGCODE_TABLE = "debian/iso-639-3_20130123.tab"

def dictionarycod2longdescription(string):
    """
    Take a iso-639 dictionary string (like lat-deu) and translate it into the
    long version (like Latin-German)."""
    for item in os.listdir('debian'):
        if(item.startswith('iso-')):
            tmp = codecs.open(os.path.join('debian', item), 'r', 'utf-8').read()
            tmp = tmp.split('\n')
    langtbl = {}
    for line in tmp[1:]:
        line = line.split('\t')
        langtbl[line[0]] = line[6]
        if(line[1] != ''): # some have two language code, add it:
            langtbl[line[1]] = line[6]
    string = string.split('-')
    return langtbl[string[0]] +'-' + langtbl[string[1]]

def get_xml_content(fetch_new_db=False):
    """
    Check, whether debian/freedict-database.xml exists and is not older than
    ../freedict_VER.orig.tar.gz; if older download automatically, if not, use
    that instead of downloading again.
    """
    fetch_new = True
    if(fetch_new_db):
        # new source, new XML:
        fetch_new = True
    elif(os.path.exists('debian/freedict-database.xml')):
        # modification time of last xml:
        modified = datetime.datetime.fromtimestamp(
                os.path.getmtime('debian/freedict-database.xml') )
        # latest freedict version:
        names = [e for e in os.listdir('..') if e.endswith('.orig.tar.gz') and
                e.startswith('freedict')]
        names.sort()
        version = names[-1].split('_')[1]
        version = version.split('.')[:3]
        version = [int(e) for e in version]
        version = datetime.datetime( version[0], version[1], version[2])
        if(modified < version): # fetch new
            fetch_new = True
        else:
            fetch_new = False
    else:
        fetch_new = True
    data = None
    if(fetch_new):
        # fetch data
        data = urlopen(XML_URL).read()
    else: # read from file
        data = codecs.open('debian/freedict-database.xml', 'r', 'utf-8').read()
    # make str, when py3
    # save back, when specified:
    if(fetch_new):
        f = codecs.open('debian/freedict-database.xml','w', 'utf-8')
        f.write( data )
        f.close()
    return data


class generate_control():
    def __init__(self, root):
        self.__dictionaries = {}
        self.root = root
        self.parse_data()

    def parse_data(self):
        """Iterate over XML tree to collect dictionary data."""
        for child in self.root:
            attr = child.attrib # shortcut
            self.__dictionaries[child.attrib['name']] = (attr['headwords'],\
                            attr['edition'], attr['status'])

    def write_data(self):
        """Generate debian/control from debian/control.HEAD and the gathered
        dictionary data."""
        HEAD = codecs.open('debian/control.HEAD', 'r', 'utf-8').read() +'\n'
        self.sort_dictionaries()
        string = [HEAD]
        string += ["""Package: dict-freedict-all
Architecture: all
Depends: ${misc:Depends}, """ + 'dict-freedict-' + ', dict-freedict-'.join( self.__dictionaries.keys() )
                ]
        string += ["\nDescription: meta-package to install or dictionary databases from the FreeDict project\n",
                " This package can be used to install all available bilingual dictionaries from the FreeDict project at once.",
                "\n\n"]

        for dict, content in self.__dictionaries.items():
            string.append('Package: dict-freedict-%s\n' % dict)
            string.append("""Architecture: all
Depends: ${misc:Depends}
Suggests: dictd | serpento, dict | opendict | kdict | gnome-dictionary
Provides: dictd-dictionary\n""")
            string.append('Description: %s dictionary for the dict server/client'\
                    % dictionarycod2longdescription(dict))
            longstr = '''
 This is the %s dictionary from the FreeDict project in version %s. It contains %s headwords (FreeDict status: %s). It can be used for the dict server in conjunction with a dict client.'''\
                    % (dictionarycod2longdescription( dict ), content[1], \
                    content[0], content[2])
            # bring the description to 80 chars each line
            tmp = ' '
            for piece in longstr.split(' '):
                if(len(tmp+piece) <= 80):
                    tmp += piece + ' '
                else:
                    string.append(tmp[:-1]+'\n')
                    tmp = ' '+piece+ ' '
            string.append(tmp+'\n\n')
        codecs.open('debian/control','w', 'utf-8').write( ''.join(string) )

    def sort_dictionaries(self):
        """
        Overwrite the self.__dictionaries-dictionary with a sorted
        collectionss.OrderedDict. We cannot expect to find ordered data in the
        XML, so we should sort on our own, afterwards.
        """
        d = collections.OrderedDict()
        keylist = list(self.__dictionaries.keys())
        keylist.sort()
        for key in keylist:
            d[key] = self.__dictionaries[key]
        self.__dictionaries = d


class generate_language_list():
    def __init__(self, root):
        self.file_name = 'debian/dictionarylist'
        self.root = root

    def write_data(self):
        """Just write all available into a list for usage in make rules / other
        scripts."""
        dicts = []
        for child in self.root:
            dicts.append( child.attrib['name'] )
        dicts.sort()
        codecs.open(self.file_name, 'w', 'utf-8').write(' '.join( dicts ))


class fetch_source():
    def __init__(self, root):
        self.date = self.gen_date()
        self.dirname = 'freedict-%s.orig' % self.date
        self.root = root

    def gen_date(self):
        """Return date in format "yyyy.mm.dd"."""
        d = datetime.datetime.now()
        return str(d.year) + '.' + str(d.month).zfill(2).replace(' ','0') \
                + '.' + str(d.day).zfill(2).replace(' ','0')

    def prepare_environment(self):
        """
        Perform all actions which are needed before downloading the
        source.
        """
        os.mkdir(self.dirname)
        os.chdir( self.dirname )

    def clean_up(self):
        """
        Compress the original source, move it to the right destination and
        remove download directory.
        """
        os.chdir('..')
        os.system('tar czf %s.tar.gz %s' % (self.dirname.replace('-','_'),
                self.dirname))
        print('Moving tar archive upward to "..".')
        os.rename(self.dirname+'.tar.gz', '..'+os.sep+self.dirname+'.tar.gz')
        os.system('rm -r %s' % self.dirname)

    def write_data(self):
        """Download all upstream source packages."""
        self.prepare_environment()
        imported = 0
        for dict in self.root:
            for release in dict:
                try:
                    if(release.attrib['platform'] == 'src'):
                        srcURL = release.attrib['URL']
                        fn = self.get_dict_fn_from_url ( release.attrib['URL'] )
                        data = urlopen( srcURL ).read()
                        Print("Fetching", srcURL)
                        open( fn, 'wb').write( data )
                        Print("Extracting",fn)
                        if(fn.endswith('.zip')):
                            os.system('unzip -qq "%s"' % fn)
                        elif(fn.endswith('tar.gz') or fn.endswith('tar.bz2')):
                            os.system('tar xf "%s"' % fn)
                        elif(fn.endswith('.bz2')):
                            os.mkdir(fn[:7])
                            os.system('tar xf "%s" -C "%s"' % (fn, fn[:7]))
                        else:
                            Print('E: unknown format of "%s".' % fn)
                            sys.exit(0)
                        os.remove(fn)
                        imported += 1
                except KeyError: pass
        print("Imported",str(imported),"dictionaries.")
        # fetch tools directory
        print("Fetching tools directory")
        ret=os.system('svn export http://svn.code.sf.net/p/freedict/code/trunk/tools')
        if(ret != 0):
            print("Error while fetching the tools directory.")
            sys.exit(127)
        self.clean_up()

    def get_dict_fn_from_url(self, url):
        """The SF URL's end on '?download' or '/download', just before those
        strings in the file name, so remove that and return the file name."""
        if(url.endswith('?download')):
            # that's len('?download')
            return url.split('/')[-1][:-9]
        elif(url.endswith('/download')):
            return url.split('/')[-2]
        else:
            Print("Unknown URL Format:", url)
            sys.exit(127)

class name_and_author():
    def __init__(self, tree):
        self.tree = tree
    def write_data(self):
        """Iterate over XML tree to collect dictionary data. Afterwards decide,
        wether sys.argv[2] is in there and Print() name and author."""
        try:
            dict = sys.argv[2]
        except IndexError:
            Print("Error: you must specify a dictionary.")
            sys.exit(127)

        for child in self.tree:
            attr = child.attrib # shortcut
            if(attr['name'] == dict):
                if(attr['maintainerName'] == '[up for grabs]'):
                    dict = (dictionarycod2longdescription( attr['name'] ), \
                                'up for adoption')
                else:
                    dict = (dictionarycod2longdescription( attr['name'] ),\
                                attr['maintainerName'])
        if(type(dict) == str):
            Print("Error: database not found.")
            sys.exit(127)
        else:
            Print(dict[0]+';'+dict[1])

def clean_up_tree(root):
    """Iterate over XML tree and delete those <dictionary/>-nodes which have no
    release."""
    for child in root:
        if(len(child.getchildren()) == 0):
            print('Removing '+child.attrib['name']+' (is empty).')
            root.remove(child)

def main():
    # are we in the correct directory?
    if(os.getcwd().endswith('debian')):
        os.chdir('..')
    elif(not os.getcwd().find('freedict') >= 0):
        Print("You must run this script from the FreeDict packaging root.")
        sys.exit(127)

    # cmd args
    xmlsrc = None
    if(len(sys.argv) == 1):
        Print(__doc__)
        sys.exit(127)

    xmlsrc = get_xml_content()
    if(sys.argv[1] == '-f'):
        objects = [fetch_source]
    elif(sys.argv[1] == '-dc'):
        objects = [generate_control, generate_language_list]
    elif(sys.argv[1] == '-a'):
        objects = [generate_control, generate_language_list, fetch_source]
    elif(sys.argv[1] == '-x'):
        # fetch again, but this time from the web, overhead should not be
        # perceptible
        xmlsrc = get_xml_content(fetch_new_db=True)
        sys.exit(0)
    elif(sys.argv[1] == '-na'):
        objects = [name_and_author]
    else:
        Print(__doc__)
        sys.exit(127)

    # usual operation
    root = ET.fromstring(xmlsrc)
    clean_up_tree(root)
    for obj in objects:
        inst = obj(root)
        inst.write_data()

main()
