#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim: tabstop=4 shiftwidth=4 softtabstop=4

"""OpenQuake: software for seismic hazard and risk assessment

It receives its inputs through a configuration file plus input data in .xml
format and stores the results in .xml format.

Available Hazard Analysis

  Classical PSHA
    Input   Source Model Logic Tree
            GMPE Logic Tree

    Output  Hazard maps
            Hazard curves

  Event-Based PSHA
    Input   Source Model Logic Tree
            GMPE Logic Tree

    Output  Ground Motion fields

  Scenario SHA
    Input   Rupture Model

    Output  Ground Motion fields

Available Risk Analysis

  Classical PSHA-based
    Input   Exposure (a value per asset)
            Vulnerability curves (a list of vulnerability functions)
            Seismic hazard input: hazard curves

    Output  A grid of loss-ratio curves
            A grid of loss curves
            A map of losses at each interval

  Probabilistic event-based
    Input   Exposure (a value per asset)
            Vulnerability curves (a list of vulnerability functions)
            Seismic hazard input: sets of ground motion fields

    Output  A grid of loss-ratio curves
            A grid of loss curves
            A map of losses at each interval
            An aggregated loss curve

"""

import argparse
import getpass
import logging
import os
import sys
sys.path.append('/Users/aholland/proj/hazard.d/oq.d/oq-engine')

from openquake.utils import config

config.abort_if_no_config_available()

try:
    import celeryconfig
except ImportError:
    sys.path.append('/usr/openquake')

import oqpath
oqpath.set_oq_path()

from openquake import __version__
from openquake.export import core as export
from openquake.utils import version as utils_version


def set_up_arg_parser():
    """Set up and return an :class:`argparse.ArgumentParser` with all of the
    OpenQuake command line options."""
    parser = argparse.ArgumentParser(
        description='Openquake - Seismic Hazard and Risk Analysis Engine')

    general_grp = parser.add_argument_group('General')
    general_grp.add_argument(
        '--version', action='store_true', help='display version information')
    general_grp.add_argument(
        '--force-inputs', action='store_true',
        help='parse model inputs and write them to the db no matter what')

    calc_grp = parser.add_argument_group('Run calculations')
    calc_grp.add_argument(
        '--config-file', '--config_file',
        help='run a calculation with the specifed config file',
        metavar='CONFIG_FILE')
    calc_grp.add_argument(
        '--output-type', '--output_type',
        help='defaults to "db"', required=False, choices=['db', 'xml'],
        default='db')
    calc_grp.add_argument(
        '--log-level', '-l',
        help='defaults to "warn"', required=False,
        choices=['debug', 'info', 'warn', 'error', 'critical'], default='warn')
    calc_grp.add_argument(
        '--log-file', '-L',
        help=('location to store log messages; if not specified, log messages'
              ' will be printed to the console'),
        required=False, metavar='LOG_FILE')

    export_grp = parser.add_argument_group('List and export')
    export_grp.add_argument(
        '--list-calculations',
        help='list completed calculations', action='store_true')
    export_grp.add_argument(
        '--list-outputs',
        help='list outputs for a completed calculation', type=int,
        metavar='CALCULATION_ID')
    export_grp.add_argument(
        '--export',
        help='export the desired output to the specified directory',
        nargs=2, metavar=('OUTPUT_ID', 'TARGET_DIR'))

    return parser


def _touch_log_file(log_file):
    """If a log file destination is specified, attempt to open the file in
    'append' mode ('a'). If the specified file is not writable, an
    :exception:`IOError` will be raised."""
    open(os.path.abspath(log_file), 'a').close()


def list_calculations():
    """Simple UI wrapper around
    :function:`openquake.export.core.get_jobs`. It prints the results in
    a nice way."""
    calcs = export.get_jobs(getpass.getuser())
    if len(calcs) > 0:
        print 'ID\tStatus\tDescription'
        for c in calcs:
            print '%s\t%s\t%s' % (c.id, c.status, c.description)


def list_outputs(job_id):
    """Simple UI wrapper arround
    :function:`openquake.export.core.get_outputs`. It prints the results in a
    nice way."""
    outputs = export.get_outputs(job_id)
    if len(outputs) > 0:
        print 'ID\tOuput Type'
        for o in outputs:
            print '%s\t%s' % (o.id, o.output_type)


def do_export(output_id, target_dir):
    """Simple UI wrapper around
    :function:`openquake.export.core.export`. It prints the results in a nice
    way."""
    from django.core.exceptions import ObjectDoesNotExist
    try:
        files = export.export(output_id, target_dir)
        if len(files) > 0:
            print 'Files Exported:'
            for f in files:
                print f
    except NotImplementedError, err:
        print err.message
        print 'This feature is probably not implemented yet'
    except ObjectDoesNotExist:
        print 'No output found for OUTPUT_ID %s' % output_id


if __name__ == '__main__':
    arg_parser = set_up_arg_parser()

    args = arg_parser.parse_args()

    if args.version:
        print utils_version.info(__version__)
    elif args.config_file is not None:
        from openquake import job
        from openquake import engine
        try:
            if args.log_file is not None:
                # Capture logging messages to a file.
                try:
                    _touch_log_file(args.log_file)
                except IOError as e:
                    raise IOError('Error writing to log file %s: %s'
                              % (args.log_file, e.strerror))

            user_name = getpass.getuser()
            ajob = engine.prepare_job(user_name)
            _, params, sections = engine.import_job_profile(
                args.config_file, ajob, user_name, args.force_inputs)
            engine.run_job(ajob, params, sections,
                           output_type=args.output_type,
                           log_level=args.log_level,
                           force_inputs=args.force_inputs,
                           log_file=args.log_file)
        except job.config.ValidationException as e:
            print str(e)
        except IOError as e:
            print str(e)
        except Exception as e:
            raise
    elif args.list_calculations:
        list_calculations()
    elif args.list_outputs is not None:
        list_outputs(args.list_outputs)
    elif args.export is not None:
        output_id, target_dir = args.export
        output_id = int(output_id)

        do_export(output_id, target_dir)
    else:
        arg_parser.print_usage()
