""" Predefined filters.

"""
from re import compile
from typing import Optional


__all__ = ("BoolFilter", "CountFilter", "FieldFilter", "RangeFilter",
           "RegexFilter", "SliceFilter")


class CountFilter(object):
    """ Add a count field.

    This will count the number of records that pass through this filter, which
    may not be the same as the total number of records read or written due to
    the actions of other filters.

    Use this with a Reader or Writer via its filter() method.

    """
    def __init__(self, field: str, start=1):
        """ Initialize this object

        :param field: output field
        :param start: start value
        """
        self._field = field
        self._count = start
        return

    def __call__(self, record: dict) -> dict:
        """ Increment the count.

        :param record: input record
        :return: modified record
        """
        record[self._field] = self._count
        self._count += 1
        return record


class _MatchFilter(object):
    """ Abstract filter base class for matching records.
    
    """
    def __init__(self, field: str, blacklist=False):
        """ Initialize this object.
        
        By default, records that match on the value for the given field are 
        passed through and all other records are dropped (whitelisting).

        :param field: input field
        :param blacklist: True to blacklist matching records
        """
        self._field = field
        self._blacklist = blacklist
        return
        
    def __call__(self, record: dict) -> Optional[dict]:
        """ Match a record.

        Matching records will be passed along for whitelisting and dropped for
        blacklisting.

        :param record: input record
        :return: input record or None
        """
        try:
            match = self._match(record[self._field])
        except KeyError:
            match = False
        return record if match != self._blacklist else None
        
    def _match(self, value) -> bool:
        """ Return True if the given field value is a match for the filter.
        
        """
        raise NotImplementedError
    

class BoolFilter(_MatchFilter):
    """ Filter records by the boolean value of a specific field.

    Accept values that are not false-y (False, 0, None, empty string, etc.) for
    whitelisting and reject them for blacklisting.

    Use this with a Reader or Writer via its filter() method.

    """
    def __init__(self, field: str, blacklist=False):
        """ Initialize this object.

        :param field: input field
        :param blacklist: True to blacklist matching records
        """
        super(BoolFilter, self).__init__(field, blacklist)
        return

    def _match(self, value) -> bool:
        """ Convert the value to a bool.

        :return: True if the value is not false-y
        """
        return bool(value)


class FieldFilter(_MatchFilter):
    """ Filter records by the value of a specific field.

    Use this with a Reader or Writer via its filter() method.

    """
    def __init__(self, field: str, values, blacklist=False):
        """ Initialize this object.

        :param field: input field
        :param values: values to match
        :param blacklist: True to blacklist matching records
        """
        super(FieldFilter, self).__init__(field, blacklist)
        self._values = set(values)
        return

    def _match(self, value) -> bool:
        """ Match a value against the filter values.

        :return: True for matching vale
        """
        return value in self._values


class RangeFilter(_MatchFilter):
    """ Filter records based on a range of values.

    Use this with a Reader or Writer via its filter() method.

    """
    def __init__(self, field: str, start=None, stop=None, blacklist=False):