# -*- coding: utf-8 -*-
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vi: set ft=python sts=4 ts=4 sw=4 et:
from __future__ import (print_function, division, unicode_literals,
                        absolute_import)

import os

from .base import TraitedSpec, CommandLineInputSpec, CommandLine, File, isdefined, traits
from ..utils.filemanip import fname_presuffix
from ..external.due import BibTeX

pvc_methods = [
    'GTM', 'IY', 'IY+RL', 'IY+VC', 'LABBE', 'LABBE+MTC', 'LABBE+MTC+RL',
    'LABBE+MTC+VC', 'LABBE+RBV', 'LABBE+RBV+RL', 'LABBE+RBV+VC', 'MG', 'MG+RL',
    'MG+VC', 'MTC', 'MTC+RL', 'MTC+VC', 'RBV', 'RBV+RL', 'RBV+VC', 'RL', 'VC'
]


class PETPVCInputSpec(CommandLineInputSpec):
    in_file = File(
        desc="PET image file", exists=True, mandatory=True, argstr="-i %s")
    out_file = File(
        desc="Output file", genfile=True, hash_files=False, argstr="-o %s")
    mask_file = File(
        desc="Mask image file", exists=True, mandatory=True, argstr="-m %s")
    pvc = traits.Enum(
        pvc_methods, desc="Desired PVC method", mandatory=True, argstr="-p %s")
    fwhm_x = traits.Float(
        desc="The full-width at half maximum in mm along x-axis",
        mandatory=True,
        argstr="-x %.4f")
    fwhm_y = traits.Float(
        desc="The full-width at half maximum in mm along y-axis",
        mandatory=True,
        argstr="-y %.4f")
    fwhm_z = traits.Float(
        desc="The full-width at half maximum in mm along z-axis",
        mandatory=True,
        argstr="-z %.4f")
    debug = traits.Bool(
        desc="Prints debug information",
        usedefault=True,
        default_value=False,
        argstr="-d")
    n_iter = traits.Int(
        desc="Number of iterations", default_value=10, usedefault=True,
        argstr="-n %d")
    n_deconv = traits.Int(
        desc="Number of deconvolution iterations",
        default_value=10,
        usedefault=True,
        argstr="-k %d")
    alpha = traits.Float(
        desc="Alpha value", default_value=1.5, usedefault=True,
        argstr="-a %.4f")
    stop_crit = traits.Float(
        desc="Stopping criterion", default_value=0.01, usedefault=True,
        argstr="-a %.4f")


class PETPVCOutputSpec(TraitedSpec):
    out_file = File(desc="Output file")


class PETPVC(CommandLine):
    """ Use PETPVC for partial volume correction of PET images.

    PETPVC is a software from the Nuclear Medicine Department
    of the UCL University Hospital, London, UK.

    Its source code is here: https://github.com/UCL/PETPVC

    The methods that it implement are explained here:
    K. Erlandsson, I. Buvat, P. H. Pretorius, B. A. Thomas, and B. F. Hutton,
    "A review of partial volume correction techniques for emission tomography
    and their applications in neurology, cardiology and oncology," Phys. Med.
    Biol., vol. 57, no. 21, p. R119, 2012.

    Its command line help shows this:

       -i --input < filename >
          = PET image file
       -o --output < filename >
          = Output file
       [ -m --mask < filename > ]
          = Mask image file
       -p --pvc < keyword >
          = Desired PVC method
       -x < X >
          = The full-width at half maximum in mm along x-axis
       -y < Y >
          = The full-width at half maximum in mm along y-axis
       -z < Z >
          = The full-width at half maximum in mm along z-axis
       [ -d --debug ]
          = Prints debug information
       [ -n --iter [ Val ] ]
          = Number of iterations
            With: Val (Default = 10)
       [ -k [ Val ] ]
          = Number of deconvolution iterations
            With: Val (Default = 10)
       [ -a --alpha [ aval ] ]
          = Alpha value
            With: aval (Default = 1.5)
       [ -s --stop [ stopval ] ]
          = Stopping criterion
            With: stopval (Default = 0.01)

    Technique - keyword
    -------------------
    - Geometric transfer matrix - "GTM"
    - Labbe approach - "LABBE"
    - Richardson-Lucy - "RL"
    - Van-Cittert - "VC"
    - Region-based voxel-wise correction - "RBV"
    - RBV with Labbe - "LABBE+RBV"
    - RBV with Van-Cittert - "RBV+VC"
    - RBV with Richardson-Lucy - "RBV+RL"
    - RBV with Labbe and Van-Cittert - "LABBE+RBV+VC"
    - RBV with Labbe and Richardson-Lucy- "LABBE+RBV+RL"
    - Multi-target correction - "MTC"
    - MTC with Labbe - "LABBE+MTC"
    - MTC with Van-Cittert - "MTC+VC"
    - MTC with Richardson-Lucy - "MTC+RL"
    - MTC with Labbe and Van-Cittert - "LABBE+MTC+VC"
    - MTC with Labbe and Richardson-Lucy- "LABBE+MTC+RL"
    - Iterative Yang - "IY"
    - Iterative Yang with Van-Cittert - "IY+VC"
    - Iterative Yang with Richardson-Lucy - "IY+RL"
    - Muller Gartner - "MG"
    - Muller Gartner with Van-Cittert - "MG+VC"
    - Muller Gartner with Richardson-Lucy - "MG+RL"

    Examples
    --------
    >>> from ..testing import example_data
    >>> #TODO get data for PETPVC
    >>> pvc = PETPVC()
    >>> pvc.inputs.in_file   = 'pet.nii.gz'
    >>> pvc.inputs.mask_file = 'tissues.nii.gz'
    >>> pvc.inputs.out_file  = 'pet_pvc_rbv.nii.gz'
    >>> pvc.inputs.pvc = 'RBV'
    >>> pvc.inputs.fwhm_x = 2.0
    >>> pvc.inputs.fwhm_y = 2.0
    >>> pvc.inputs.fwhm_z = 2.0
    >>> outs = pvc.run() #doctest: +SKIP
    """
    input_spec = PETPVCInputSpec
    output_spec = PETPVCOutputSpec
    _cmd = 'petpvc'

    references_ = [{
        'entry':
        BibTeX(
            "@article{0031-9155-61-22-7975,"
            "author={Benjamin A Thomas and Vesna Cuplov and Alexandre Bousse and "
            "Adriana Mendes and Kris Thielemans and Brian F Hutton and Kjell Erlandsson},"
            "title={PETPVC: a toolbox for performing partial volume correction "
            "techniques in positron emission tomography},"
            "journal={Physics in Medicine and Biology},"
            "volume={61},"
            "number={22},"
            "pages={7975},"
            "url={http://stacks.iop.org/0031-9155/61/i=22/a=7975},"
            "doi={https://doi.org/10.1088/0031-9155/61/22/7975},"
            "year={2016},"
            "}"),
        'description':
        'PETPVC software implementation publication',
        'tags': ['implementation'],
    }]

    def _list_outputs(self):
        outputs = self.output_spec().get()
        outputs['out_file'] = self.inputs.out_file
        if not isdefined(outputs['out_file']):
            method_name = self.inputs.pvc.lower()
            outputs['out_file'] = self._gen_fname(
                self.inputs.in_file, suffix='_{}_pvc'.format(method_name))

        outputs['out_file'] = os.path.abspath(outputs['out_file'])
        return outputs

    def _gen_fname(self,
                   basename,
                   cwd=None,
                   suffix=None,
                   change_ext=True,
                   ext='.nii.gz'):
        """Generate a filename based on the given parameters.

        The filename will take the form: cwd/basename<suffix><ext>.
        If change_ext is True, it will use the extentions specified in
        <instance>intputs.output_type.

        Parameters
        ----------
        basename : str
            Filename to base the new filename on.
        cwd : str
            Path to prefix to the new filename. (default is os.getcwd())
        suffix : str
            Suffix to add to the `basename`.  (defaults is '' )
        change_ext : bool
            Flag to change the filename extension to the given `ext`.
            (Default is False)

        Returns
        -------
        fname : str
            New filename based on given parameters.

        """
        if basename == '':
            msg = 'Unable to generate filename for command %s. ' % self.cmd
            msg += 'basename is not set!'
            raise ValueError(msg)
        if cwd is None:
            cwd = os.getcwd()
        if change_ext:
            if suffix:
                suffix = ''.join((suffix, ext))
            else:
                suffix = ext
        if suffix is None:
            suffix = ''
        fname = fname_presuffix(
            basename, suffix=suffix, use_ext=False, newpath=cwd)
        return fname

    def _gen_filename(self, name):
        if name == 'out_file':
            return self._list_outputs()['out_file']
        return None
