#!/usr/bin/env python

# Copyright (C) 2010-2011, Aleksey Lim
#
# 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/>.

import os
import re
import sys
import shutil
import gettext
import subprocess
from fnmatch import fnmatch
from ConfigParser import ConfigParser
from os.path import dirname, abspath, join, exists, isdir, islink, basename


_IGNORE_FILES = [
        '.git', '.gitignore', '.tmp', '*.pyc', '*.pyo', '*.~',
        '*.bak', '*.la', '*.tar.*', '.sweets', '/debian/**',
        ]


def glob(path, include=None, exclude=None):
    result = []
    include = include or []
    exclude = (exclude or []) + _IGNORE_FILES

    def is_path_filter(filter_value):
        return '/' in filter_value or '**' in filter_value

    for root, dirs, files in os.walk(path):
        rel_path = root[len(path):].lstrip(os.sep)

        for filename in dirs[:]:

            def name_match(filters):
                for i in filters:
                    if not is_path_filter(i) and fnmatch(filename, i):
                        return True

            if name_match(exclude):
                dirs.remove(filename)

        links_to_dirs = []
        for filename in os.listdir(root):
            if islink(join(root, filename)) and isdir(join(root, filename)):
                links_to_dirs.append(filename)

        for filename in files + links_to_dirs:
            file_path = join(rel_path, filename)

            def match(filters):
                for filter_value in filters:
                    if not is_path_filter(filter_value):
                        matched = fnmatch(filename, filter_value)
                    else:
                        if filter_value.startswith('/'):
                            filter_value = filter_value[1:]
                        regexp = ''
                        for i in filter_value.replace('**', '\0'):
                            if i == '?':
                                regexp += '[^/]'
                            elif i == '*':
                                regexp += '[^/]*'
                            elif i == '\0':
                                regexp += '.*'
                            else:
                                regexp += re.escape(i)
                        regexp += '$'
                        matched = re.match(regexp, file_path)
                    if matched:
                        return True
                return False

            if (not include or match(include)) and not match(exclude):
                result.append(file_path)

    return result


def move(pms, srcdir, destdir, include, exclude):
    sweet = basename(destdir)
    include = include.split(';') if include else []
    exclude = exclude.split(';') if exclude else []
    result = []

    if pms == 'rpm':
        result.append('%defattr(-,root,root,-)')
        entry_mask = '/opt/sweets/%(sweet)s/%(path)s'
    else:
        entry_mask = 'debian/sweets-%(sweet)s/opt/sweets/%(sweet)s/%(path)s ' \
                     '/opt/sweets/%(sweet)s/%(dir)s'

    for src in glob(srcdir, include=include, exclude=exclude):
        dst = join(destdir, src)
        if exists(dst):
            continue

        if not exists(dirname(dst)):
            os.makedirs(dirname(dst))

        if not _fix_install_paths(srcdir, src, dst, sweet):
            shutil.move(join(srcdir, src), dst)

        src = src.replace(' ', '?').strip(os.sep)
        result.append(entry_mask % \
                {'sweet': sweet,
                 'path': src,
                 'dir': dirname(src),
                 })

    return result


def cmd_activity(args):
    assert len(args) >= 1, 'Pass destdir path'
    destdir = args.pop(0)

    cmd_link([destdir, '', ''])

    if not exists('po'):
        return

    info_file = join('activity', 'activity.info')
    assert exists(info_file), 'Cannot find activity.info file'
    info = ConfigParser()
    info.read(info_file)

    assert info.has_option('Activity', 'bundle_id'), \
            'Cannot find bundle_id option in activity.info'
    bundle_id = info.get('Activity', 'bundle_id')

    assert info.has_option('Activity', 'name'), \
            'Cannot find name option in activity.info'
    name = info.get('Activity', 'name')

    localedir = join(destdir, 'locale')
    shutil.rmtree(localedir, ignore_errors=True)

    for f in os.listdir('po'):
        if not f.endswith('.po') or f == 'pseudo.po':
            continue

        file_name = join('po', f)
        lang = f[:-3]

        mo_path = join(localedir, lang, 'LC_MESSAGES')
        if not isdir(mo_path):
            os.makedirs(mo_path)

        mo_file = join(mo_path, '%s.mo' % bundle_id)
        args = ['msgfmt', '--output-file=%s' % mo_file, file_name]
        if subprocess.call(args):
            print 'msgfmt failed for %s' % file_name
            continue

        cat = gettext.GNUTranslations(open(mo_file, 'r'))
        translated_name = cat.gettext(name)
        linfo_file = join(localedir, lang, 'activity.linfo')
        f = open(linfo_file, 'w')
        f.write('[Activity]\nname = %s\n' % translated_name)
        f.close()


def cmd_link(args):
    destdir = args.pop(0)

    include = args.pop(0)
    include = include.split(';') if include else []

    exclude = args.pop(0)
    exclude = exclude.split(';') if exclude else []

    for src in glob('.', include=include, exclude=exclude):
        dst = join(destdir, src)
        if exists(dst):
            continue
        if not exists(dirname(dst)):
            os.makedirs(dirname(dst))
        try:
            os.link(src, dst)
        except OSError:
            shutil.copy(src, dst)


def cmd_move(args):
    pms, srcdir, destdir, include, exclude = args
    for i in move(pms, srcdir, destdir, include, exclude):
        print i


def _fix_install_paths(srcdir, src, dst, sweet):
    content = ''

    if src.endswith('.pc'):
        for line in file(join(srcdir, src)).readlines():
            match = re.match('prefix\s*=\s*/(.*)$', line)
            if match:
                pc_prefix = \
                        '${pcfiledir}' + '/..' * (len(src.split(os.sep)) - 1)
                line = 'prefix=%s/%s\n' % (pc_prefix, match.groups()[0])
            content += line
    elif src.endswith('.service'):
        for line in file(join(srcdir, src)).readlines():
            match = re.match('Exec\s*=\s*/(/.*)$', line)
            if match:
                line = 'Exec = /opt/sweets/%s%s\n' % (sweet, match.groups()[0])
            content += line
    else:
        return False

    f = file(dst, 'w')
    f.write(content)
    f.close()

    return True


def main():
    args = sys.argv[1:]
    cmd = args.pop(0)

    if 'cmd_' + cmd not in globals():
        print 'Wrong command %s' % cmd
        exit(1)

    globals()['cmd_' + cmd](args)


if __name__ == '__main__':
    main()
