# -*- coding: utf-8 -*-
# Elisa - Home multimedia server
# Copyright (C) 2007 Fluendo Embedded S.L. (www.fluendo.com).
# All rights reserved.
#
# This file is available under one of two license agreements.
#
# This file is licensed under the GPL version 2.
# See "LICENSE.GPL" in the root of this distribution including a special
# exception to use Elisa with Fluendo's plugins.
#
# The GPL part of Elisa is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Elisa" in the root directory of this distribution package
# for details on that license.

"""
"""

__maintainer__ = 'Philippe Normand <philippe@fluendo.com>'
__maintainer2__ = 'Guido Amoruso <guidonte@fluendo.com>>'


import os
import re
import tempfile
import shutil

import sys
# FIXME: review me!
sys.path.insert(0, os.path.join(os.path.abspath(os.path.dirname(__file__)),
                                                '..', '..', '..'))
import pkg_resources
from distutils.core import run_setup
from setuptools import find_packages

FIELDS = ['name', 'license', 'author', 'summary', 'version', 'keywords',
          'requires', 'author-email', 'description']

def make_fake_egg(name, version='0.0.1', deps=[], keywords=['test']):
    """
    Build an egg file with some custom setup() parameters.

    @param name: name of the egg's distribution
    @type name:  string
    @param version: version of the egg to build
    @type version: string
    @param deps: required dependencies
    @type deps: list of strings
    @param keywords: list of keywords
    @type keywords: list of strings
    @returns: the egg filename and its data
    @rtype: tuple
    """
    egg_dir = tempfile.mkdtemp()

    plugin_name = name.lower()
    class_name = plugin_name.capitalize()
    distribution_name = 'elisa-plugin-' + name
    egg_info_dirname = 'elisa_plugin_' + name + '.egg-info'

    setup_string="""\
from setuptools import setup
setup(data_files=[],
          name=%r,
          elisa_infos={'plugin_name':'%s'},
          install_requires=%s,
          version=%r,
          description="Test egg",
          author="The Elisa Team",
          author_email="",
          maintainer='Pinco Pallino',
          maintainer_email="pinco@pallino.com",
          long_description="A fake egg, just for testing!",
          url="http://elisa.fluendo.com",
          packages=['elisa', 'elisa.plugins', 'elisa.plugins.%s'],
          keywords=%r,
          entry_points={'elisa.plugins' : ['%s = elisa.plugins.%s:%s']},
          namespace_packages=['elisa.plugins',])
          """ % (distribution_name,
                 plugin_name,
                 ['elisa-plugin-' + dep for dep in deps],
                 version,
                 plugin_name,
                 keywords,
                 plugin_name,
                 plugin_name,
                 class_name)

    fd, filename = tempfile.mkstemp(dir=egg_dir)
    os.write(fd, setup_string)
    os.close(fd)

    # build the egg
    pwd = os.getcwd()
    os.chdir(egg_dir)

    for dir in ['elisa', 'plugins', plugin_name]:
        os.mkdir(dir)
        os.chdir(dir)

        init = open("__init__.py", "wb")
        init.write("""\
__import__('pkg_resources').declare_namespace(__name__)

from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
""")
        init.close()

    init = open("__init__.py", "wb")

    init.write ("""
from elisa.core.plugin import Plugin

class %s(Plugin):
    version = '%s'

""" % (class_name,
       version))

    init.close()

    os.chdir(os.path.join("..", "..", ".."))

    dist = run_setup(filename, ('bdist_egg', '--dist-dir=%s' % egg_dir))
    os.chdir(pwd)

    # dist.dist_files is something like
    # [('bdist_egg', '2.5', u'dist/my_bundle-0.2-py2.5.egg')]
    egg_path = dist.dist_files[0][2]

    shutil.rmtree(os.path.join(egg_dir, egg_info_dirname), ignore_errors=False)

    # extract egg data
    filename = os.path.basename(egg_path)
    data = open(egg_path, 'rb').read()

    # remove temporary directory
    shutil.rmtree(egg_dir, ignore_errors=False)

    return (filename, data)


class EggParser(object):
    """
    DOCME
    """
    
    key_value_re = re.compile("([a-zA-Z\-]*): (.*)")
    elisa_key_value_re = re.compile("([a-z_]*)\s?=\s?(.*)")
    
    def __init__(self):
        """
        DOCME
        """
        self._eggs_dir = tempfile.mkdtemp(prefix='epr')

    def close(self):
        """
        DOCME
        """
        os.removedirs(self._eggs_dir)

    def parse_egg_file(self, egg_path):
        """
        DOCME
        """
        # FIXME: quick hack to support file:// URIs
        if egg_path.startswith("file:///"):
            egg_path = egg_path[len("file://"):]

        egg_metadata = {}        
        dists = list(pkg_resources.find_distributions(egg_path, True))
        if len(dists) > 0:
            dist = dists[0]
            egg_metadata = self._parse_distribution(dist)
        return egg_metadata
        
    def parse_egg_data(self, egg_filename, egg_data):
        """
        DOCME
        """                
        # put the egg in the environment
        full_path = os.path.join(self._eggs_dir, egg_filename)
        f = open(full_path, 'w')
        f.write(egg_data)
        f.close()

        metadata = self.parse_egg_file(full_path)
        os.unlink(full_path)
        return metadata
    
    def _parse_distribution(self, dist):
        egg_metadata = self._parse_pkg_info(dist)
        egg_metadata.update(self._parse_elisa_metadata(dist))
        egg_metadata.update(self._parse_entry_points(dist))
        egg_metadata.update(self._parse_requires(dist))
        return egg_metadata
   
    def _parse_pkg_info(self, dist):
        defaults = {'version': '0.0.1', 'license': 'UNKNOWN',
                    'summary': '', 'description': ''}
        pkg_info = {}
        metadata = dist.get_metadata('PKG-INFO')
        lines = metadata.splitlines()
        line_nb = 0
        while line_nb < len(lines):
            line = lines[line_nb]

            # is it a multi-lines value?
            if line.startswith(8*' '):
                saved = line_nb
                previous_line = lines[line_nb-1]
                match = self.key_value_re.search(previous_line)
                while not match and line_nb > 0:
                    line_nb -= 1
                    previous_line = lines[line_nb]
                    match = self.key_value_re.search(previous_line)
                    
                if match:
                    key, value = match.groups()
                    key = key.lower()
                    value = pkg_info[key]
                    line = line[8:]
                    pkg_info[key] = value + " " + line
                line_nb = saved
            else:
                match = self.key_value_re.search(line)
                if match:
                    key, value = match.groups()
                    key = key.lower()

                    if key in FIELDS:
                        if key in pkg_info:
                            previous = pkg_info[key]
                            if type(previous) == type(''):
                                pkg_info[key] = [previous, value]
                            else:
                                previous.append(value)
                                pkg_info[key] = previous
                        else:
                            pkg_info[key] = value
            line_nb += 1

        for key, value in defaults.iteritems():
            if key not in pkg_info:
                pkg_info[key] = value
            
        if 'keywords' in pkg_info:
            kw = pkg_info['keywords'].split(',')
            # FIXME: can keywords be space delimited too?
            pkg_info['keywords'] = kw
        return pkg_info

    def _parse_elisa_metadata(self, dist):
        metadata = {'py_deps': '', 'ext_deps': '', 'plugin_deps': '',
                    'category_id': 'unknown', 'replaces': '', 'bundles': '',
                    'replaces': '',
                    'needs_elisa_restart': 1, 'plugin_name': ''}

        if dist.has_metadata('elisa_infos.txt'):
            for line in dist.get_metadata_lines('elisa_infos.txt'):
                match = self.elisa_key_value_re.search(line)
                if match:
                    key, value = match.groups()
                    metadata[key] = value

        return metadata

    def _parse_entry_points(self, dist):
        entry_points = ""
        if dist.has_metadata('entry_points.txt'):
            entry_points = "\n".join(list(dist.get_metadata_lines('entry_points.txt')))
        metadata = {'entry_points': entry_points}
        return metadata

    def _parse_requires(self, dist):
        metadata = {'install_requires': [str(r) for r in dist.requires()]}
        return metadata

if __name__ == '__main__':
    import sys

    if len(sys.argv) > 1:
        egg = sys.argv[-1]
        parser = EggParser()
        print parser.parse_egg_file(egg)
        parser.close()
    else:
        filename, data = make_fake_egg('foo', version='0.0.1')
        tmpdir = tempfile.mkdtemp()
        egg_path = os.path.join(tmpdir, filename)
        egg_file = open(egg_path, 'wb')
        egg_file.write(data)
        egg_file.close()
        print "EGG: %s %s bytes" % (egg_path, len(data))

