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

import typing
import pathlib
from styxdefs import *

METRIC_FIND_CLUSTERS_METADATA = Metadata(
    id="7e590372c8ad9ee4a954f1f091435601a39d86ba.workbench",
    name="metric-find-clusters",
    package="workbench",
    container_image_tag="nx10x/workbench:2.1.0",
)


_MetricFindClustersParamsDictNoTag = typing.TypedDict('_MetricFindClustersParamsDictNoTag', {
    "surface": InputPathType,
    "metric-in": InputPathType,
    "value-threshold": float,
    "minimum-area": float,
    "metric-out": str,
    "startval": typing.NotRequired[int | None],
    "distance": typing.NotRequired[float | None],
    "ratio": typing.NotRequired[float | None],
    "column": typing.NotRequired[str | None],
    "area-metric": typing.NotRequired[InputPathType | None],
    "roi-metric": typing.NotRequired[InputPathType | None],
    "less-than": bool,
})
MetricFindClustersParamsDictTagged = typing.TypedDict('MetricFindClustersParamsDictTagged', {
    "@type": typing.Literal["workbench/metric-find-clusters"],
    "surface": InputPathType,
    "metric-in": InputPathType,
    "value-threshold": float,
    "minimum-area": float,
    "metric-out": str,
    "startval": typing.NotRequired[int | None],
    "distance": typing.NotRequired[float | None],
    "ratio": typing.NotRequired[float | None],
    "column": typing.NotRequired[str | None],
    "area-metric": typing.NotRequired[InputPathType | None],
    "roi-metric": typing.NotRequired[InputPathType | None],
    "less-than": bool,
})
MetricFindClustersParamsDict = _MetricFindClustersParamsDictNoTag | MetricFindClustersParamsDictTagged


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


def metric_find_clusters_params(
    surface: InputPathType,
    metric_in: InputPathType,
    value_threshold: float,
    minimum_area: float,
    metric_out: str,
    startval: int | None = None,
    distance: float | None = None,
    ratio: float | None = None,
    column: str | None = None,
    area_metric: InputPathType | None = None,
    roi_metric: InputPathType | None = None,
    less_than: bool = False,
) -> MetricFindClustersParamsDictTagged:
    """
    Build parameters.
    
    Args:
        surface: the surface to compute on.
        metric_in: the input metric.
        value_threshold: threshold for data values.
        minimum_area: threshold for cluster area, in mm^2.
        metric_out: the output metric.
        startval: start labeling clusters from a value other than 1\
            \
            the value to give the first cluster found.
        distance: ignore clusters further than a given distance from the\
            largest cluster\
            \
            how far from the largest cluster a cluster can be, edge to edge, in\
            mm.
        ratio: ignore clusters smaller than a given fraction of the largest\
            cluster in map\
            \
            fraction of the largest cluster's area.
        column: select a single column\
            \
            the column number or name.
        area_metric: vertex areas to use instead of computing them from the\
            surface\
            \
            the corrected vertex areas, as a metric.
        roi_metric: select a region of interest\
            \
            the roi, as a metric.
        less_than: find values less than <value-threshold>, rather than greater.
    Returns:
        Parameter dictionary
    """
    params = {
        "@type": "workbench/metric-find-clusters",
        "surface": surface,
        "metric-in": metric_in,
        "value-threshold": value_threshold,
        "minimum-area": minimum_area,
        "metric-out": metric_out,
        "less-than": less_than,
    }
    if startval is not None:
        params["startval"] = startval
    if distance is not None:
        params["distance"] = distance
    if ratio is not None:
        params["ratio"] = ratio
    if column is not None:
        params["column"] = column
    if area_metric is not None:
        params["area-metric"] = area_metric
    if roi_metric is not None:
        params["roi-metric"] = roi_metric
    return params


def metric_find_clusters_validate(
    params: typing.Any,
) -> None:
    """
    Validate parameters. Throws an error if `params` is not a valid
    `MetricFindClustersParamsDict` 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", None) is None:
        raise StyxValidationError("`surface` must not be None")
    if not isinstance(params["surface"], (pathlib.Path, str)):
        raise StyxValidationError(f'`surface` has the wrong type: Received `{type(params.get("surface", None))}` expected `InputPathType`')
    if params.get("metric-in", None) is None:
        raise StyxValidationError("`metric-in` must not be None")
    if not isinstance(params["metric-in"], (pathlib.Path, str)):
        raise StyxValidationError(f'`metric-in` has the wrong type: Received `{type(params.get("metric-in", None))}` expected `InputPathType`')
    if params.get("value-threshold", None) is None:
        raise StyxValidationError("`value-threshold` must not be None")
    if not isinstance(params["value-threshold"], (float, int)):
        raise StyxValidationError(f'`value-threshold` has the wrong type: Received `{type(params.get("value-threshold", None))}` expected `float`')
    if params.get("minimum-area", None) is None:
        raise StyxValidationError("`minimum-area` must not be None")
    if not isinstance(params["minimum-area"], (float, int)):
        raise StyxValidationError(f'`minimum-area` has the wrong type: Received `{type(params.get("minimum-area", None))}` expected `float`')
    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("startval", None) is not None:
        if not isinstance(params["startval"], int):
            raise StyxValidationError(f'`startval` has the wrong type: Received `{type(params.get("startval", None))}` expected `int | None`')
    if params.get("distance", None) is not None:
        if not isinstance(params["distance"], (float, int)):
            raise StyxValidationError(f'`distance` has the wrong type: Received `{type(params.get("distance", None))}` expected `float | None`')
    if params.get("ratio", None) is not None:
        if not isinstance(params["ratio"], (float, int)):
            raise StyxValidationError(f'`ratio` has the wrong type: Received `{type(params.get("ratio", None))}` expected `float | None`')
    if params.get("column", None) is not None:
        if not isinstance(params["column"], str):
            raise StyxValidationError(f'`column` has the wrong type: Received `{type(params.get("column", None))}` expected `str | None`')
    if params.get("area-metric", None) is not None:
        if not isinstance(params["area-metric"], (pathlib.Path, str)):
            raise StyxValidationError(f'`area-metric` has the wrong type: Received `{type(params.get("area-metric", None))}` expected `InputPathType | None`')
    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`')
    if params.get("less-than", False) is None:
        raise StyxValidationError("`less-than` must not be None")
    if not isinstance(params["less-than"], bool):
        raise StyxValidationError(f'`less-than` has the wrong type: Received `{type(params.get("less-than", False))}` expected `bool`')


def metric_find_clusters_cargs(
    params: MetricFindClustersParamsDict,
    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",
        "-metric-find-clusters"
    ])
    cargs.append(execution.input_file(params.get("surface", None)))
    cargs.append(execution.input_file(params.get("metric-in", None)))
    cargs.append(str(params.get("value-threshold", None)))
    cargs.append(str(params.get("minimum-area", None)))
    cargs.append(params.get("metric-out", None))
    if params.get("startval", None) is not None:
        cargs.extend([
            "-start",
            str(params.get("startval", None))
        ])
    if params.get("distance", None) is not None:
        cargs.extend([
            "-distance",
            str(params.get("distance", None))
        ])
    if params.get("ratio", None) is not None:
        cargs.extend([
            "-size-ratio",
            str(params.get("ratio", None))
        ])
    if params.get("column", None) is not None:
        cargs.extend([
            "-column",
            params.get("column", None)
        ])
    if params.get("area-metric", None) is not None:
        cargs.extend([
            "-corrected-areas",
            execution.input_file(params.get("area-metric", None))
        ])
    if params.get("roi-metric", None) is not None:
        cargs.extend([
            "-roi",
            execution.input_file(params.get("roi-metric", None))
        ])
    if params.get("less-than", False):
        cargs.append("-less-than")
    return cargs


def metric_find_clusters_outputs(
    params: MetricFindClustersParamsDict,
    execution: Execution,
) -> MetricFindClustersOutputs:
    """
    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 = MetricFindClustersOutputs(
        root=execution.output_file("."),
        metric_out=execution.output_file(params.get("metric-out", None)),
    )
    return ret


def metric_find_clusters_execute(
    params: MetricFindClustersParamsDict,
    runner: Runner | None = None,
) -> MetricFindClustersOutputs:
    """
    FILTER CLUSTERS BY SURFACE AREA.
    
    Outputs a metric with nonzero integers for all vertices within a large
    enough cluster, and zeros elsewhere. The integers denote cluster membership
    (by default, first cluster found will use value 1, second cluster 2, etc).
    Cluster values are not reused across maps of the output, but instead keep
    counting up. By default, values greater than <value-threshold> are
    considered to be in a cluster, use -less-than to test for values less than
    the threshold. To apply this as a mask to the data, or to do more
    complicated thresholding, see -metric-math.
    
    Args:
        params: The parameters.
        runner: Command runner.
    Returns:
        NamedTuple of outputs (described in `MetricFindClustersOutputs`).
    """
    metric_find_clusters_validate(params)
    runner = runner or get_global_runner()
    execution = runner.start_execution(METRIC_FIND_CLUSTERS_METADATA)
    params = execution.params(params)
    cargs = metric_find_clusters_cargs(params, execution)
    ret = metric_find_clusters_outputs(params, execution)
    execution.run(cargs)
    return ret


def metric_find_clusters(
    surface: InputPathType,
    metric_in: InputPathType,
    value_threshold: float,
    minimum_area: float,
    metric_out: str,
    startval: int | None = None,
    distance: float | None = None,
    ratio: float | None = None,
    column: str | None = None,
    area_metric: InputPathType | None = None,
    roi_metric: InputPathType | None = None,
    less_than: bool = False,
    runner: Runner | None = None,
) -> MetricFindClustersOutputs:
    """
    FILTER CLUSTERS BY SURFACE AREA.
    
    Outputs a metric with nonzero integers for all vertices within a large
    enough cluster, and zeros elsewhere. The integers denote cluster membership
    (by default, first cluster found will use value 1, second cluster 2, etc).
    Cluster values are not reused across maps of the output, but instead keep
    counting up. By default, values greater than <value-threshold> are
    considered to be in a cluster, use -less-than to test for values less than
    the threshold. To apply this as a mask to the data, or to do more
    complicated thresholding, see -metric-math.
    
    Args:
        surface: the surface to compute on.
        metric_in: the input metric.
        value_threshold: threshold for data values.
        minimum_area: threshold for cluster area, in mm^2.
        metric_out: the output metric.
        startval: start labeling clusters from a value other than 1\
            \
            the value to give the first cluster found.
        distance: ignore clusters further than a given distance from the\
            largest cluster\
            \
            how far from the largest cluster a cluster can be, edge to edge, in\
            mm.
        ratio: ignore clusters smaller than a given fraction of the largest\
            cluster in map\
            \
            fraction of the largest cluster's area.
        column: select a single column\
            \
            the column number or name.
        area_metric: vertex areas to use instead of computing them from the\
            surface\
            \
            the corrected vertex areas, as a metric.
        roi_metric: select a region of interest\
            \
            the roi, as a metric.
        less_than: find values less than <value-threshold>, rather than greater.
        runner: Command runner.
    Returns:
        NamedTuple of outputs (described in `MetricFindClustersOutputs`).
    """
    params = metric_find_clusters_params(
        surface=surface,
        metric_in=metric_in,
        value_threshold=value_threshold,
        minimum_area=minimum_area,
        metric_out=metric_out,
        startval=startval,
        distance=distance,
        ratio=ratio,
        column=column,
        area_metric=area_metric,
        roi_metric=roi_metric,
        less_than=less_than,
    )
    return metric_find_clusters_execute(params, runner)


__all__ = [
    "METRIC_FIND_CLUSTERS_METADATA",
    "MetricFindClustersOutputs",
    "MetricFindClustersParamsDict",
    "MetricFindClustersParamsDictTagged",
    "metric_find_clusters",
    "metric_find_clusters_execute",
    "metric_find_clusters_params",
]
