import multiprocessing.pool
import typing
from typing import Any, Iterator, Iterable, Union, Sequence, List, Optional, Tuple

from pyopal.lib import ALIGN_ALGORITHM, FullResult, BaseDatabase


__version__: str


class KmerGenerator:
    @property
    def scorer(self) -> Scorer: ...
    def __init__(self, scorer: Scorer, kmer_length: int = 3, score_threshold: int = 13) -> None: ...


class Scorer:
    @property
    def gap_open(self) -> int: ...
    @property
    def gap_extend(self) -> int: ...
    @property
    def name(self) -> str: ...
    def __init__(self, name: str = "BLOSUM62", gap_open: int = 10, gap_extend: int = 1) -> None: ...
    def __repr__(self) -> str: ...
    def __reduce__(self) -> Tuple[Any, ...]: ...
    def __buffer__(self) -> memoryview: ...


class EValue:
    @property
    def scorer(self) -> Scorer: ...
    def __init__(self, database_size: int, scorer: Scorer) -> None: ...
    def calculate(self, scorer: int, query_length: int, target_length: int) -> float: ...


class Sequences(BaseDatabase):
    def __init__(self, sequences: Iterable[Union[str, bytes, bytearray]] = ()) -> None: ...
    def __reduce__(self) -> Tuple[Any, ...]: ...
    @typing.overload
    def __getitem__(self, index: int) -> str: ...
    @typing.overload
    def __getitem__(self, index: slice) -> Sequences: ...
    def __getitem__(self, index: Union[int, slice]) -> Union[str, Sequences]: ...
    def clear(self) -> None: ...
    def append(self, sequence: Union[str, bytes, bytearray]) -> None: ...
    def extend(self, sequences: Iterable[Union[str, bytes, bytearray]]) -> None: ...
    def reverse(self) -> None: ...
    def insert(self, index: int, sequence: Union[str, bytes, bytearray]) -> None: ...
    def mask(self, bitmask: Sequence[bool]) -> Sequences: ...
    def extract(self, indices: Sequence[int]) -> Sequences: ...


class FilterScore:
    @property
    def index(self) -> int: ...
    @property
    def score(self) -> int: ...
    def __init__(self, index: int, score: int) -> None: ...
    def __repr__(self) -> str: ...
    def __eq__(self, other: object) -> bool: ...

class FilterResult:
    @property
    def database_size(self) -> int: ...
    @property
    def database_length(self) -> int: ...
    # @property
    # def entries
    @property
    def indices(self) -> List[int]: ...


class HeuristicFilter:
    @property
    def queries(self) -> Sequences: ...
    @property
    def kmer_generator(self) -> KmerGenerator: ...
    @property
    def score_threshold(self) -> int: ...
    @property
    def max_candidates(self) -> int: ...
    @property
    def threads(self) -> int: ...
    @property
    def pool(self) -> Optional[multiprocessing.pool.ThreadPool]: ...
    @property
    def scorer(self) -> Scorer: ...
    def __init__(
        self,
        queries: Sequences,
        *,
        kmer_length: int = 3,
        max_candidates: int = 30000,
        score_threshold: int = 13,
        scorer: Scorer = Scorer(),
        threads: int = 0,
        pool: Optional[multiprocessing.pool.ThreadPool] = None
    ) -> None: ...
    def score(self, database: Sequences) -> HeuristicFilter: ...
    def finish(self) -> FilterResult: ...


class Hit:
    @property
    def query_index(self) -> int: ...
    @property
    def target_index(self) -> int: ...
    @property
    def evalue(self) -> float: ...
    @property
    def result(self) -> FullResult: ...
    @property
    def score(self) -> float: ...
    def __init__(
        self,
        query_index: int,
        target_index: int,
        evalue: float,
        result: FullResult
    ) -> None: ...
    def __repr__(self) -> str: ...


def search(
    queries,
    targets,
    *,
    gap_open: int = 10,
    gap_extend: int = 1,
    scorer_name: str = "BLOSUM62",
    kmer_length: int = 3,
    max_candidates: int = 30000,
    score_threshold: int = 13,
    max_alignments: int = 10,
    max_evalue: float = 10.0,
    algorithm: ALIGN_ALGORITHM = "sw",
    threads: int = 0,
) -> Iterator[Hit]: ...