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

from qidicom import (reader, meta)


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.
    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.
    command.add_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 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 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 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 = 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())
