"""Print the metadata for one or more Python package distributions.

Usage:  %prog [options] path+

Each 'path' entry can be one of the following:

o a source distribution:  in this case, 'path' should point to an existing
  archive file (.tar.gz, .tar.bz2, or .zip) as generated by 'setup.py sdist'.

o a binary distribution:  in this case, 'path' should point to an existing
  archive file (.egg)

o a "develop" checkout:  in this case,  'path' should point to a directory
  initialized via 'setup.py develop' (under setuptools).

o an installed package:  in this case, 'path' should be the importable name
  of the package.
"""
try:
    from configparser import ConfigParser
except ImportError:  # pragma: NO COVER
    from ConfigParser import ConfigParser
from collections import OrderedDict
from csv import writer
import json
import optparse
import os
import sys

from .utils import get_metadata


def _parse_options(args=None):
    parser = optparse.OptionParser(usage=__doc__)

    parser.add_option("-m", "--metadata-version", default=None,
                      help="Override metadata version")

    parser.add_option("-f", "--field", dest="fields", action="append",
                      help="Specify an output field (repeatable)",
                      )

    parser.add_option("-d", "--download-url-prefix",
                      dest="download_url_prefix",
                      help="Download URL prefix",
                      )

    parser.add_option("--simple", dest="output", action="store_const",
                      const='simple', default='simple',
                      help="Output as simple key-value pairs",
                      )

    parser.add_option("-s", "--skip", dest="skip", action="store_true",
                      default=True,
                      help="Skip missing values in simple output",
                     )

    parser.add_option("-S", "--no-skip", dest="skip", action="store_false",
                      help="Don't skip missing values in simple output",
                     )

    parser.add_option("--single", dest="output", action="store_const",
                      const='single',
                      help="Output delimited values",
                      )

    parser.add_option("--item-delim", dest="item_delim", action="store",
                      default=';',
                      help="Delimiter for fields in single-line output",
                      )

    parser.add_option("--sequence-delim", dest="sequence_delim",
                      action="store", default=',',
                      help="Delimiter for multi-valued fields",
                      )

    parser.add_option("--csv", dest="output", action="store_const",
                      const='csv',
                      help="Output as CSV",
                      )

    parser.add_option("--ini", dest="output", action="store_const",
                      const='ini',
                      help="Output as INI",
                      )

    parser.add_option("--json", dest="output", action="store_const",
                      const='json',
                      help="Output as JSON",
                      )

    options, args = parser.parse_args(args)

    if len(args)==0:
        parser.error("Pass one or more files or directories as arguments.")
    else:
        return options, args

class Base(object):
    _fields = None
    def __init__(self, options):
        if options.fields:
            self._fields = options.fields

    def finish(self):  # pragma: NO COVER
        pass

class Simple(Base):
    def __init__(self, options):
        super(Simple, self).__init__(options)
        self._skip = options.skip

    def __call__(self, meta):
        for field in self._fields or list(meta):
            value = getattr(meta, field)
            if (not self._skip) or (value is not None and value!=()):
                print("%s: %s" % (field, value))

class SingleLine(Base):
    _fields = None
    def __init__(self, options):
        super(SingleLine, self).__init__(options)
        self._item_delim = options.item_delim
        self._sequence_delim = options.sequence_delim

    def __call__(self, meta):
        if self._fields is None:
            self._fields = list(meta)
        values = []
        for field in self._fields:
            value = getattr(meta, field)
            if isinstance(value, (tuple, list)):
                value = self._sequence_delim.join(value)
            else:
                value = str(value)
            values.append(value)
        print(self._item_delim.join(values))

class CSV(Base):
    _writer = None
    def __init__(self, options):
        super(CSV, self).__init__(options)
        self._sequence_delim = options.sequence_delim

    def __call__(self, meta):
        if self._fields is None:
            self._fields = list(meta) # first dist wins
        fields = self._fields
        if self._writer is None:
            self._writer = writer(sys.stdout)
            self._writer.writerow(fields)
        values = []
        for field in fields:
            value = getattr(meta, field)
            if isinstance(value, (tuple, list)):
                value = self._sequence_delim.join(value)
            else:
                value = str(value)
            values.append(value)
        self._writer.writerow(values)

class INI(Base):
    _fields = None
    def __init__(self, options):
        super(INI, self).__init__(options)
        self._parser = ConfigParser()

    def __call__(self, meta):
        name = meta.name
        version = meta.version
        section = '%s-%s' % (name, version)
        if self._parser.has_section(section):
            raise ValueError('Duplicate distribution: %s' % section)
        self._parser.add_section(section)
        for field in self._fields or list(meta):
            value = getattr(meta, field)
            if isinstance(value, (tuple, list)):
                value = '\n\t'.join(value)
            self._parser.set(section, field, value)

    def finish(self):
        self._parser.write(sys.stdout)  # pragma: NO COVER

class JSON(Base):
    _fields = None
    def __init__(self, options):
        super(JSON, self).__init__(options)
        self._mapping = OrderedDict()

    def __call__(self, meta):
        if self._fields is None:
            self._fields = list(meta)
        for field in self._fields:
            value = getattr(meta, field)
            if value and not isinstance(value, (tuple, list)):
                value = str(value)
            if field in self._mapping:
                raise ValueError('Duplicate field: %(field)r' % locals())
            self._mapping[field] = value

    def finish(self):
        json.dump(self._mapping, sys.stdout, indent=2)

_FORMATTERS = {
    'simple': Simple,
    'single': SingleLine,
    'csv': CSV,
    'ini': INI,
    'json': JSON,
}

def main(args=None):
    """Entry point for pkginfo tool
    """
    options, paths = _parse_options(args)
    format = getattr(options, 'output', 'simple')
    formatter = _FORMATTERS[format](options)

    for path in paths:
        meta = get_metadata(path, options.metadata_version)
        if meta is None:
            continue

        if options.download_url_prefix:
            if meta.download_url is None:
                filename = os.path.basename(path)
                meta.download_url = '%s/%s' % (options.download_url_prefix,
                                               filename)

        formatter(meta)

    formatter.finish()
