import logging
import numpy as np

from .android import capture
from .util import debug_pattern, get_bounds, log_pattern_result

log = logging.getLogger('boxeye')


"""
## Example color pattern with the default values.

pattern = {
    'name': 'PATTERN-DEFAULTS',
    'color': None,
    'region': None,
    'cluster': 5,
    'size': (50, 5000),
}
find_color(**pattern)
# OR
find_color(img=whatever, **pattern)

"""


def find_colors(name=None, color_range=None, region=None, cluster=5,
                size=(50, 500000), debug=None, img=None):
    """ Find a color pattern in an image.
        Find all points in an image that match a color range, cluster them
        and then filter them by size and probably some other stuff too.

        Parameters
        ----------
        img : np.ndarray | None
            Optional image to find in, otherwise capture is used.
        color_range : (color1, color2)
            The range of colors to look for.
        cluster : int = 5 (pixels)
            The radius to use when clustering.
        size: (int, int)
            The minimum and maximum size a cluster can be in pixels.

        Returns
        -------
        [[(int, int)]...]
            The clusters.
    """

    (height, width, _) = img.shape
    if region is None:
        region = (0, 0, width, height)
    x0, y0, x1, y1 = region

    if img is None:
        img = capture()

    img = img[y0: y1, x0: x1]  # crop

    def find_color_range(img, color_range):
        """ Find all pixels that fall within color_range (min, max)
            and return their positions in a list.
        """
        color_min, color_max = color_range
        y, x = np.where(np.logical_and(np.all(img <= color_max, 2),
                                       np.all(img >= color_min, 2)))
        points = np.column_stack((x, y))  # x,y order
        return points

    # clustering
    def benland100_cluster(points, cluster):
        """ Cluster `points` together if they are within `cluster` of each
            other.
        """
        clusters = np.zeros(len(points), dtype='uint32')
        while True:  # loop until all points are clustered
            unclustered = clusters == 0
            remaining = np.count_nonzero(unclustered)
            if remaining == 0:
                break
            candidate = points[unclustered][np.random.randint(remaining)]
            dist = np.sum(np.square(points - candidate), axis=1)
            nearby_mask = dist <= cluster * cluster
            overlaps = set(list(clusters[nearby_mask]))
            overlaps.remove(0)
            if len(overlaps) == 0:
                G = np.max(clusters)+1  # new cluster
            else:
                G = np.min(list(overlaps))  # prefer smaller numbers
            clusters[nearby_mask] = G
            for g in overlaps:
                if g == G or g == 0:
                    continue
                clusters[clusters == g] = G
        unique, counts = np.unique(clusters, return_counts=True)
        clustered_points = np.array([points[clusters == c] for c in unique])
        return clustered_points

    points = find_color_range(img, color_range)
    clusters = benland100_cluster(points, cluster)

    # filter by size (array length)
    min_size, max_size = size
    clusters = [c for c in clusters if len(c) >= min_size 
                and len(c) <= max_size]

    # convert to region
    regions = [get_bounds(c) for c in clusters]
    # undo crop
    regions = [(rx0 + x0, ry0 + y0, rx1, ry1) for rx0, ry0, rx1, ry1 in regions]

    debug_pattern(debug, regions, debug_img)

    log_pattern_result(name, regions, 1.0)
    return regions
