"""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.
"""
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()
