# Copyright (C) 2012-2024  C-PAC Developers
# This file is part of C-PAC.
# C-PAC is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
# C-PAC is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with C-PAC. If not, see <https://www.gnu.org/licenses/>.
import os
from CPAC.utils.monitoring import IFLOGGER
[docs]
def compute_fisher_z_score(correlation_file, timeseries_one_d):
    """
    Computes the fisher z transform of the input correlation map
    If the correlation map contains data for multiple ROIs then
    the function returns z score for each ROI as a seperate nifti
    file.
    Parameters
    ----------
    correlation_file : string
        Input correlations file
    Returns
    -------
    out_file : list (nifti files)
        list of z_scores for mask or ROI
    """
    import os
    import numpy as np
    import nibabel as nib
    roi_numbers = []
    if "#" in open(timeseries_one_d, "r").readline().rstrip("\r\n"):
        roi_numbers = (
            open(timeseries_one_d, "r")
            .readline()
            .rstrip("\r\n")
            .replace("#", "")
            .split("\t")
        )
    corr_img = nib.load(correlation_file)
    corr_data = corr_img.get_fdata()
    hdr = corr_img.header
    corr_data = np.log((1 + corr_data) / (1 - corr_data)) / 2.0
    dims = corr_data.shape
    out_file = []
    if len(dims) == 5 or len(roi_numbers) > 0:
        if len(dims) == 5:
            x, y, z, one, roi_number = dims
            corr_data = np.reshape(corr_data, (x * y * z, roi_number), order="F")
        for i in range(0, len(roi_numbers)):
            sub_data = corr_data
            if len(dims) == 5:
                sub_data = np.reshape(corr_data[:, i], (x, y, z), order="F")
            sub_img = nib.Nifti1Image(
                sub_data, header=corr_img.header, affine=corr_img.affine
            )
            sub_z_score_file = os.path.join(
                os.getcwd(), "z_score_ROI_number_%s.nii.gz" % (roi_numbers[i])
            )
            sub_img.to_filename(sub_z_score_file)
            out_file.append(sub_z_score_file)
    else:
        z_score_img = nib.Nifti1Image(corr_data, header=hdr, affine=corr_img.affine)
        z_score_file = os.path.join(os.getcwd(), "z_score.nii.gz")
        z_score_img.to_filename(z_score_file)
        out_file.append(z_score_file)
    return out_file 
def check_ts(in_file):
    import os
    import numpy as np
    if in_file.endswith(".txt"):
        try:
            timepoints, rois = np.loadtxt(in_file).shape
        except ValueError:
            timepoints = np.loadtxt(in_file).shape[0]
            rois = 1
        out_file = in_file
    elif in_file.endswith(".csv") or in_file.endswith(".1D"):
        csv_array = np.genfromtxt(in_file, delimiter=",")
        if np.isnan(csv_array[0][0]):
            csv_array = csv_array[1:]
        timepoints, rois = csv_array.shape
        # multiple regression (fsl_glm) needs this format for -design input
        out_file = os.path.join(
            os.getcwd(), os.path.basename(in_file).replace(".csv", ".txt")
        )
        np.savetxt(out_file, csv_array, delimiter="\t")
    if rois > timepoints:
        message = (
            f"\n\n\n****The number of timepoints ({timepoints}) is smaller than the"
            f" number of ROIs to run ({rois}) - therefore the GLM is underspecified"
            " and can't run.****\n\n\n"
        )
        raise ValueError(message)
    else:
        return out_file
[docs]
def map_to_roi(timeseries, maps):
    r"""
    Renames the outputs of the temporal multiple regression workflow for sca
    according to the header information of the timeseries.txt file that was
    passed
    NOTE: This is only run if the temporal regression is run as part of sca
          (which = 'RT') when calling the temporal regression workflow.
          If you run the temporal regression workflow manually, don\'t set
          (which = 'RT') unless you provide a timeseries.txt file with a header
          containing the names of the timeseries.
    Parameters
    ----------
    timeseries : string
        Input timeseries.txt file
    maps : List (nifti files)
        List of output files generated by the temporal regression workflow if
        (which == 'RT')
    Returns
    -------
    labels : List (strings)
        List of names that the output files should be renamed to
    maps : List (nifti files)
        List of output files generated by the temporal regression workflow if
        (which == 'RT')
    """
    import pandas as pd
    testMat = pd.read_csv(timeseries)
    timepoints, rois = testMat.shape
    if rois > timepoints:
        IFLOGGER.warning(
            "The number of timepoints is smaller than the number "
            "of ROIs to run - therefore the GLM is "
            "underspecified and can't run."
        )
    # pull in the ROI labels from the header of the extracted timeseries
    # CSV file
    with open(timeseries, "r") as f:
        roi_file_lines = f.read().splitlines()
    roi_err = (
        "\n\n[!] The output of 3dROIstats, used in extracting "
        "the timeseries, was not in the expected format.\n\nROI "
        f"output file: {timeseries}\n\n"
    )
    for line in roi_file_lines:
        if "Mean_" in line:
            try:
                roi_list = line.split(",")
                # clear out any blank strings/non ROI labels in the list
                roi_list = [x for x in roi_list if "Mean" in x]
                # rename labels
                roi_list = [
                    x.replace("Mean", "sca_tempreg_z_maps_roi")
                    .replace(" ", "")
                    .replace("#", "")
                    for x in roi_list
                ]
            except:
                raise Exception(roi_err)
            break
    else:
        raise Exception(roi_err)
    new_labels = []
    for roi_label in roi_list:
        new_labels.append(os.path.join(os.getcwd(), roi_label))
    len(maps)
    maps.sort()
    # if not numMaps / 2 == rois:
    #     raise Exception('You specified {0} timeseries but only {1} spatial '
    #                     'maps were generated'.format(str(rois),
    #                                                  str(numMaps/2)))
    maps = maps[:rois]
    return roi_list, maps