import logging
from abc import abstractmethod, ABC
from tools import clean_bounds_offset
from spin_image import FFT
from cache import persist_to_file, timeit
import tqdm
import numpy as np
import matplotlib.pyplot as plt
# from scipy.spatial import Voronoi
# import cv2
logger = logging.getLogger("fft")


class Evaluator(ABC):
    eval_points: list[np.ndarray]

    def extract(self, img: FFT):
        all_val = []
        all_idx = []
        for ev_points in self.eval_points:
            logger.info(f"Extracting points: {ev_points}")
            temp_val = []
            temp_idx = []
            for num in ev_points:
                if np.sum(self.mask == num) == 0:
                    continue
                temp_val.append(np.sum(img.intens[self.mask == num]))
                temp_idx.append(num)
            all_val.append(temp_val)
            all_idx.append(temp_idx)
        all_val.append([np.sum(img.intens[self.mask == -1])])
        all_idx.append([-1])
        return all_idx, all_val

    def debug(self, img: FFT):
        for count, ev_points in enumerate(self.eval_points, start=0):
            for num in ev_points:
                img.intens[self.mask == num] += count
        return img

    def get_mask(self):
        return self.mask

    @timeit
    def generate_mask(self, img: FFT, merge=False):
        hash = str(img.intens.shape)
        self.mask = self.gen_mask_helper(img)
        if merge:
            self.mask = self.merge_mask_helper()
            self.eval_points = [[a] for a in np.arange(len(self.eval_points))]

    @abstractmethod
    def gen_mask_helper(self, img: FFT, hash=str):
        pass

    @abstractmethod
    def merge_mask_helper(self, hash: str):
        pass

    def purge(self, img: FFT):
        img.intens[self.mask != -1] = 0


class Rect_Evaluator(Evaluator):
    # def __init__(self, points, eval_points):
    #    self.eval_points = eval_points
    #    self.points = points
    #    self.length = 4
    def __init__(self, spots: list[tuple[np.ndarray, np.ndarray]], length: int = 6):
        self.spots = spots
        self.length = length
        self.eval_points = []
        start = 0
        for sp in spots:
            self.eval_points.append(np.arange(start, start+sp[0].size))
            start += sp[0].size

    def merge_mask_helper(self):
        new_eval_points = np.arange(len(self.eval_points))
        mask = self.mask.copy()
        for nc, ev_points in zip(new_eval_points, self.eval_points):
            maske_low = np.min(ev_points) <= self.mask
            maske_high = np.max(ev_points) >= self.mask
            mask[np.logical_and(maske_high, maske_low)] = nc

        plt.figure()
        plt.imshow(mask)
        plt.figure()
        plt.imshow(self.mask)
        return mask

    def gen_mask_helper(self, img: FFT):
        mask = np.full_like(img.intens, -1)
        count = 0
        for spot_group in self.spots:
            logger.debug(f"Spot: {spot_group}")
            (x, y) = spot_group
            x, y = img.val2pos(x, y)
            for x_p, y_p in zip(x, y):
                xl, yl, xu, yu = clean_bounds_offset(
                    x_p, y_p, self.length, img.intens.shape)
                mask[xl:xu, yl:yu] = count
                count += 1
        return mask

