#!/usr/bin/env python
"""
Prints tab-delimited DICOM tag values.
"""
import os
import sys
import imp
import argparse
from collections import defaultdict
import qiutil
import qidicom


def main(argv=sys.argv):
    # Parse the command line arguments.
    files, opts = _parse_arguments()
    
    # The XNAT configuration.
    config = opts.pop('config', None)

    # Configure the logger.
    qiutil.command.configure_log('lsdicom', opts)

    # Print the tag value lines.
    _print_tag_values(*files, **opts)

    return 0


def _parse_arguments():
    """Parses the command line arguments."""
    parser = argparse.ArgumentParser()

    # The log options.
    qiutil.command.add_logging_options(parser)

    # The output content options.
    tag_grp = parser.add_mutually_exclusive_group()
    tag_grp.add_argument('-s', '--summary',
                         help='print only the Patient ID, Study Instance UID,'
                              'Series Instance UID and Instance Number tags',
                         action='store_true')
    tag_grp.add_argument('-t', '--tags',
                         help='the comma-separated tags to print'
                         '(default is all non-pixel, non-bracketed tags whose'
                         'value does not include a line break)')
    parser.add_argument(
        '-n', '--no-header', help='omit the tag name header line',
        dest='header', action='store_false')
    parser.add_argument(
        '-o', '--output', help='print the values to the given file')

    # The input file argument.
    parser.add_argument(
        'files', help='the DICOM files or directories to list (default stdin)',
        metavar='FILE', nargs='+')

    args = vars(parser.parse_args())
    nonempty_args = dict((k, v) for k, v in args.iteritems() if v != None)

    return nonempty_args.pop('files'), nonempty_args


def _print_tag_values(*files, **opts):
    """
    Prints the tab-delimited tag value lines.
    If there is only one file, then there is one line per
    tag. Otherwise, the output is formatted as a table
    with a header line if there is a header option and
    one line per file.

    :param files: the DICOM files
    :param opts: the print options
    :keyword tags: the tags to print (default all non-empty tags)
    :keyword output: the destination (default stdout)
    :keyword header: flag indicating whether to print a header line
        (default True)
    """
    if opts.get('summary'):
        tags = ImageHierarchy.TAGS
    elif 'tags' in opts:
        tags = opts['tags'].split(',')
    else:
        # The default tags include any tag defined in at least one file.
        # This preparatory step requires reading each DICOM file to
        # collect the tags before rereading the file to print the tag
        # values.
        tags = _collect_default_tags(*files)

    # The print output.
    output = opts.get('output') or sys.stdout
    
    # Print the tags in each DICOM file.
    logger(__name__).debug('Printing the DICOM tag values...')

    if len(files) == 1:
        # The DICOM data set.
        ds = next(hdr for hdr in qidicom.reader.iter_dicom_headers(*files))
        # The DICOM tag value dictionary.
        tdict = _read_tags(ds, *tags)
        tag_col_len = max([len(tag) for tag in tags])
        fmt = "%%-%ds\t%%s" % tag_col_len
        for tag in tags:
            print >> output, fmt % (tag, tdict[tag])
        return

    # Print the header, if necessary.
    if opts.get('header'):
        print >> output, '\t'.join(tags)

    for ds in qidicom.reader.iter_dicom_headers(*files):
        # The DICOM tag value dictionary.
        tdict = _read_tags(ds, *tags)
        # If there is at least one tag with a valid value, then print
        # the value line.
        if tdict:
            # Augment the tag dictionary with a default empty string
            # value for a missing tag.
            dd = defaultdict(str)
            dd.update(tdict)
            # Collect the (possibly empty) string values for each tag.
            values = [str(dd[t]) for t in tags]
            # Print the tab-delimited tag value line.
            print >> output, '\t'.join(values)


def _collect_default_tags(*files):
    """
    Collects the tags which occur in at least one of the given files.

    :param fps: the DICOM file paths, streams or datasets
    :return: the tags with a valid value
    """
    tag_set = set()
    for ds in qidicom.reader.iter_dicom_headers(*files):
        tdict = _read_tags(ds)
        tag_set.update(tdict.iterkeys())

    # Sort the tags
    tags = list(tag_set)
    tags.sort()

    return tags


def _read_tags(ds, *tags):
    """
    Reads the DICOM tags in the given file
    Filters the given tag dictionary to exclude empty or multi-line tag values.

    :param ds: the pydicom dataset
    :param tags: the DICOM tags to read (default all tags)
    :return: the filtered tag dictionary
    """
    tdict = qidicom.meta.select(ds, *tags)
    return {t: v for t, v in tdict.iteritems() if v and '\n' not in str(v)}

if __name__ == '__main__':
    sys.exit(main())
