# This file was auto generated by Styx.
# Do not edit this file directly.

import typing
import pathlib
from styxdefs import *

SURFACE_DISTORTION_METADATA = Metadata(
    id="6b557cde1c22a8589b67dd2c10b7db6b8b4df56a.workbench",
    name="surface-distortion",
    package="workbench",
    container_image_tag="nx10x/workbench:2.1.0",
)


_SurfaceDistortionSmoothParamsDictNoTag = typing.TypedDict('_SurfaceDistortionSmoothParamsDictNoTag', {
    "sigma": float,
    "fwhm": bool,
})
SurfaceDistortionSmoothParamsDictTagged = typing.TypedDict('SurfaceDistortionSmoothParamsDictTagged', {
    "@type": typing.Literal["smooth"],
    "sigma": float,
    "fwhm": bool,
})
SurfaceDistortionSmoothParamsDict = _SurfaceDistortionSmoothParamsDictNoTag | SurfaceDistortionSmoothParamsDictTagged


_SurfaceDistortionMatchSurfaceAreaParamsDictNoTag = typing.TypedDict('_SurfaceDistortionMatchSurfaceAreaParamsDictNoTag', {
    "roi-metric": typing.NotRequired[InputPathType | None],
})
SurfaceDistortionMatchSurfaceAreaParamsDictTagged = typing.TypedDict('SurfaceDistortionMatchSurfaceAreaParamsDictTagged', {
    "@type": typing.Literal["match-surface-area"],
    "roi-metric": typing.NotRequired[InputPathType | None],
})
SurfaceDistortionMatchSurfaceAreaParamsDict = _SurfaceDistortionMatchSurfaceAreaParamsDictNoTag | SurfaceDistortionMatchSurfaceAreaParamsDictTagged


_SurfaceDistortionParamsDictNoTag = typing.TypedDict('_SurfaceDistortionParamsDictNoTag', {
    "surface-reference": InputPathType,
    "surface-distorted": InputPathType,
    "metric-out": str,
    "smooth": typing.NotRequired[SurfaceDistortionSmoothParamsDict | None],
    "match-surface-area": typing.NotRequired[SurfaceDistortionMatchSurfaceAreaParamsDict | None],
    "log2": typing.NotRequired[bool | None],
    "edge-method": bool,
    "caret5-method": bool,
})
SurfaceDistortionParamsDictTagged = typing.TypedDict('SurfaceDistortionParamsDictTagged', {
    "@type": typing.Literal["workbench/surface-distortion"],
    "surface-reference": InputPathType,
    "surface-distorted": InputPathType,
    "metric-out": str,
    "smooth": typing.NotRequired[SurfaceDistortionSmoothParamsDict | None],
    "match-surface-area": typing.NotRequired[SurfaceDistortionMatchSurfaceAreaParamsDict | None],
    "log2": typing.NotRequired[bool | None],
    "edge-method": bool,
    "caret5-method": bool,
})
SurfaceDistortionParamsDict = _SurfaceDistortionParamsDictNoTag | SurfaceDistortionParamsDictTagged


def surface_distortion_smooth(
    sigma: float,
    fwhm: bool = False,
) -> SurfaceDistortionSmoothParamsDictTagged:
    """
    Build parameters.
    
    Args:
        sigma: the size of the smoothing kernel in mm, as sigma by default.
        fwhm: kernel size is FWHM, not sigma.
    Returns:
        Parameter dictionary
    """
    params = {
        "@type": "smooth",
        "sigma": sigma,
        "fwhm": fwhm,
    }
    return params


def surface_distortion_smooth_validate(
    params: typing.Any,
) -> None:
    """
    Validate parameters. Throws an error if `params` is not a valid
    `SurfaceDistortionSmoothParamsDict` object.
    
    Args:
        params: The parameters object to validate.
    """
    if params is None or not isinstance(params, dict):
        raise StyxValidationError(f'Params object has the wrong type \'{type(params)}\'')
    if params.get("sigma", None) is None:
        raise StyxValidationError("`sigma` must not be None")
    if not isinstance(params["sigma"], (float, int)):
        raise StyxValidationError(f'`sigma` has the wrong type: Received `{type(params.get("sigma", None))}` expected `float`')
    if params.get("fwhm", False) is None:
        raise StyxValidationError("`fwhm` must not be None")
    if not isinstance(params["fwhm"], bool):
        raise StyxValidationError(f'`fwhm` has the wrong type: Received `{type(params.get("fwhm", False))}` expected `bool`')


def surface_distortion_smooth_cargs(
    params: SurfaceDistortionSmoothParamsDict,
    execution: Execution,
) -> list[str]:
    """
    Build command-line arguments from parameters.
    
    Args:
        params: The parameters.
        execution: The execution object for resolving input paths.
    Returns:
        Command-line arguments.
    """
    cargs = []
    cargs.extend([
        "-smooth",
        str(params.get("sigma", None))
    ])
    if params.get("fwhm", False):
        cargs.append("-fwhm")
    return cargs


def surface_distortion_match_surface_area(
    roi_metric: InputPathType | None = None,
) -> SurfaceDistortionMatchSurfaceAreaParamsDictTagged:
    """
    Build parameters.
    
    Args:
        roi_metric: only use the surface area within a given ROI (e.g., to\
            exclude the medial wall)\
            \
            the ROI to use, as a metric file.
    Returns:
        Parameter dictionary
    """
    params = {
        "@type": "match-surface-area",
    }
    if roi_metric is not None:
        params["roi-metric"] = roi_metric
    return params


def surface_distortion_match_surface_area_validate(
    params: typing.Any,
) -> None:
    """
    Validate parameters. Throws an error if `params` is not a valid
    `SurfaceDistortionMatchSurfaceAreaParamsDict` object.
    
    Args:
        params: The parameters object to validate.
    """
    if params is None or not isinstance(params, dict):
        raise StyxValidationError(f'Params object has the wrong type \'{type(params)}\'')
    if params.get("roi-metric", None) is not None:
        if not isinstance(params["roi-metric"], (pathlib.Path, str)):
            raise StyxValidationError(f'`roi-metric` has the wrong type: Received `{type(params.get("roi-metric", None))}` expected `InputPathType | None`')


def surface_distortion_match_surface_area_cargs(
    params: SurfaceDistortionMatchSurfaceAreaParamsDict,
    execution: Execution,
) -> list[str]:
    """
    Build command-line arguments from parameters.
    
    Args:
        params: The parameters.
        execution: The execution object for resolving input paths.
    Returns:
        Command-line arguments.
    """
    cargs = []
    cargs.append("-match-surface-area")
    if params.get("roi-metric", None) is not None:
        cargs.extend([
            "-roi",
            execution.input_file(params.get("roi-metric", None))
        ])
    return cargs


class SurfaceDistortionOutputs(typing.NamedTuple):
    """
    Output object returned when calling `SurfaceDistortionParamsDict(...)`.
    """
    root: OutputPathType
    """Output root folder. This is the root folder for all outputs."""
    metric_out: OutputPathType
    """the output distortion metric"""


def surface_distortion_params(
    surface_reference: InputPathType,
    surface_distorted: InputPathType,
    metric_out: str,
    smooth: SurfaceDistortionSmoothParamsDict | None = None,
    match_surface_area: SurfaceDistortionMatchSurfaceAreaParamsDict | None = None,
    log2: bool | None = None,
    edge_method: bool = False,
    caret5_method: bool = False,
) -> SurfaceDistortionParamsDictTagged:
    """
    Build parameters.
    
    Args:
        surface_reference: the reference surface.
        surface_distorted: the distorted surface.
        metric_out: the output distortion metric.
        smooth: smooth the area data.
        match_surface_area: isotropically rescale the distorted surface so that\
            it has the same surface area as the reference surface.
        log2: calculate distortion by the local affines between triangles\
            \
            apply base-2 log transform.
        edge_method: calculate distortion of edge lengths rather than areas.
        caret5_method: use the surface distortion method from caret5.
    Returns:
        Parameter dictionary
    """
    params = {
        "@type": "workbench/surface-distortion",
        "surface-reference": surface_reference,
        "surface-distorted": surface_distorted,
        "metric-out": metric_out,
        "edge-method": edge_method,
        "caret5-method": caret5_method,
    }
    if smooth is not None:
        params["smooth"] = smooth
    if match_surface_area is not None:
        params["match-surface-area"] = match_surface_area
    if log2 is not None:
        params["log2"] = log2
    return params


def surface_distortion_validate(
    params: typing.Any,
) -> None:
    """
    Validate parameters. Throws an error if `params` is not a valid
    `SurfaceDistortionParamsDict` object.
    
    Args:
        params: The parameters object to validate.
    """
    if params is None or not isinstance(params, dict):
        raise StyxValidationError(f'Params object has the wrong type \'{type(params)}\'')
    if params.get("surface-reference", None) is None:
        raise StyxValidationError("`surface-reference` must not be None")
    if not isinstance(params["surface-reference"], (pathlib.Path, str)):
        raise StyxValidationError(f'`surface-reference` has the wrong type: Received `{type(params.get("surface-reference", None))}` expected `InputPathType`')
    if params.get("surface-distorted", None) is None:
        raise StyxValidationError("`surface-distorted` must not be None")
    if not isinstance(params["surface-distorted"], (pathlib.Path, str)):
        raise StyxValidationError(f'`surface-distorted` has the wrong type: Received `{type(params.get("surface-distorted", None))}` expected `InputPathType`')
    if params.get("metric-out", None) is None:
        raise StyxValidationError("`metric-out` must not be None")
    if not isinstance(params["metric-out"], str):
        raise StyxValidationError(f'`metric-out` has the wrong type: Received `{type(params.get("metric-out", None))}` expected `str`')
    if params.get("smooth", None) is not None:
        surface_distortion_smooth_validate(params["smooth"])
    if params.get("match-surface-area", None) is not None:
        surface_distortion_match_surface_area_validate(params["match-surface-area"])
    if params.get("log2", None) is not None:
        if not isinstance(params["log2"], bool):
            raise StyxValidationError(f'`log2` has the wrong type: Received `{type(params.get("log2", None))}` expected `bool | None`')
    if params.get("edge-method", False) is None:
        raise StyxValidationError("`edge-method` must not be None")
    if not isinstance(params["edge-method"], bool):
        raise StyxValidationError(f'`edge-method` has the wrong type: Received `{type(params.get("edge-method", False))}` expected `bool`')
    if params.get("caret5-method", False) is None:
        raise StyxValidationError("`caret5-method` must not be None")
    if not isinstance(params["caret5-method"], bool):
        raise StyxValidationError(f'`caret5-method` has the wrong type: Received `{type(params.get("caret5-method", False))}` expected `bool`')


def surface_distortion_cargs(
    params: SurfaceDistortionParamsDict,
    execution: Execution,
) -> list[str]:
    """
    Build command-line arguments from parameters.
    
    Args:
        params: The parameters.
        execution: The execution object for resolving input paths.
    Returns:
        Command-line arguments.
    """
    cargs = []
    cargs.extend([
        "wb_command",
        "-surface-distortion"
    ])
    cargs.append(execution.input_file(params.get("surface-reference", None)))
    cargs.append(execution.input_file(params.get("surface-distorted", None)))
    cargs.append(params.get("metric-out", None))
    if params.get("smooth", None) is not None or params.get("match-surface-area", None) is not None:
        cargs.extend([
            *(surface_distortion_smooth_cargs(params.get("smooth", None), execution) if (params.get("smooth", None) is not None) else []),
            *(surface_distortion_match_surface_area_cargs(params.get("match-surface-area", None), execution) if (params.get("match-surface-area", None) is not None) else [])
        ])
    if params.get("log2", None) is not None:
        cargs.extend([
            "-local-affine-method",
            "-log2"
        ])
    if params.get("edge-method", False):
        cargs.append("-edge-method")
    if params.get("caret5-method", False):
        cargs.append("-caret5-method")
    return cargs


def surface_distortion_outputs(
    params: SurfaceDistortionParamsDict,
    execution: Execution,
) -> SurfaceDistortionOutputs:
    """
    Build outputs object containing output file paths and possibly stdout/stderr.
    
    Args:
        params: The parameters.
        execution: The execution object for resolving input paths.
    Returns:
        Outputs object.
    """
    ret = SurfaceDistortionOutputs(
        root=execution.output_file("."),
        metric_out=execution.output_file(params.get("metric-out", None)),
    )
    return ret


def surface_distortion_execute(
    params: SurfaceDistortionParamsDict,
    runner: Runner | None = None,
) -> SurfaceDistortionOutputs:
    """
    MEASURE DISTORTION BETWEEN SURFACES.
    
    This command, when not using -caret5-method, -edge-method, or
    -local-affine-method, is equivalent to using -surface-vertex-areas on each
    surface, smoothing both output metrics with the GEO_GAUSS_EQUAL method on
    the surface they came from if -smooth is specified, and then using the
    formula 'ln(distorted/reference)/ln(2)' on the smoothed results.
    
    When using -caret5-method, it uses the surface distortion method from
    caret5, which takes the base 2 log of the ratio of tile areas, then averages
    those results at each vertex, and then smooths the result on the reference
    surface.
    
    When using -edge-method, the -smooth option is ignored, and the output at
    each vertex is the average of 'abs(ln(refEdge/distortEdge)/ln(2))' over all
    edges connected to the vertex.
    
    When using -local-affine-method, the -smooth option is ignored. The output
    is two columns, the first is the area distortion ratio, and the second is
    anisotropic strain. These are calculated by an affine transform between
    matching triangles, and then averaged across the triangles of a vertex.
    
    Args:
        params: The parameters.
        runner: Command runner.
    Returns:
        NamedTuple of outputs (described in `SurfaceDistortionOutputs`).
    """
    surface_distortion_validate(params)
    runner = runner or get_global_runner()
    execution = runner.start_execution(SURFACE_DISTORTION_METADATA)
    params = execution.params(params)
    cargs = surface_distortion_cargs(params, execution)
    ret = surface_distortion_outputs(params, execution)
    execution.run(cargs)
    return ret


def surface_distortion(
    surface_reference: InputPathType,
    surface_distorted: InputPathType,
    metric_out: str,
    smooth: SurfaceDistortionSmoothParamsDict | None = None,
    match_surface_area: SurfaceDistortionMatchSurfaceAreaParamsDict | None = None,
    log2: bool | None = None,
    edge_method: bool = False,
    caret5_method: bool = False,
    runner: Runner | None = None,
) -> SurfaceDistortionOutputs:
    """
    MEASURE DISTORTION BETWEEN SURFACES.
    
    This command, when not using -caret5-method, -edge-method, or
    -local-affine-method, is equivalent to using -surface-vertex-areas on each
    surface, smoothing both output metrics with the GEO_GAUSS_EQUAL method on
    the surface they came from if -smooth is specified, and then using the
    formula 'ln(distorted/reference)/ln(2)' on the smoothed results.
    
    When using -caret5-method, it uses the surface distortion method from
    caret5, which takes the base 2 log of the ratio of tile areas, then averages
    those results at each vertex, and then smooths the result on the reference
    surface.
    
    When using -edge-method, the -smooth option is ignored, and the output at
    each vertex is the average of 'abs(ln(refEdge/distortEdge)/ln(2))' over all
    edges connected to the vertex.
    
    When using -local-affine-method, the -smooth option is ignored. The output
    is two columns, the first is the area distortion ratio, and the second is
    anisotropic strain. These are calculated by an affine transform between
    matching triangles, and then averaged across the triangles of a vertex.
    
    Args:
        surface_reference: the reference surface.
        surface_distorted: the distorted surface.
        metric_out: the output distortion metric.
        smooth: smooth the area data.
        match_surface_area: isotropically rescale the distorted surface so that\
            it has the same surface area as the reference surface.
        log2: calculate distortion by the local affines between triangles\
            \
            apply base-2 log transform.
        edge_method: calculate distortion of edge lengths rather than areas.
        caret5_method: use the surface distortion method from caret5.
        runner: Command runner.
    Returns:
        NamedTuple of outputs (described in `SurfaceDistortionOutputs`).
    """
    params = surface_distortion_params(
        surface_reference=surface_reference,
        surface_distorted=surface_distorted,
        metric_out=metric_out,
        smooth=smooth,
        match_surface_area=match_surface_area,
        log2=log2,
        edge_method=edge_method,
        caret5_method=caret5_method,
    )
    return surface_distortion_execute(params, runner)


__all__ = [
    "SURFACE_DISTORTION_METADATA",
    "SurfaceDistortionMatchSurfaceAreaParamsDict",
    "SurfaceDistortionMatchSurfaceAreaParamsDictTagged",
    "SurfaceDistortionOutputs",
    "SurfaceDistortionParamsDict",
    "SurfaceDistortionParamsDictTagged",
    "SurfaceDistortionSmoothParamsDict",
    "SurfaceDistortionSmoothParamsDictTagged",
    "surface_distortion",
    "surface_distortion_execute",
    "surface_distortion_match_surface_area",
    "surface_distortion_params",
    "surface_distortion_smooth",
]
