# coding: utf-8

"""
Application to convert a bliss-hdf5 tomography dataset to Nexus - NXtomo (hdf5) format

.. program-output:: nxtomomill h52nx --help

For a complete tutorial you can have a look at: :ref:`Tomoh52nx`

"""

import argparse
import logging

from tqdm import tqdm

from nxtomomill import utils
from nxtomomill.converter import from_h5_to_nx
from nxtomomill.io.config.confighandler import (
    SETTABLE_PARAMETERS_UNITS,
    TomoHDF5ConfigHandler,
)

logging.basicConfig(level=logging.INFO)
_logger = logging.getLogger(__name__)


def _getPossibleInputParams():
    """

    :return: string with param1 (expected unit) ...
    """
    res = []
    for key, value in SETTABLE_PARAMETERS_UNITS.items():
        res.append(f"{key} ({value})")
    return ", ".join(res)


def _ask_for_selecting_detector(det_grps: list[str] | tuple[str]) -> str | None:
    res = input(
        "Several detector found. Only one detector is managed at the "
        "time. Please enter the name of the detector you want to use "
        f"or 'Cancel' to stop translation ({det_grps})"
    )
    if res == "Cancel":
        return None
    elif res in det_grps:
        return res
    else:
        # warning: this is not a debug log !!!
        print("detector name not recognized.")
        return _ask_for_selecting_detector(det_grps)


def main(argv):
    """ """
    parser = argparse.ArgumentParser(
        description="convert data acquired as "
        "hdf5 from bliss to nexus "
        "`NXtomo` classes. For `zseries` it will create one entry per `z`"
    )
    parser.add_argument(
        "input_file", help="master file of the " "acquisition", default=None, nargs="?"
    )
    parser.add_argument(
        "output_file", help="output .nx or .h5 file", default=None, nargs="?"
    )
    parser.add_argument(
        "--file-extension",
        "--file_extension",
        default=None,
        help="extension of the output file. Valid values are "
        "" + "/".join([item.value for item in utils.FileExtension]),
    )
    parser.add_argument(
        "--single-file",
        help="merge all scan sequence to the same output file. "
        "By default create one file per sequence and "
        "group all sequence in the output file",
        dest="single_file",
        action="store_true",
        default=None,  # default is None and not False for the ConfigHandler so it can knows when set by the user or not
    )
    parser.add_argument(
        "--with-master-file",
        help="Creates a master file linking all .nx files",
        dest="no_master_file",
        action="store_false",
        default=None,
    )
    parser.add_argument(
        "--overwrite",
        help="Do not ask for user permission to overwrite output files",
        action="store_true",
        default=None,  # default is None and not False for the ConfigHandler so it can knows when set by the user or not
    )
    parser.add_argument(
        "--debug",
        help="Set logs to debug mode",
        action="store_true",
        default=None,  # default is None and not False for the ConfigHandler so it can knows when set by the user or not
    )
    parser.add_argument(
        "--entries",
        help="Specify (root) entries to be converted. By default it will try "
        "to convert all existing entries.",
        default=None,
    )
    parser.add_argument(
        "--sub-entries-to-ignore",
        "--ignore-sub-entries",
        help="Specify (none-root) sub entries to ignore.",
        default=None,
    )
    parser.add_argument(
        "--raises-error",
        help="Raise errors if some data are not met instead of providing some"
        " default values",
        action="store_true",
        default=None,  # default is None and not False for the ConfigHandler so it can knows when set by the user or not
    )
    parser.add_argument(
        "--field-of-view",
        help="Force the output to be `Half`  or `Full` acquisition. Otherwise "
        "parse raw data to find this information.",
        default=None,
    )
    parser.add_argument(
        "--no-input",
        "--no-input-for-missing-information",
        help="The user won't be ask for any inputs",
        dest="request_input",
        action="store_false",
        default=None,  # default is None and not False for the ConfigHandler so it can knows when set by the user or not
    )
    parser.add_argument(
        "--data-copy",
        "--copy-data",
        help="Force data duplication of frames. This will permit to have an "
        "'all-embed' file. Otherwise the detector/data dataset will haves "
        "links to other files.",
        action="store_true",
        dest="default_data_copy",
        default=None,  # default is None and not False for the ConfigHandler so it can knows when set by the user or not
    )
    parser.add_argument(
        "--sample_x_keys",
        "--sample-x-keys",
        default=None,
        help="x translation key in bliss HDF5 file",
    )
    parser.add_argument(
        "--sample_y_keys",
        "--sample-y-keys",
        default=None,
        help="y translation key in bliss HDF5 file",
    )
    parser.add_argument(
        "--translation_z_keys",
        "--translation-z-keys",
        default=None,
        help="z translation key in bliss HDF5 file",
    )
    parser.add_argument(
        "--sample-detector-distance-keys",
        "--sample-detector-distance-paths",
        "--distance-paths",
        default=None,
        help="sample detector distance paths",
    )
    parser.add_argument(
        "--valid_camera_names",
        "--valid-camera-names",
        default=None,
        help="Valid NXDetector dataset name to be considered. Otherwise will"
        "try to deduce them from NX_class attibute (value should be"
        "NXdetector) or from instrument group child structure.",
    )
    parser.add_argument(
        "--rotation_angle_keys",
        "--rotation-angle-keys",
        "--rot-angle-keys",
        "--rot_angle_keys",
        default=None,
        help="Valid dataset name for rotation angle",
    )
    parser.add_argument(
        "--exposure_time_keys",
        "--exposure-time-keys",
        "--acq_expo_time_keys",
        "--acq-expo-time-keys",
        default=None,
        help="Valid dataset name for acquisition exposure time",
    )
    parser.add_argument(
        "--sample-x-pixel-size_keys",
        "--sample_x_pixel_size_keys",
        default=None,
        help="X pixel size key to read",
    )
    parser.add_argument(
        "--sample-y-pixel-size_keys",
        "--sample_y_pixel_size_keys",
        default=None,
        help="Y pixel size key to read",
    )

    # scan titles
    parser.add_argument(
        "--init_titles",
        "--init-titles",
        default=None,
        help="Titles corresponding to init scans",
    )
    parser.add_argument(
        "--zseries-init-titles",
        "--zseries_init_titles",
        "--z_series_init_titles",
        "--z-series_init_titles",
        "--init_zserie_titles",
        "--init-zserie-titles",
        default=None,
        help="Titles corresponding to zserie init scans",
    )
    parser.add_argument(
        "--multitomo-init-titles",
        "--multitomo_init_titles",
        "--multi-tomo-init-titles",
        "--multi_tomo_init_titles",
        "--init-multi-tomo-titles",
        "--init-pcotomo-titles",
        default=None,
        help="Titles corresponding to multi-tomo init scans",
    )
    parser.add_argument(
        "--back-and-forth-init-titles",
        "--back_and_forth_init_titles",
        "--init-back-and-forth-titles",
        "--init_back_and_forth_titles",
        default=None,
        help="Titles corresponding to back-and-forth init scans",
    )
    parser.add_argument(
        "--dark-titles",
        "--dark_titles",
        default=None,
        help="Titles corresponding to dark scans",
    )
    parser.add_argument(
        "--flat-titles",
        "--flat_titles",
        "--ref_titles",
        "--ref-titles",
        default=None,
        help="Titles corresponding to ref scans",
        dest="flat_titles",
    )
    parser.add_argument(
        "--projection_titles",
        "--proj_titles",
        "--proj-titles",
        default=None,
        help="Titles corresponding to projection scans",
    )
    parser.add_argument(
        "--alignment-titles",
        "--alignment_titles",
        "--align_titles",
        "--align-titles",
        default=None,
        help="Titles corresponding to alignment scans",
    )
    parser.add_argument(
        "--set-params",
        default=None,
        nargs="*",
        help="Allow manual definition of some parameters. "
        "Valid parameters (and expected input unit) "
        f"are: {_getPossibleInputParams()}. Should be added at the end of the command line because "
        "will try to cover all text set after this option.",
    )

    # config file
    parser.add_argument(
        "--config-file",
        "--config",
        "--configuration",
        "--configuration-file",
        default=None,
        help="file containing the full configuration to convert from h5 "
        "bliss to nexus",
    )

    options = parser.parse_args(argv[1:])
    if options.request_input:
        callback_det_sel = _ask_for_selecting_detector
    else:
        callback_det_sel = None
    try:
        configuration_handler = TomoHDF5ConfigHandler(options)
    except Exception:
        _logger.error("Fail to initiate the configuration.", exc_info=True)
        return
    else:
        for title in configuration_handler.configuration.init_titles:
            assert title != ""
        logging.getLogger("nxtomomill").setLevel(
            configuration_handler.configuration.log_level
        )
        from_h5_to_nx(
            configuration=configuration_handler.configuration,
            progress=tqdm(desc="h52nx", delay=1, bar_format="{l_bar}{bar}"),
            input_callback=None,
            detector_sel_callback=callback_det_sel,
        )
