#!/usr/bin/env python
# Copyright (c) 2015, Ecole Polytechnique Federale de Lausanne, Blue Brain Project
# All rights reserved.
#
# This file is part of NeuroM <https://github.com/BlueBrain/NeuroM>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#     1. Redistributions of source code must retain the above copyright
#        notice, this list of conditions and the following disclaimer.
#     2. Redistributions in binary form must reproduce the above copyright
#        notice, this list of conditions and the following disclaimer in the
#        documentation and/or other materials provided with the distribution.
#     3. Neither the name of the copyright holder nor the names of
#        its contributors may be used to endorse or promote products
#        derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

'''Examples of extracting basic statistics'''
import argparse
import json
import logging
import os
import sys

import pkg_resources

import neurom as nm
from neurom import exceptions
from neurom.apps import get_config
from neurom.apps.morph_stats import (extract_stats, generate_flattened_dict,
                                     get_header, sanitize_config)
from neurom.io.utils import get_files_by_path
from neurom.utils import NeuromJSON

L = logging.getLogger(__name__)

CONFIG_PATH = pkg_resources.resource_filename(
    'neurom', 'config')

IGNORABLE_EXCEPTIONS = {
    'SomaError': exceptions.SomaError,
}


def get_parser():
    '''Parse command line arguments'''
    parser = argparse.ArgumentParser(
        description='Morphology statistics extractor',
        epilog='Example config: {}'.format(os.path.join(CONFIG_PATH, 'morph_stats.yaml')))

    parser.add_argument('datapath', nargs='?',
                        help='Path to a morphology data file or a directory')

    parser.add_argument('-v', '--verbose', action='count', dest='verbose', default=0,
                        help='-v for INFO, -vv for DEBUG')
    parser.add_argument('-l', '--list', action='store_true', default=False,
                        help='List the available features')

    parser.add_argument('--as-population',
                        action='store_true',
                        default=False,
                        help='If enabled the directory is treated as a population')

    parser.add_argument('-C', '--config', help='Configuration File')

    parser.add_argument('-o', '--output', dest='output_file',
                        help=('Summary output file name, if it ends in .json, '
                              'a json file is created, if .csv, then a csv file'))

    parser.add_argument('-I', '--ignored-exceptions', dest='ignored_exceptions',
                        default=[], choices=IGNORABLE_EXCEPTIONS, action='append',
                        help='Exception to ignore')

    return parser


def main(args):
    '''main function'''
    try:
        config = get_config(args.config, os.path.join(CONFIG_PATH, 'morph_stats.yaml'))
        config = sanitize_config(config)
    except exceptions.ConfigError as e:
        L.error(str(e))
        sys.exit(1)

    ignored_exceptions = tuple(IGNORABLE_EXCEPTIONS[k] for k in args.ignored_exceptions)
    neurons = nm.load_neurons(get_files_by_path(args.datapath),
                              ignored_exceptions=ignored_exceptions)

    results = {}
    if args.as_population:
        results[args.datapath] = extract_stats(neurons, config)
    else:
        from tqdm import tqdm
        for neuron in tqdm(neurons):
            results[neuron.name] = extract_stats(neuron, config)

    if not args.output_file:
        print(json.dumps(results, indent=2, separators=(',', ':'), cls=NeuromJSON))
    elif args.output_file.endswith('.json'):
        with open(args.output_file, 'w') as output_file:
            json.dump(results, output_file, cls=NeuromJSON)
    elif args.output_file.endswith('.csv'):
        import csv
        with open(args.output_file, 'w') as output_file:
            csvwriter = csv.writer(output_file)
            header = get_header(results)
            csvwriter.writerow(header)
            for line in generate_flattened_dict(header, dict(results)):
                csvwriter.writerow(line)


if __name__ == '__main__':
    _args = get_parser().parse_args()
    logging.basicConfig(level=(logging.WARNING,
                               logging.INFO,
                               logging.DEBUG)[min(_args.verbose, 2)])

    if _args.list:
        print(nm.fst._get_doc())  # pylint: disable=W0212
        sys.exit(0)
    elif not _args.datapath:
        get_parser().print_usage()
        sys.exit('Need a "datapath" to operate on')

    main(_args)
