"""
Test parameters i.e. sample data, known past errors, etc.
"""
from functools import lru_cache
from pathlib import Path
from traceback import print_tb
from typing import List

import geopandas as gpd
import numpy as np
import pandas as pd
import pytest
from click.testing import Result
from hypothesis.strategies import floats, integers, tuples
from shapely.geometry import (
    LineString,
    MultiLineString,
    MultiPolygon,
    Point,
    Polygon,
    box,
)
from shapely.wkt import loads

from fractopo import general
from fractopo.analysis import length_distributions, parameters
from fractopo.general import (
    CC_branch,
    CI_branch,
    E_node,
    I_node,
    II_branch,
    X_node,
    Y_node,
    bounding_polygon,
    determine_azimuth,
    read_geofile,
)
from fractopo.tval import trace_validation
from fractopo.tval.trace_validators import (
    GeomNullValidator,
    GeomTypeValidator,
    MultiJunctionValidator,
    MultipleCrosscutValidator,
    SharpCornerValidator,
    StackedTracesValidator,
    TargetAreaSnapValidator,
    UnderlappingSnapValidator,
    VNodeValidator,
)
from tests import trace_builder
from tests.sample_data.py_samples.samples import (
    results_in_false_positive_stacked_traces_list,
    results_in_false_positive_underlapping_ls,
    results_in_multijunction_why_ls_list,
    results_in_multijunction_why_ls_list_2,
    results_in_overlapping_ls_list,
    should_result_in_multij_ls_list,
    should_result_in_some_error_ls_list,
    should_result_in_target_area_underlapping_ls,
    should_result_in_target_area_underlapping_poly,
    should_result_in_vnode_ls_list,
    v_node_network_error_ls_list,
)

GEOMETRY_COLUMN = trace_validation.Validation.GEOMETRY_COLUMN
ERROR_COLUMN = trace_validation.Validation.ERROR_COLUMN

SNAP_THRESHOLD = 0.001
SNAP_THRESHOLD_ERROR_MULTIPLIER = 1.1
AREA_EDGE_SNAP_MULTIPLIER = 5


def click_error_print(result: Result):
    """
    Print click result traceback.
    """
    if result.exit_code == 0:
        return
    assert result.exc_info is not None
    _, _, tb = result.exc_info
    # print(err_class, err)
    print_tb(tb)
    print(result.output)
    raise Exception(result.exception)


class Helpers:

    """
    Parameters for tests.
    """

    valid_geom = LineString(((0, 0), (1, 1)))

    invalid_geom_empty = LineString()
    invalid_geom_none = None
    invalid_geom_multilinestring = MultiLineString(
        [((0, 0), (1, 1)), ((-1, 0), (1, 0))]
    )
    mergeable_geom_multilinestring = MultiLineString(
        [((0, 0), (1, 1)), ((1, 1), (2, 2))]
    )
    (
        valid_traces,
        invalid_traces,
        valid_areas_geoseries,
        invalid_areas_geoseries,
    ) = trace_builder.main(False, SNAP_THRESHOLD, SNAP_THRESHOLD_ERROR_MULTIPLIER)
    valid_error_srs = pd.Series([[] for _ in valid_traces.geometry.values])
    invalid_error_srs = pd.Series([[] for _ in invalid_traces.geometry.values])

    multilinestring_critical_err_in_validation_gdf = read_geofile(
        Path("tests/sample_data/validation_errror_08042021/Circle3_fractures.shp")
    )
    multilinestring_critical_err_in_validation_gdf.error_amount = 1

    v_node_network_error_gdf = gpd.GeoDataFrame(geometry=v_node_network_error_ls_list)
    v_node_network_error_area_gdf = gpd.GeoDataFrame(
        geometry=[bounding_polygon(v_node_network_error_gdf)]
    )

    hastholmen_traces = read_geofile(
        Path("tests/sample_data/hastholmen_traces.geojson")
    )
    hastholmen_area = read_geofile(Path("tests/sample_data/hastholmen_area.geojson"))

    @staticmethod
    def random_data_column(iterable):
        """
        Make random data column contents.
        """
        return ["aaa" for _ in iterable]

    # geoms are all LineStrings and no errors
    @classmethod
    def valid_gdf_get(cls):
        """
        Get valid gdf.
        """
        return gpd.GeoDataFrame(
            {
                GEOMETRY_COLUMN: Helpers.valid_traces,
                ERROR_COLUMN: Helpers.valid_error_srs,
                "random_col": cls.random_data_column(Helpers.valid_traces),
                "random_col2": cls.random_data_column(Helpers.valid_traces),
                "random_col3": cls.random_data_column(Helpers.valid_traces),
                "random_col4": cls.random_data_column(Helpers.valid_traces),
            }
        )

    @classmethod
    def invalid_gdf_get(cls):
        """
        Get invalid gdf.
        """
        return gpd.GeoDataFrame(
            {
                GEOMETRY_COLUMN: Helpers.invalid_traces,
                ERROR_COLUMN: Helpers.invalid_error_srs,
                "random_col": cls.random_data_column(Helpers.invalid_traces),
            }
        )

    @classmethod
    def invalid_gdf_null_get(cls):
        """
        Get gdf with None and empty geometries.
        """
        return gpd.GeoDataFrame(
            {
                GEOMETRY_COLUMN: [None, LineString()],
                ERROR_COLUMN: [[], []],
                "random_col": cls.random_data_column(range(2)),
            }
        )

    @staticmethod
    def valid_area_gdf_get():
        """
        Get a valid area gdf.
        """
        return gpd.GeoDataFrame({GEOMETRY_COLUMN: Helpers.valid_areas_geoseries})

    @staticmethod
    def invalid_area_gdf_get():
        """
        Get an invalid area gdf.
        """
        return gpd.GeoDataFrame({GEOMETRY_COLUMN: Helpers.invalid_areas_geoseries})

    faulty_error_srs = pd.Series([[] for _ in valid_traces.geometry.values])
    faulty_error_srs[0] = np.nan
    faulty_error_srs[1] = "this cannot be transformed to list?"
    faulty_error_srs[2] = (1, 2, 3, "hello?")
    faulty_error_srs[5] = 5.12315235

    @classmethod
    def valid_gdf_with_faulty_error_col_get(cls):
        """
        Get valid gdf with faulty error column.
        """
        return gpd.GeoDataFrame(
            {
                GEOMETRY_COLUMN: Helpers.valid_traces,
                ERROR_COLUMN: Helpers.faulty_error_srs,
                "random_col": cls.random_data_column(Helpers.valid_traces),
            }
        )

    @staticmethod
    def iterate_validators():
        """
        Iterate over validators.
        """
        for validator in (
            GeomNullValidator,
            GeomTypeValidator,
            MultiJunctionValidator,
            VNodeValidator,
            MultipleCrosscutValidator,
            UnderlappingSnapValidator,
            TargetAreaSnapValidator,
        ):
            yield validator

    nice_integer_coordinates = integers(-10, 10)
    nice_float = floats(
        allow_nan=False, allow_infinity=False, min_value=-1e5, max_value=1e5
    )
    nice_tuple = tuples(
        nice_float,
        nice_float,
    )
    triple_tuples = tuples(
        nice_tuple,
        nice_tuple,
        nice_tuple,
    )

    snap_threshold = 0.001
    geosrs_identicals = gpd.GeoSeries(
        [Point(1, 1), Point(1, 1), Point(2, 1), Point(2, 1), Point(3, 1), Point(2, 3)]
    )

    traces_geosrs = gpd.GeoSeries(
        [
            LineString([(-1, 0), (1, 0)]),
            LineString([(0, -1), (0, 1)]),
            LineString(
                [(-1.0 - snap_threshold * 0.99, -1), (-1.0 - snap_threshold * 0.99, 1)]
            ),
        ]
    )
    areas_geosrs = gpd.GeoSeries([Polygon([(5, 5), (-5, 5), (-5, -5), (5, -5)])])

    nice_traces = gpd.GeoSeries(
        [
            # Horizontal
            LineString([(-10, 0), (10, 0)]),
            # Underlapping
            LineString([(-5, 2), (-5, 0 + snap_threshold * 0.01)]),
            LineString([(-4, 2), (-4, 0 + snap_threshold * 0.5)]),
            LineString([(-3, 2), (-3, 0 + snap_threshold * 0.7)]),
            LineString([(-2, 2), (-2, 0 + snap_threshold * 0.9)]),
            LineString([(-1, 2), (-1, 0 + snap_threshold * 1.1)]),
            # Overlapping
            LineString([(1, 2), (1, 0 - snap_threshold * 1.1)]),
            LineString([(2, 2), (2, 0 - snap_threshold * 0.9)]),
            LineString([(3, 2), (3, 0 - snap_threshold * 0.7)]),
            LineString([(4, 2), (4, 0 - snap_threshold * 0.5)]),
            LineString([(5, 2), (5, 0 - snap_threshold * 0.01)]),
        ]
    )
    nice_integer_coordinates = integers(-10, 10)
    nice_float = floats(
        allow_nan=False, allow_infinity=False, min_value=-1e5, max_value=1e5
    )
    nice_tuple = tuples(
        nice_float,
        nice_float,
    )
    triple_tuples = tuples(
        nice_tuple,
        nice_tuple,
        nice_tuple,
    )

    @classmethod
    def get_nice_traces(cls):
        """
        Get nice traces GeoSeries.
        """
        return cls.nice_traces.copy()

    @classmethod
    def get_traces_geosrs(cls):
        """
        Get traces GeoSeries.
        """
        return cls.traces_geosrs.copy()

    @classmethod
    def get_areas_geosrs(cls):
        """
        Get areas GeoSeries.
        """
        return cls.areas_geosrs.copy()

    @classmethod
    def get_geosrs_identicals(cls):
        """
        Get GeoSeries with identical geometries.
        """
        return cls.geosrs_identicals.copy()

    line_1 = LineString([(0, 0), (0.5, 0.5)])
    line_2 = LineString([(0, 0), (0.5, -0.5)])
    line_3 = LineString([(0, 0), (1, 0)])
    line_1_sp = Point(list(line_1.coords)[0])
    line_2_sp = Point(list(line_2.coords)[0])
    line_1_ep = Point(list(line_1.coords)[-1])
    line_2_ep = Point(list(line_2.coords)[-1])
    halved_azimuths = [
        determine_azimuth(line, halved=True)
        for line in (
            line_1,
            line_2,
            line_3,
        )
    ]
    branch_frame = gpd.GeoDataFrame(
        {
            "geometry": [line_1, line_2, line_3],
            "Connection": ["C - C", "C - I", "I - I"],
            "Class": ["X - I", "Y - Y", "I - I"],
            "halved": halved_azimuths,
            "length": [line.length for line in [line_1, line_2, line_3]],
        }
    )

    trace_frame = gpd.GeoDataFrame(
        {
            "geometry": [line_1, line_2],
            "length": [line_1.length, line_2.length],
            "startpoint": [line_1_sp, line_2_sp],
            "endpoint": [line_1_ep, line_2_ep],
        }
    )
    point_1 = Point(0.5, 0.5)
    point_2 = Point(1, 1)
    point_3 = Point(10, 10)
    node_frame = gpd.GeoDataFrame(
        {"geometry": [point_1, point_2, point_3], "Class": ["X", "Y", "I"]}
    )
    node_frame["c"] = node_frame["Class"]
    area_1 = Polygon([(0, 0), (1, 1), (1, 0)])
    area_frame = gpd.GeoDataFrame({"geometry": [area_1]})

    sample_trace_data = Path("tests/sample_data/KB11_traces.shp")
    sample_branch_data = Path("tests/sample_data/KB11_branches.shp")
    sample_area_data = Path("tests/sample_data/KB11_area.shp")
    kb11_traces = read_geofile(sample_trace_data)
    kb11_area = read_geofile(sample_area_data)

    kl2_2_traces = read_geofile(Path("tests/sample_data/kl2_2/kl2_2_traces.geojson"))
    kl2_2_area = read_geofile(Path("tests/sample_data/kl2_2/kl2_2_area.geojson"))
    multipolygon_traces = read_geofile(
        Path(
            "tests/sample_data/multipolygon_traces_area/"
            "traces_within_multipolygon_target_area.geojson"
        )
    )
    multipolygon_area = read_geofile(
        Path(
            "tests/sample_data/multipolygon_traces_area/"
            "multipolygon_target_area.geojson"
        )
    )
    manypolygon_area = read_geofile(
        Path(
            "tests/sample_data/multipolygon_traces_area/"
            "many_polygon_target_area.geojson"
        )
    )
    assert isinstance(kb11_traces, gpd.GeoDataFrame)
    assert isinstance(kb11_area, gpd.GeoDataFrame)

    kb7_trace_path = Path("tests/sample_data/KB7/KB7_tulkinta_50.shp")
    kb7_area_path = Path("tests/sample_data/KB7/KB7_tulkinta_alue.shp")

    kb7_traces = read_geofile(kb7_trace_path)
    kb7_area = read_geofile(kb7_area_path)

    test_tracevalidate_params = [
        (
            Path("tests/sample_data/KB7/KB7_tulkinta_50.shp"),  # cut 0-50
            Path("tests/sample_data/KB7/KB7_tulkinta_alue.shp"),
            "--allow-fix",
        ),
        (
            Path("tests/sample_data/KB7/KB7_tulkinta_100.shp"),  # cut 50-100
            Path("tests/sample_data/KB7/KB7_tulkinta_alue.shp"),
            "--allow-fix",
        ),
    ]

    test_match_crs_params = [
        (
            gpd.GeoSeries([Point(1, 1)]).set_crs(3067),  # first
            gpd.GeoSeries([Point(1, 1)]),  # second
            True,  # same
            True,  # from_first
            False,  # is_none
        ),
        (
            gpd.GeoSeries([Point(1, 1)]),  # first
            gpd.GeoSeries([Point(1, 1)]),  # second
            True,  # same
            True,  # from_first
            True,  # is_none
        ),
        (
            gpd.GeoSeries([Point(1, 1)]).set_crs(3067),  # first
            gpd.GeoSeries([Point(1, 1)]).set_crs(3066),  # second
            False,  # same
            True,  # from_first
            False,  # is_none
        ),
    ]
    test_is_within_buffer_distance_params = [
        (nice_traces, 0.5, 25),
        (nice_traces, 1, 35),
    ]

    test_plot_xyi_plot_params = [
        ([{X_node: 0, Y_node: 0, I_node: 50}], ["title"]),
        ([{X_node: 0, Y_node: 0, I_node: 0}], ["title"]),
        ([{X_node: 0, Y_node: 10, I_node: 25}], [""]),
    ]

    test_plot_branch_plot_params = [
        ([{CC_branch: 30, CI_branch: 15, II_branch: 50}], ["title"]),
        ([{CC_branch: 0, CI_branch: 0, II_branch: 50}], ["title"]),
        ([{CC_branch: 0, CI_branch: 0, II_branch: 0}], ["title"]),
    ]

    test_determine_topology_parameters_params = [
        (
            np.array([10, 10, 10, 10]),  # trace_length_array
            {X_node: 3, Y_node: 5, I_node: 8, E_node: 0},  # node_counts dict
            10.0,  # area
        ),
        (
            np.array([1, 1, 1, 1]),  # trace_length_array
            {X_node: 3, Y_node: 5, I_node: 8, E_node: 0},  # node_counts dict
            1.0,  # area
        ),
    ]

    test_plot_topology_params = [
        (
            [
                parameters.determine_topology_parameters(  # topology_parameters_list
                    *test_determine_topology_parameters_params[0]
                )
            ],
            ["title"],  # labels
            ["black"],  # colors
        )
    ]
    test_determine_nodes_intersecting_sets_params = [
        (
            (
                gpd.GeoSeries([LineString([(0, 0), (1, 1)])]),
                gpd.GeoSeries([LineString([(0, 1), (0, -1)])]),
            ),  # trace_series_two_sets
            np.array(["1", "2"]),  # set_array
            ("1", "2"),  # set_names_two_sets
            gpd.GeoSeries(
                [Point(0, 0), Point(1, 1), Point(0, 1), Point(0, -1)]
            ),  # node_series_xy
            0.001,  # buffer_value
            [True, False, False, False],  # assumed_intersections
        ),
        (
            (
                gpd.GeoSeries([LineString([(0.5, 0.5), (1, 1)])]),
                gpd.GeoSeries([LineString([(0, 1), (0, -1)])]),
            ),  # trace_series_two_sets
            np.array(["1", "2"]),  # set_array
            ("1", "2"),  # set_names_two_sets
            gpd.GeoSeries(
                [Point(0.5, 0.5), Point(1, 1), Point(0, 1), Point(0, -1)]
            ),  # node_series_xy
            0.001,  # buffer_value
            [False, False, False, False],  # assumed_intersections
        ),
    ]

    test_prepare_geometry_traces_params = [
        (
            gpd.GeoSeries(
                [LineString([(0.5, 0.5), (1, 1)]), LineString([(0, 1), (0, -1)])]
            )
        ),
        (
            gpd.GeoSeries(
                [
                    LineString([(0.5, 0.5), (1, 1)]),
                    LineString([(0, 1), (0, -1)]),
                    LineString([(0, 100), (0, -15)]),
                    LineString([(5, 100), (67, -15), (67, -150)]),
                ]
            )
        ),
    ]
    test_determine_intersects_params = [
        (
            (
                gpd.GeoSeries([LineString([(0, 0), (1, 1)])]),
                gpd.GeoSeries([LineString([(0, 1), (0, -1)])]),
            ),  # trace_series_two_sets
            ("1", "2"),  # set_names_two_sets
            gpd.GeoSeries([Point(0, 0)]),  # node_series_xy_intersects
            np.array(["Y"]),  # node_types_xy_intersects
            # assumed_intersections
            0.001,  # buffer_value
        ),
        (
            (
                gpd.GeoSeries([LineString([(0.5, 0.5), (1, 1)])]),
                gpd.GeoSeries([LineString([(0, 1), (0, -1)])]),
            ),  # trace_series_two_sets
            ("1", "2"),  # set_names_two_sets
            gpd.GeoSeries([]),  # node_series_xy_intersects
            np.array([]),  # node_types_xy_intersects
            # assumed_intersections
            0.001,  # buffer_value
        ),
    ]
    test_determine_crosscut_abutting_relationships_params = [
        (
            gpd.GeoSeries(
                [LineString([(0, 0), (1, 0)]), LineString([(0, 1), (0, -1)])]
            ),  # trace_series
            gpd.GeoSeries(
                [Point(0, 0), Point(1, 0), Point(0, 1), Point(0, -1)]
            ),  # node_series
            np.array(["Y", "I", "I", "I"]),  # node_types
            np.array(["1", "2"]),  # set_array
            ("1", "2"),  # set_names
            0.001,  # buffer_value
            "title",  # label
        ),
    ]

    test__validate_params = [
        (
            GeomNullValidator,  # validator
            None,  # geom
            [],  # current_errors
            True,  # allow_fix
            [None, [GeomNullValidator.ERROR], True],  # assumed_result
        ),
        (
            GeomTypeValidator,  # validator
            invalid_geom_multilinestring,  # geom
            [],  # current_errors
            True,  # allow_fix
            [
                invalid_geom_multilinestring,
                [GeomTypeValidator.ERROR],
                True,
            ],  # assumed_result
        ),
        (
            GeomTypeValidator,  # validator
            mergeable_geom_multilinestring,  # geom
            [],  # current_errors
            True,  # allow_fix
            [
                loads("LINESTRING (0 0, 1 1, 2 2)"),
                [],
                False,
            ],  # assumed_result
        ),
    ]
    intersect_nodes = [
        (Point(0, 0), Point(1, 1)),
        (Point(1, 1),),
        (Point(5, 5),),
        (Point(0, 0), Point(1, 1)),
    ]

    # Intersects next trace three times
    intersects_next_trace_3_times = LineString(
        [Point(-4, -3), Point(-2, -3), Point(-4, -2), Point(-2, -1)]
    )

    # Straight line which is intersected twice by same line
    intersected_3_times = LineString([Point(-3, -4), Point(-3, -1)])
    test_validation_params = [
        (
            kb7_traces,  # traces
            kb7_area,  # area
            "kb7",  # name
            True,  # auto_fix
            [SharpCornerValidator.ERROR],  # assume_errors
        ),
        (
            hastholmen_traces,  # traces
            hastholmen_area,  # area
            "hastholmen_traces",  # name
            True,  # auto_fix
            [],  # assume_errors
        ),
        # (
        #     kb11_traces,  # traces
        #     kb11_area,  # area
        #     "kb11",  # name
        #     True,  # auto_fix
        #     None,  # assume_errors
        # ),
        (
            gpd.GeoDataFrame(
                geometry=trace_builder.make_invalid_traces(
                    snap_threshold=0.01, snap_threshold_error_multiplier=1.1
                )
            ),  # traces
            gpd.GeoDataFrame(
                geometry=trace_builder.make_invalid_target_areas()
            ),  # area
            "invalid_traces",  # name
            True,  # auto_fix
            None,  # assume_errors
        ),
        (
            gpd.GeoDataFrame(geometry=[LineString([(0, 0), (0, 1)])]),  # traces
            gpd.GeoDataFrame(
                geometry=[
                    Polygon(
                        [
                            Point(-1, -1),
                            Point(-1, 1.011),
                            Point(1, 1.011),
                            Point(1, -1),
                        ]
                    )
                ]
            ),  # area
            "TargetAreaSnapValidator error",  # name
            True,  # auto_fix
            [TargetAreaSnapValidator.ERROR],  # assume_errors
        ),
        (
            gpd.GeoDataFrame(
                geometry=[LineString([(0, 0), (0, 1)]), LineString([(5, 5), (5, 6)])]
            ),  # traces
            gpd.GeoDataFrame(
                geometry=[
                    Polygon(
                        [
                            Point(-1, -1),
                            Point(-1, 1.011),
                            Point(1, 1.011),
                            Point(1, -1),
                        ]
                    ),
                    Polygon(
                        [
                            Point(2, 2),
                            Point(2, 6.011),
                            Point(6, 6.011),
                            Point(6, 2),
                        ]
                    ),
                ]
            ),  # area
            "TargetAreaSnapValidator error",  # name
            True,  # auto_fix
            [TargetAreaSnapValidator.ERROR],  # assume_errors
        ),
        (
            gpd.GeoDataFrame(
                geometry=[LineString([(0, 0), (0, 1)]), LineString([(5, 5), (5, 6)])]
            ),  # traces
            gpd.GeoDataFrame(
                geometry=[
                    MultiPolygon(
                        [
                            Polygon(
                                [
                                    Point(-1, -1),
                                    Point(-1, 1.011),
                                    Point(1, 1.011),
                                    Point(1, -1),
                                ]
                            ),
                            Polygon(
                                [
                                    Point(2, 2),
                                    Point(2, 6.011),
                                    Point(6, 6.011),
                                    Point(6, 2),
                                ]
                            ),
                        ]
                    )
                ]
            ),  # area
            "TargetAreaSnapValidator error",  # name
            True,  # auto_fix
            [TargetAreaSnapValidator.ERROR],  # assume_errors
        ),
    ]

    test_determine_v_nodes_params = [
        (
            [(Point(1, 1),), (Point(1, 1),)],  # endpoint_nodes
            0.01,  # snap_threshold
            1.1,  # snap_threshold_error_multiplier
            {0, 1},  # assumed_result
        ),
        (
            [(Point(1, 1),), (Point(1, 1),)],  # endpoint_nodes
            0.01,  # snap_threshold
            1.1,  # snap_threshold_error_multiplier
            {0, 1},  # assumed_result
        ),
    ]

    test_determine_node_junctions_params = [
        (
            [
                (Point(0, 0), Point(1, 1)),
                (Point(1, 1),),
                (Point(5, 5),),
                (Point(0, 0), Point(1, 1)),
            ],  # nodes
            0.01,  # snap_threshold
            1.1,  # snap_threshold_error_multiplier
            2,  # error_threshold
        )
    ]

    test_bounding_polygon_params = [
        (gpd.GeoSeries([line_1, line_2, line_3])),
        (gpd.GeoSeries([line_1])),
    ]

    test_testtargetareasnapvalidator_validation_method = [
        (
            LineString([(0.5, 0), (0.5, 0.5)]),  # geom: LineString,
            gpd.GeoDataFrame(
                geometry=[
                    MultiPolygon(
                        [
                            Polygon([(0, 0), (0, 1), (1, 1), (1, 0)]),
                            Polygon([(10, 10), (10, 11), (11, 11), (11, 10)]),
                        ]
                    )
                ]
            ),  # area:gpd.GeoDataFrame
            0.01,  # snap_threshold: float,
            1.1,  # snap_threshold_error_multiplier: float,
            2.5,  # area_edge_snap_multiplier: float,
            True,  # assumed_result: bool,
        ),
        (
            LineString([(0.5, 0.01 * 1.05), (0.5, 0.5)]),  # geom: LineString,
            gpd.GeoDataFrame(
                geometry=[
                    MultiPolygon(
                        [
                            Polygon([(0, 0), (0, 1), (1, 1), (1, 0)]),
                            Polygon([(10, 10), (10, 11), (11, 11), (11, 10)]),
                        ]
                    )
                ]
            ),  # area:gpd.GeoDataFrame
            0.01,  # snap_threshold: float,
            1.1,  # snap_threshold_error_multiplier: float,
            2.5,  # area_edge_snap_multiplier: float,
            False,  # assumed_result: bool,
        ),
        (
            LineString([(0.5, 0), (0.5, 0.5)]),  # geom: LineString,
            gpd.GeoDataFrame(
                geometry=[
                    Polygon([(0, 0), (0, 1), (1, 1), (1, 0)]),
                    Polygon([(10, 10), (10, 11), (11, 11), (11, 10)]),
                ]
            ),  # area:gpd.GeoDataFrame
            0.01,  # snap_threshold: float,
            1.1,  # snap_threshold_error_multiplier: float,
            2.5,  # area_edge_snap_multiplier: float,
            True,  # assumed_result: bool,
        ),
        (
            LineString([(10, 0), (4.991, 0)]),  # geom: LineString,
            gpd.GeoDataFrame(
                geometry=[Polygon([(5, 5), (-5, 5), (-5, -5), (5, -5)])]
            ),  # area:gpd.GeoDataFrame
            0.01,  # snap_threshold: float,
            1.1,  # snap_threshold_error_multiplier: float,
            1.5,  # area_edge_snap_multiplier: float,
            True,  # assumed_result: bool,
        ),  # Test that traces coming from outside area are not marked as underlapping
        (
            LineString([(10, 0), (5.011, 0)]),  # geom: LineString,
            gpd.GeoDataFrame(
                geometry=[Polygon([(5, 5), (-5, 5), (-5, -5), (5, -5)])]
            ),  # area:gpd.GeoDataFrame
            0.01,  # snap_threshold: float,
            1.1,  # snap_threshold_error_multiplier: float,
            1.5,  # area_edge_snap_multiplier: float,
            True,  # assumed_result: bool,
        ),  # Test that traces coming from outside area are not marked as underlapping
        (
            should_result_in_target_area_underlapping_ls,  # geom: LineString,
            gpd.GeoDataFrame(
                geometry=[should_result_in_target_area_underlapping_poly]
            ),  # area:gpd.GeoDataFrame
            0.01,  # snap_threshold: float,
            1.1,  # snap_threshold_error_multiplier: float,
            2.5,  # area_edge_snap_multiplier: float,
            False,  # assumed_result: bool,
        ),  # Test that traces coming from outside area are not marked as underlapping
    ]

    test_tracevalidate_only_area_params = [
        (
            [
                "tests/sample_data/KB7/KB7_tulkinta_50.shp",  # cut 0-50
                "tests/sample_data/KB7/KB7_tulkinta_alue.shp",
                "--allow-fix",
                "--only-area-validation",
            ]  # args
        )
    ]

    geta_1_traces = read_geofile(
        Path("tests/sample_data/geta1/Getaberget_20m_1_traces.gpkg")
    )
    geta_1_1_area = read_geofile(
        Path("tests/sample_data/geta1/Getaberget_20m_1_1_area.gpkg")
    )

    geta_1_traces_1000_n = geta_1_traces.iloc[0:1000]

    test_network_params = [
        (
            geta_1_traces,  # traces
            geta_1_1_area,  # area
            "Geta1_1",  # name
            True,  # determine_branches_nodes
            True,  # truncate_traces
            0.001,  # snap_threshold
            True,  # circular_target_area
        ),
        (
            kb11_traces,  # traces
            kb11_area,  # area
            "KB11",  # name
            True,  # determine_branches_nodes
            True,  # truncate_traces
            0.001,  # snap_threshold
            False,  # circular_target_area
        ),
        (
            kb11_traces.iloc[0:100],  # traces
            kb11_area,  # area
            "KB11_0_100",  # name
            True,  # determine_branches_nodes
            True,  # truncate_traces
            0.001,  # snap_threshold
            False,  # circular_target_area
        ),
        (
            v_node_network_error_gdf,  # traces
            v_node_network_error_area_gdf,  # area
            "v-node-error-network",  # name
            True,  # determine_branches_nodes
            True,  # truncate_traces
            0.001,  # snap_threshold
            False,  # circular_target_area
        ),
        (
            multipolygon_traces,  # traces
            multipolygon_area,  # area
            "MultiPolygon_target_area",  # name
            True,  # determine_branches_nodes
            True,  # truncate_traces
            0.001,  # snap_threshold
            False,  # circular_target_area
        ),
        (
            multipolygon_traces,  # traces
            multipolygon_area,  # area
            "MultiPolygon_target_area",  # name
            True,  # determine_branches_nodes
            False,  # truncate_traces
            0.001,  # snap_threshold
            False,  # circular_target_area
        ),
        (
            multipolygon_traces,  # traces
            manypolygon_area,  # area
            "MultiPolygon_target_area",  # name
            True,  # determine_branches_nodes
            True,  # truncate_traces
            0.001,  # snap_threshold
            False,  # circular_target_area
        ),
        (
            kl2_2_traces.iloc[0:1500],  # traces
            kl2_2_area,  # area
            "kl_2_2",  # name
            True,  # determine_branches_nodes
            True,  # truncate_traces
            0.001,  # snap_threshold
            False,  # circular_target_area
        ),
    ]

    test_network_random_sampler_params = [
        (
            geta_1_traces,  # trace_gdf
            geta_1_1_area,  # area_gdf
            10,  # min_radius
            0.001,  # snap_threshold
            1,  # samples
            "area",  # random_choice
        ),
        (
            geta_1_traces,  # trace_gdf
            geta_1_1_area,  # area_gdf
            10,  # min_radius
            0.001,  # snap_threshold
            1,  # samples
            "radius",  # random_choice
        ),
    ]

    test_describe_powerlaw_fit_params = [
        (
            geta_1_traces.geometry.length.values,  # lengths
            "traces",  # label
        )
    ]

    test_determine_boundary_intersecting_lines_params = [
        (
            gpd.GeoDataFrame(
                geometry=[
                    LineString([(0, 0), (0, 5)]),
                    LineString([(0, 0), (0, 3)]),
                    LineString([(0, 0), (0, 10)]),
                ]
            ),  # line_gdf
            gpd.GeoDataFrame(
                geometry=[
                    Polygon([(-5, 5), (5, 5), (5, -5), (-5, -5)]),
                ]
            ),  # area_gdf
            0.01,  # snap_threshold
            np.array([True, False, True]),  # assumed_result_inter
            np.array([False, False, False]),  # assumed_result_cuts
        ),
        (
            gpd.GeoDataFrame(
                geometry=[
                    LineString([(0, -50), (0, 5)]),
                    LineString([(0, 0), (0, 3)]),
                    LineString([(0, 0), (0, 10)]),
                ]
            ),  # line_gdf
            gpd.GeoDataFrame(
                geometry=[
                    Polygon([(-5, 5), (5, 5), (5, -5), (-5, -5)]),
                ]
            ),  # area_gdf
            0.01,  # snap_threshold
            np.array([True, False, True]),  # assumed_result_inter
            np.array([True, False, False]),  # assumed_result_cuts
        ),
        (
            gpd.GeoDataFrame(
                geometry=[
                    LineString([(0, -50), (0, -5.00001)]),
                ]
            ),  # line_gdf
            gpd.GeoDataFrame(
                geometry=[
                    Polygon([(-5, 5), (5, 5), (5, -5), (-5, -5)]),
                ]
            ),  # area_gdf
            0.01,  # snap_threshold
            np.array([True]),  # assumed_result_inter
            np.array([False]),  # assumed_result_cuts
        ),
        (
            gpd.GeoDataFrame(
                geometry=[
                    LineString([(0, -4.999), (0, 4.999)]),
                ]
            ),  # line_gdf
            gpd.GeoDataFrame(
                geometry=[
                    Polygon([(-5, 5), (5, 5), (5, -5), (-5, -5)]),
                ]
            ),  # area_gdf
            0.01,  # snap_threshold
            np.array([True]),  # assumed_result_inter
            np.array([True]),  # assumed_result_cuts
        ),
        (
            gpd.GeoDataFrame(
                geometry=[
                    LineString([(0, 0), (0, 1)]),
                    LineString([(10, 0), (10, 1)]),
                ]
            ),  # line_gdf
            gpd.GeoDataFrame(
                geometry=[
                    Polygon([(-1, -1), (1, -1), (1, 1), (-1, 1)]),
                    Polygon([(9, -1), (11, -1), (11, 1), (9, 1)]),
                ]
            ),  # area_gdf
            0.01,  # snap_threshold
            np.array([True, True]),  # assumed_result_inter
            np.array([False, False]),  # assumed_result_cuts
        ),
        (
            gpd.GeoDataFrame(
                geometry=[
                    LineString([(-2, 0), (2, 1)]),
                    LineString([(8, 0), (12, 0)]),
                ]
            ),  # line_gdf
            gpd.GeoDataFrame(
                geometry=[
                    Polygon([(-1, -1), (1, -1), (1, 1), (-1, 1)]),
                    Polygon([(9, -1), (11, -1), (11, 1), (9, 1)]),
                ]
            ),  # area_gdf
            0.01,  # snap_threshold
            np.array([True, True]),  # assumed_result_inter
            np.array([True, True]),  # assumed_result_cuts
        ),
    ]

    test_network_circular_target_area_params = [
        (
            gpd.GeoDataFrame(
                geometry=[
                    LineString([(0, 0), (0, 10)]),  # one intersect
                    LineString([(1, 0), (1, 3)]),  # no intersect
                    LineString([(4, -10), (4, 10)]),  # two intersect
                ]
            ),  # trace_gdf
            gpd.GeoDataFrame(
                geometry=[Polygon([(-5, -5), (-5, 5), (5, 5), (5, -5)])]
            ),  # area_gdf
            "circular_target_area_param_test",  # name
        )
    ]

    test_snap_trace_to_another_params = [
        (
            [Point(0, 0), Point(0, 4)],  # trace_endpoints
            LineString([(-5, 5), (5, 5)]),  # another
            1.1,  # snap_threshold
            LineString([(-5, 5), (0, 4), (5, 5)]),  # another
        ),
        (
            [Point(0, 0), Point(0, 4)],  # trace_endpoints
            LineString([(-5, 5), (5, 5)]),  # another
            0.1,  # snap_threshold
            LineString([(-5, 5), (5, 5)]),  # another
        ),
    ]

    test_insert_point_to_linestring_params = [
        (
            LineString([(0, 0), (1, 1), (2, 2)]),
            Point(0.5, 0.5),
            0.01,  # snap_threshold
            None,
        ),
        (
            LineString([(0, 0), (-1, -1), (-2, -2)]),
            Point(-0.5, -0.5),
            0.01,  # snap_threshold
            None,
        ),
        (
            LineString([(0, 0), (-1, -1), (-2, -2)]),
            Point(-1.0, -1.1),
            0.11,  # snap_threshold
            LineString([(0, 0), (-1.0, -1.1), (-2, -2)]),
        ),
        (
            LineString([(0, 0), (-1, -1), (-1.5, -1.5), (-2, -2)]),
            Point(-1.0, -1.1),
            0.11,  # snap_threshold
            LineString([(0, 0), (-1.0, -1.1), (-1.5, -1.5), (-2, -2)]),
        ),
        (
            LineString([(0, 0), (-1.5, -1.5), (-2, -2)]),
            Point(-1.5, -1.5),
            0.11,  # snap_threshold
            LineString([(0, 0), (-1.5, -1.5), (-2, -2)]),
        ),
    ]

    sample_traces_path = Path("tests/sample_data/branches_and_nodes/traces.gpkg")
    sample_areas_path = Path("tests/sample_data/branches_and_nodes/areas.gpkg")
    sample_traces = gpd.read_file(sample_traces_path)
    sample_areas = gpd.read_file(sample_areas_path)

    test_branches_and_nodes_regression_params = [
        (
            sample_traces,  # traces
            sample_areas,  # areas
            0.001,  # snap_threshold
            10,  # allowed_loops
            False,  # already_clipped
        )
    ]

    troubling_traces_path = Path(
        "tests/sample_data/branches_and_nodes/traces_troubling.gpkg"
    )
    troubling_traces = gpd.read_file(troubling_traces_path)

    troubling_upper = troubling_traces.geometry.values[1]
    troubling_middle = troubling_traces.geometry.values[0]
    troubling_lower = troubling_traces.geometry.values[2]

    test_simple_snap_params = [
        (
            troubling_upper,  # trace
            gpd.GeoSeries([troubling_middle, troubling_lower]),  # trace_candidates
            0.001,  # snap_threshold
            0,  # intersects_idx
        ),
        (
            troubling_lower,  # trace
            gpd.GeoSeries([troubling_middle, troubling_upper]),  # trace_candidates
            0.001,  # snap_threshold
            0,  # intersects_idx
        ),
    ]

    test_snap_trace_simple_params = [
        (
            0,  # idx
            troubling_upper,  # trace
            0.001,  # snap_threshold
            [troubling_upper, troubling_middle, troubling_upper],  # traces
            1,  # intersects_idx
        )
    ]

    unary_err_traces_path = Path("tests/sample_data/unary_error_data/err_traces.shp")
    unary_err_areas_path = Path("tests/sample_data/unary_error_data/err_area.shp")
    unary_err_traces = gpd.read_file(unary_err_traces_path).iloc[5500:8000]
    unary_err_areas = gpd.read_file(unary_err_areas_path)
    assert isinstance(unary_err_traces, gpd.GeoDataFrame)
    assert isinstance(unary_err_areas, gpd.GeoDataFrame)

    test_safer_unary_union_params = [
        (
            unary_err_traces.geometry,  # traces_geosrs
            0.001,  # snap_threshold
            13000,  # size_threshold
        ),
        (
            unary_err_traces.geometry,  # traces_geosrs
            0.001,  # snap_threshold
            50,  # size_threshold
        ),
    ]

    test_segment_within_buffer_params = [
        (valid_geom, invalid_geom_multilinestring, 0.001, 1.1, 50, True),
        (valid_geom, mergeable_geom_multilinestring, 0.001, 1.1, 50, True),
        (
            valid_geom,
            MultiLineString([LineString([(10, 10), (50, 50)])]),
            0.001,
            1.1,
            50,
            False,
        ),
    ]

    test_segmentize_linestring_params = [
        (LineString(((0, 0), (0, 1))), 0.1, 10),
        (LineString(((0, 0), (1, 1))), 0.1, 15),
        (LineString(((0, 0), (0, 1))), 1, 1),
    ]

    test_split_to_determine_triangle_errors_params = [
        (
            LineString([(-1, 0), (0, 2), (1, 0)]),  # trace
            LineString([(-1, 1.99), (0, 1.99), (1, 1.99)]),  # splitter_trace
            0.001,  # snap_threshold
            50,  # triangle_error_snap_multiplier
            True,  # assumed_result
        ),
        (
            LineString([(-1, 0), (0, 5), (1, 0)]),  # trace
            LineString([(-1, 1.99), (0, 1.99), (1, 1.99)]),  # splitter_trace
            0.001,  # snap_threshold
            50,  # triangle_error_snap_multiplier
            False,  # assumed_result
        ),
        (
            LineString([(-1, 0), (0, 1.98), (1, 0)]),  # trace
            LineString([(-1, 1.99), (0, 1.99), (1, 1.99)]),  # splitter_trace
            0.001,  # snap_threshold
            50,  # triangle_error_snap_multiplier
            False,  # assumed_result
        ),
    ]

    test_determine_middle_in_triangle_params = [
        (
            [
                LineString([(0, 0), (0, 1)]),
                LineString([(0, 1), (0, 2)]),
                LineString([(0, 2), (0, 3)]),
            ],  # segments
            0.001,  # snap_threshold
            1.1,  # snap_threshold_error_multiplier
            [
                LineString([(0, 1), (0, 2)]),
            ],  # assumed_result
        ),
        (
            [
                LineString([(0, 0), (0, 1)]),
                LineString([(0, 1), (0, 2)]),
                LineString([(0, 2), (0, 3)]),
                LineString([(0, 3), (0, 4)]),
            ],  # segments
            0.001,  # snap_threshold
            1.1,  # snap_threshold_error_multiplier
            [
                LineString([(0, 1), (0, 2)]),
                LineString([(0, 2), (0, 3)]),
            ],  # assumed_result
        ),
        (
            [
                LineString([(0, 0), (0, 1)]),
                LineString([(0, 2), (0, 3)]),
            ],  # segments
            0.001,  # snap_threshold
            1.1,  # snap_threshold_error_multiplier
            [],  # assumed_result
        ),
    ]

    test_network_contour_grid_params = [
        (
            kb11_traces,  # traces
            kb11_area,  # areas
            0.001,  # snap_threshold
        ),
    ]

    test_report_snapping_loop_params = [
        (
            5,  # loop
            10,  # allowed_loops
            False,  # will_error
        ),
        (
            11,  # loop
            10,  # allowed_loops
            True,  # will_error
        ),
    ]


class ValidationHelpers:

    """
    Known examples of validation.
    """

    # Known Errors
    # ============

    known_errors = dict()

    known_multi_junction_gdfs = [
        gpd.GeoDataFrame(
            geometry=[
                LineString([Point(0, -3), Point(2, -3)]),
                LineString([Point(1, -4), Point(1, -2)]),
                LineString([Point(2, -4), Point(0.5, -2.50001)]),
            ]
        ),
        gpd.GeoDataFrame(
            geometry=[
                LineString([Point(-2, 0), Point(2, 0)]),
                LineString([Point(0, -2), Point(0, 4)]),
                LineString([Point(1, -1), Point(-1, 1)]),
            ]
        ),
        gpd.GeoDataFrame(
            geometry=[
                LineString([Point(-2, 4), Point(-3, 4)]),
                LineString([Point(-2.5, 3.5), Point(-3.5, 4.5)]),
                LineString([Point(-3.5, 3.5), Point(-2.5, 4.5)]),
            ]
        ),
        gpd.GeoDataFrame(
            geometry=[
                LineString([Point(-2, 2), Point(-4, 2)]),
                LineString(
                    [
                        Point(-3, 1),
                        Point(-3, 2 + 0.01 + 0.0001),
                    ]
                ),
            ]
        ),
        gpd.GeoDataFrame(geometry=should_result_in_some_error_ls_list),
        gpd.GeoDataFrame(geometry=should_result_in_multij_ls_list),
    ]

    known_multilinestring_gdfs = [
        gpd.GeoDataFrame(
            geometry=[
                MultiLineString(
                    [
                        LineString([Point(3, -4), Point(3, -1)]),
                        LineString([Point(3, 0), Point(3, 4)]),
                    ]
                )
            ],
        ),
        Helpers.multilinestring_critical_err_in_validation_gdf,
    ]
    known_vnode_gdfs = [
        gpd.GeoDataFrame(
            geometry=[
                LineString([Point(0, 0), Point(1.0001, 1)]),
                LineString([Point(1, 0), Point(1.0001, 0.9999)]),
            ]
        ),
        gpd.GeoDataFrame(geometry=should_result_in_vnode_ls_list),
    ]
    known_stacked_gdfs = [
        gpd.GeoDataFrame(
            geometry=[
                LineString([Point(0, -7), Point(0, -5)]),
                LineString([Point(-1, -7), Point(0 + 0.01, -6), Point(-1, -5)]),
            ]
        ),
    ]

    known_non_underlaping_gdfs_but_overlapping = [
        gpd.GeoDataFrame(geometry=results_in_false_positive_underlapping_ls)
    ]

    known_null_gdfs = [gpd.GeoDataFrame(geometry=[None, LineString()])]

    known_errors[MultiJunctionValidator.ERROR] = known_multi_junction_gdfs

    known_errors[GeomTypeValidator.ERROR] = known_multilinestring_gdfs
    known_errors[VNodeValidator.ERROR] = known_vnode_gdfs
    known_errors[StackedTracesValidator.ERROR] = known_stacked_gdfs
    known_errors[GeomNullValidator.ERROR] = known_null_gdfs
    known_errors[
        UnderlappingSnapValidator._OVERLAPPING
    ] = known_non_underlaping_gdfs_but_overlapping

    # False Positives
    # ===============

    known_false_positives = dict()

    known_non_stacked_gdfs = [
        gpd.GeoDataFrame(geometry=results_in_false_positive_stacked_traces_list),
    ]

    known_non_overlapping_gdfs = [
        gpd.GeoDataFrame(geometry=results_in_overlapping_ls_list)
    ]

    known_non_multijunction_gdfs = [
        gpd.GeoDataFrame(geometry=results_in_multijunction_why_ls_list),
        gpd.GeoDataFrame(geometry=results_in_multijunction_why_ls_list_2),
    ]

    known_false_positives[StackedTracesValidator.ERROR] = known_non_stacked_gdfs
    known_false_positives[
        UnderlappingSnapValidator._UNDERLAPPING
    ] = known_non_underlaping_gdfs_but_overlapping
    known_false_positives[
        UnderlappingSnapValidator._OVERLAPPING
    ] = known_non_overlapping_gdfs
    known_false_positives[MultiJunctionValidator.ERROR] = known_non_multijunction_gdfs

    # Class methods to generate pytest params for parametrization
    # ===========================================================

    @classmethod
    def generate_known_params(cls, error, false_positive):
        """
        Generate pytest.params.
        """
        knowns: List[gpd.GeoDataFrame] = (
            cls.known_errors[error]
            if not false_positive
            else cls.known_false_positives[error]
        )
        amounts = [
            (gdf.shape[0] if not hasattr(gdf, "error_amount") else gdf.error_amount)
            if error
            not in (
                UnderlappingSnapValidator._UNDERLAPPING,
                UnderlappingSnapValidator._OVERLAPPING,
            )
            else 1
            for gdf in knowns
        ]
        try:
            areas = [
                gpd.GeoDataFrame(geometry=[bounding_polygon(gdf)]) for gdf in knowns
            ]
        except ValueError:
            areas = [
                gpd.GeoDataFrame(geometry=[Polygon([(0, 0), (1, 1), (1, 0)])])
                for _ in knowns
            ]
        assert len(knowns) == len(areas) == len(amounts)
        return [
            pytest.param(
                known,
                area,
                f"{error}, {amount}",
                True,
                [error],
                amount,
                false_positive,
                id=f"{error}_{amount}".replace(" ", "_"),
            )
            for known, area, amount in zip(knowns, areas, amounts)
        ]

    @classmethod
    def get_all_errors(cls):
        """
        Get the defined errors.
        """
        # TODO: UnderlappingSnapValidator doesn't follow protocol
        all_error_types = set(
            [validator.ERROR for validator in trace_validation.ALL_VALIDATORS]
            + [
                UnderlappingSnapValidator._OVERLAPPING,
                UnderlappingSnapValidator._UNDERLAPPING,
            ]
        )
        all_errs = []
        for err in all_error_types:
            try:
                all_errs.extend(cls.generate_known_params(err, false_positive=False))
            except KeyError:
                pass
            try:
                all_errs.extend(cls.generate_known_params(err, false_positive=True))
            except KeyError:
                pass

        assert len(all_errs) > 0
        return all_errs


@lru_cache(maxsize=None)
def kb11_traces_lengths():
    """
    Get trace lengths of KB11.
    """
    return Helpers.kb11_traces.geometry.length


@lru_cache(maxsize=None)
def kb11_area_value():
    """
    Get area value of KB11.
    """
    return sum(Helpers.kb11_area.geometry.area)


@lru_cache(maxsize=None)
def hastholmen_traces_lengths():
    """
    Get trace lengths of hastholmen infinity lineaments.
    """
    return Helpers.hastholmen_traces.geometry.length


@lru_cache(maxsize=None)
def hastholmen_area_value():
    """
    Get area value of hastholmen.
    """
    return sum(Helpers.hastholmen_area.geometry.area)


@lru_cache(maxsize=None)
def test_populate_sample_cell_new_params():
    """
    Params for test_populate_sample_cell_new.
    """
    return [
        (
            box(0, 0, 2, 2),
            gpd.GeoDataFrame(geometry=[LineString([(-5, 1), (5, 1)])]),
            0.001,
        )
    ]


@lru_cache(maxsize=None)
def test_multinetwork_params():
    """
    Params for test_multinetwork.
    """
    return [
        (
            (
                dict(
                    trace_gdf=Helpers.geta_1_traces_1000_n,
                    area_gdf=Helpers.geta_1_1_area,
                    name="geta1_1",
                    circular_target_area=True,
                    snap_threshold=0.001,
                ),
                dict(
                    trace_gdf=Helpers.geta_1_traces_1000_n,
                    area_gdf=Helpers.geta_1_1_area,
                    name="geta1_2",
                    circular_target_area=True,
                    snap_threshold=0.001,
                ),
            ),
            1,
            5.0,
        ),
    ]


def test_ternary_heatmapping_params():
    """
    Params for test_ternary_heatmapping.
    """
    return [
        (
            np.array([0.2, 0.8, 0.1]),
            np.array([0.4, 0.1, 0.4]),
            np.array([0.4, 0.1, 0.5]),
            15,
        ),
        (
            np.array([0.4, 0.1, 0.4]),
            np.array([0.2, 0.8, 0.1]),
            np.array([0.4, 0.1, 0.5]),
            15,
        ),
    ]


def test_normalize_fit_to_area_params():
    """
    Params for test_normalize_fit_to_area.
    """
    return [
        length_distributions.LengthDistribution(
            name="kb11",
            lengths=kb11_traces_lengths(),
            area_value=kb11_area_value(),
        ),
        length_distributions.LengthDistribution(
            name="kb11_50",
            lengths=kb11_traces_lengths()[0:50],
            area_value=kb11_area_value(),
        ),
    ]


def test_concat_length_distributions_params():
    """
    Params for test_concat_length_distributions.
    """
    return [
        ([kb11_traces_lengths()], [kb11_area_value()], ["kb11_full"]),
        ([kb11_traces_lengths()[0:50]], [kb11_area_value()], ["kb11_50"]),
        (
            [kb11_traces_lengths(), hastholmen_traces_lengths()],
            [kb11_area_value(), hastholmen_area_value()],
            ["kb11_full", "hastholmen_full"],
        ),
    ]


def test_fit_to_multi_scale_lengths_params():
    """
    Params for test_fit_to_multi_scale_lengths.
    """
    return [
        [
            length_distributions.LengthDistribution(
                name="kb11", lengths=kb11_traces_lengths(), area_value=kb11_area_value()
            ),
            length_distributions.LengthDistribution(
                name="hastholmen",
                lengths=hastholmen_traces_lengths(),
                area_value=hastholmen_area_value(),
            ),
        ]
    ]


def test_aggregate_chosen_params():
    """
    Params for test_aggregate_chosen.
    """
    return [
        (
            [
                {
                    general.Param.AREA.value.name: 2000.0,
                    general.Param.CIRCLE_COUNT.value.name: 20.0,
                    general.Param.FRACTURE_INTENSITY_P21.value.name: 2.0,
                    general.Param.CONNECTIONS_PER_BRANCH.value.name: 1.2,
                },
                {
                    general.Param.AREA.value.name: 4000.0,
                    general.Param.CIRCLE_COUNT.value.name: 40.0,
                    general.Param.FRACTURE_INTENSITY_P21.value.name: 2.0,
                    general.Param.CONNECTIONS_PER_BRANCH.value.name: 1.4,
                },
            ],
            float,
            {
                general.Param.AREA.value.name: 6000.0,
                general.Param.CIRCLE_COUNT.value.name: 60.0,
                general.Param.FRACTURE_INTENSITY_P21.value.name: 2.0,
                general.Param.CONNECTIONS_PER_BRANCH.value.name: 1.3333333333333333,
            },
        ),
        (
            [
                {
                    general.Param.AREA.value.name: 2000.0,
                    "random-column": "Some name?",
                },
                {
                    general.Param.AREA.value.name: 4000.0,
                    "random-column": "Some other name?",
                },
            ],
            (float, str),
            {
                general.Param.AREA.value.name: 6000.0,
                "random-column": str(["Some name?", "Some other name?"]),
            },
        ),
    ]


def test_random_sample_of_circles_params():
    """
    Params for test_random_sample_of_circles.
    """
    return [
        (
            {
                "geta7": [
                    {
                        general.Param.AREA.value.name: 2000.0,
                        general.Param.CIRCLE_COUNT.value.name: 20.0,
                        general.Param.FRACTURE_INTENSITY_P21.value.name: 2.0,
                        general.Param.CONNECTIONS_PER_BRANCH.value.name: 1.2,
                    },
                    {
                        general.Param.AREA.value.name: 4000.0,
                        general.Param.CIRCLE_COUNT.value.name: 40.0,
                        general.Param.FRACTURE_INTENSITY_P21.value.name: 2.0,
                        general.Param.CONNECTIONS_PER_BRANCH.value.name: 1.4,
                    },
                ]
            },
            {"geta7": 12.5},
            1,
            None,
        ),
        (
            {
                "geta7": [
                    {
                        general.Param.AREA.value.name: 2000.0,
                        general.Param.CIRCLE_COUNT.value.name: 20.0,
                        general.Param.FRACTURE_INTENSITY_P21.value.name: 2.0,
                        general.Param.CONNECTIONS_PER_BRANCH.value.name: 1.2,
                    },
                    {
                        general.Param.AREA.value.name: 4000.0,
                        general.Param.CIRCLE_COUNT.value.name: 40.0,
                        general.Param.FRACTURE_INTENSITY_P21.value.name: 2.0,
                        general.Param.CONNECTIONS_PER_BRANCH.value.name: 1.4,
                    },
                ],
                "geta6": [
                    {
                        general.Param.AREA.value.name: 2000.0,
                        general.Param.CIRCLE_COUNT.value.name: 20.0,
                        general.Param.FRACTURE_INTENSITY_P21.value.name: 2.0,
                        general.Param.CONNECTIONS_PER_BRANCH.value.name: 1.2,
                    },
                    {
                        general.Param.AREA.value.name: 4000.0,
                        general.Param.CIRCLE_COUNT.value.name: 40.0,
                        general.Param.FRACTURE_INTENSITY_P21.value.name: 2.0,
                        general.Param.CONNECTIONS_PER_BRANCH.value.name: 1.4,
                    },
                ],
            },
            {"geta7": 12.5, "geta6": 5.0},
            2,
            None,
        ),
    ]


def test_collect_indexes_of_base_circles_params():
    """
    Params for test_collect_indexes_of_base_circles.
    """
    return [
        ([1, 2, 3], 1, [10, 10, 20]),
        ([1, 2, 4], 1, [10, 10, 30]),
        ([100, 2323, 10000], 2, [10, 10, 30]),
        ([100, 2323, 10000], 3, [10, 10, 30]),
    ]

pandas.Series