from enum import IntEnum
from typing import Any, Final, Generator, Iterable, Tuple

import axipy


class Direction(IntEnum):
    MIXED: Final[int] = -1
    CLOCKWISE: Final[int] = 0
    COUNTERCLOCKWISE: Final[int] = 1


def _init_geom_generator(
    items: Iterable[axipy.Feature], count: int, task: axipy.DialogTask
) -> Generator[axipy.Geometry, Any, None]:
    for _, g in _init_feature_geom_generator(items, count, task):
        yield g


def _flat_geom_generator(
    items: Iterable[axipy.Feature], count: int, task: axipy.DialogTask
) -> Generator[axipy.Geometry, Any, None]:
    for g in _init_geom_generator(items, count, task):
        if isinstance(g, axipy.GeometryCollection):
            for elem in g:
                yield elem
        else:
            yield g


def _init_feature_geom_generator(
    items: Iterable[axipy.Feature], count: int, task: axipy.DialogTask
) -> Generator[Tuple[axipy.Feature, axipy.Geometry], Any, None]:
    progress_max: int = 1000
    one_step: int = 1
    step_needed: bool = False
    n: int = 0

    if count > progress_max:
        one_step = count // progress_max
        step_needed = True

    if step_needed:
        task.max = progress_max
    else:
        task.max = count

    for f in items:
        if task.is_canceled:
            return None

        if step_needed:
            n += 1
            if n % one_step == 0:
                task.value += 1
        else:
            task.value += 1

        g = f.geometry
        if g is not None:
            yield f, g


def _flat_feature_geom_generator(
    items: Iterable[axipy.Feature], count: int, task: axipy.DialogTask
) -> Generator[Tuple[axipy.Feature, axipy.Geometry], Any, None]:
    for f, g in _init_feature_geom_generator(items, count, task):
        if isinstance(g, axipy.GeometryCollection):
            for elem in g:
                yield f, elem
        else:
            yield f, g


def get_direction(list_points: axipy.ListPoint) -> Direction:
    # private function, for internal use only
    result: float = list_points._shadow.get_determinant()
    if -1e-14 < result < 1e-14:
        return Direction.MIXED
    elif result > 0:
        return Direction.CLOCKWISE
    else:
        return Direction.COUNTERCLOCKWISE
