#!D:\IDE\Anaconda3\envs\Microscope_Scan python
# -*- coding: UTF-8 -*-
"""
 @Author: Administrator
 @FileName: SurfaceFitting.py
 @DateTime: 2025/5/28 13:48
 @SoftWare: PyCharm
 @Author  ：xsh
"""
from PyQt5 import Qt
import numpy as np
from scipy.interpolate import griddata
from AppInterfaces.AppAlgorithmInterfaces.SurfaceFittingInterface import SurfaceFittingInterface
# import plotly.graph_objects as go


class SurfaceFitting(SurfaceFittingInterface):
    Locker: Qt.QMutex = Qt.QMutex()
    @classmethod
    def GetInstance(cls, *args, **kwargs) -> 'SurfaceFitting':
        if not cls.hasInstance():
            Locker = Qt.QMutexLocker(cls.Locker)
            if not cls.hasInstance():
                _instance: cls = cls.__new__(cls)
                _instance.__init__()
                cls.setInstance(_instance)
        return cls.getInstance()

    def __new__(cls, *args, **kwargs):
        if not cls.hasInstance():
            try:
                cls.setInstance(super().__new__(cls))
            except Exception as e:
                cls.setInstance(super().__new__(cls))
        return getattr(cls, '_instance')

    @classmethod
    def hasInstance(cls) -> bool:
        return hasattr(cls, '_instance') and getattr(cls, '_instance') is not None

    @classmethod
    def getInstance(cls):
        if cls.hasInstance():
            return getattr(cls, '_instance')
        return None

    @classmethod
    def setInstance(cls, value):
        if value is not None:
            setattr(cls, "_instance", value)


    def __init__(self, start_x: int = 0, start_y: int = 0, scan_size_x: int = None, scan_size_y: int = None,
                 z_min: float = None, z_max: float = None, **args):
        '''
            start_point: 起始点坐标 [x,y]  # 起始行其实列
            scan_size: 平扫时的行、列数。即x方向和y方向的个数 # 最大为：x方向30个， y方向50个
            mark_size: 打点时的行、列数。即x方向和y方向的个数
        '''
        self.initialization('SurfaceFitting')
        self.initAlgorithm(start_x, start_y, scan_size_x, scan_size_y, z_min, z_min)

    def initValue(self):
        super(SurfaceFitting, self).initValue()
        self.IsInitialize = False
        self.IsAvailable = False

    def isValid(self) -> bool:
        return self.isInitialize and self.isAvailable

    def initAlgorithm(self, start_x: int, start_y: int, scan_size_x: int, scan_size_y: int, z_min: float = None, z_max: float = None, **args):
        self.Max_X = 30 if not isinstance(scan_size_x, int) else abs(scan_size_x)
        self.Max_Y = 50 if not isinstance(scan_size_y, int) else abs(scan_size_y)
        max_z: int = None if not isinstance(z_max, (float, int)) else abs(int(z_max))
        min_z: int = None if not isinstance(z_min, (float, int)) else abs(int(z_min))
        self.start_point = [max(0, start_x - 1), max(0, start_y - 1), 0]
        scan_size = [self.Max_X, self.Max_Y]
        expand_size = [6, 6]  # 必须是偶数
        # 初始化平扫网格点
        self.face_points = None  # 平扫的点，100*100*3
        self.z_value_scope = [min_z, max_z] if isinstance(max_z, int) and isinstance(min_z, int) else None  # z轴范围

        def_x = scan_size[0] + expand_size[0]
        def_y = scan_size[1] + expand_size[1]

        # 初始化平扫平面网格
        x = np.linspace(0, def_x - 1, scan_size[0] + expand_size[0])
        y = np.linspace(0, def_y - 1, scan_size[1] + expand_size[1])
        self.grid_x, self.grid_y = np.meshgrid(x, y)

        # 计算扩张后的
        self.start_grid_point = [expand_size[0] // 2, expand_size[1] // 2, 0]

        # 平扫时相对于扩张后的平面的起止点的索引
        self.start_grid_x = expand_size[0] // 2
        self.start_grid_y = expand_size[1] // 2
        self.end_grid_x = expand_size[0] + scan_size[0]
        self.end_grid_y = expand_size[1] + scan_size[1]
        self.IsInitialize = True
        self.IsAvailable = False

    def start_update(self, x_row: int, y_col: int, z: int):
        # 预设平面的四个角点
        self.start_z = z
        self.def_points_x = [0, 0, self.end_grid_x - 1, self.end_grid_x - 1]
        self.def_points_y = [0, self.end_grid_y - 1, 0, self.end_grid_y - 1]
        self.def_points_z = [0, 0, 0, 0]
        self.def_points_update(x_row, y_col, z)
        self.IsAvailable = True

    def def_points_update(self, x_row: int, y_col: int, z: int):
        self.def_points_x.append(max(0, x_row - 1) - self.start_point[0] + self.start_grid_point[0])
        self.def_points_y.append(max(0, y_col - 1) - self.start_point[1] + self.start_grid_point[1])
        self.def_points_z.append(z - self.start_z)

    def surface_fitting_scan(self, ):
        ############## 090909hzh新加
        if len(self.def_points_x) > 8:
            x = np.array(self.def_points_x[4:])
            y = np.array(self.def_points_y[4:])
            z = np.array(self.def_points_z[4:])
            x_max = np.max(x)
            x_min = np.min(x)
            x_max = x == x_max
            x_min = x == x_min
            y_max = np.max(y)
            y_min = np.min(y)
            y_max = y == y_max
            y_min = y == y_min
            bool_l = np.logical_and(x_min, y_min)
            if bool_l.sum() == 1:
                self.def_points_z[0] = z[np.where(bool_l)[0]][0]
            bool_l = np.logical_and(x_min, y_max)
            if bool_l.sum() == 1:
                self.def_points_z[1] = z[np.where(bool_l)[0]][0]
            bool_l = np.logical_and(x_max, y_min)
            if bool_l.sum() == 1:
                self.def_points_z[2] = z[np.where(bool_l)[0]][0]
            bool_l = np.logical_and(x_max, y_max)
            if bool_l.sum() == 1:
                self.def_points_z[3] = z[np.where(bool_l)[0]][0]
        #######################################################################

        # 平扫曲面拟合
        zi = griddata((self.def_points_x, self.def_points_y), self.def_points_z, (self.grid_x, self.grid_y),
                      method='linear')
        face_points = np.stack([self.grid_x, self.grid_y, zi], axis=-1) + [1, 1, 0]  # 100*100*3
        # self.face_points = face_points[self.start_grid_y:self.end_grid_y, self.start_grid_x:self.end_grid_x, :]
        self.face_points = face_points + np.array([[self.start_point]]) - np.array([[self.start_grid_point]])
        z_value = self.face_points[:, :, 2] + self.start_z
        z_mean = np.mean(z_value)
        z_value[z_value <= self.z_value_scope[0]] = z_mean
        z_value[z_value >= self.z_value_scope[1]] = z_mean

        self.face_points[:, :, 2] = z_value

    # def surface_fitting_scan(self):
    #     # 平扫曲面拟合
    #     zi = griddata((self.def_points_x, self.def_points_y), self.def_points_z, (self.grid_x, self.grid_y),
    #                   method='cubic')
    #     face_points = np.stack([self.grid_x, self.grid_y, zi], axis=-1)  # 100*100*3
    #     self.face_points = face_points[self.start_grid_y:self.end_grid_y, self.start_grid_x:self.end_grid_x, :]
    #     self.face_points = self.face_points + np.array([[self.start_point]]) - np.array([[self.start_grid_point]])
    #
    #     self.face_points[:,:,2] = np.clip(self.face_points[:,:,2] + self.start_z, self.z_value_scope[0], self.z_value_scope[1])
    #     # return face_points

    # def get_point_scan(self, x_row: int, y_col: int) -> int:
    #     # 输入网格索引[行,列]， 返回网格索引的xyz坐标
    #     re_point = self.face_points[max(0, y_col - self.start_point[1] - 1), max(0, x_row - self.start_point[0] - 1)]
    #     return re_point + [1, 1, 0]
    #
    # def get_point_scan_list(self, x_row: int, y_col: int) -> list[int]:
    #     # 输入网格索引[行,列]， 返回网格索引的xyz坐标
    #     re_point = self.face_points[
    #         max(0, y_col - self.start_point[1] - 1), max(0, x_row - self.start_point[0] - 1)].tolist()
    #     return re_point + [1, 1, 0]

    def get_point_scan_all(self) -> np.ndarray:
        # 输入网格索引[行,列]， 返回网格索引的xyz坐标
        return self.face_points

    def set_begin_point(self, x_row: int, y_col: int, z: int):
        self.start_update(x_row, y_col, z)
        self.surface_fitting_scan()

    def append_point(self, x_row: int, y_col: int, z: int):
        self.def_points_update(x_row, y_col, z)
        self.surface_fitting_scan()

    def get_point(self, x_row: int, y_col: int) -> (int, int, int):
        buffer: list = self.face_points[max(0, y_col - self.start_point[1] + self.start_grid_y - 1), max(0, x_row -self.start_point[0]) + self.start_grid_x - 1].tolist()
        return int(buffer[0]), int(buffer[1]), int(buffer[2])

    def get_z_point(self, x_row: int, y_col: int) -> int:
        buffer: list = self.face_points[max(0, y_col - self.start_point[1] + self.start_grid_y - 1), max(0, x_row - self.start_point[0]) + self.start_grid_x - 1].tolist()
        return int(buffer[2])






    # def __init__(self, start_x: int = 0, start_y: int = 0, scan_size_x: int = None, scan_size_y: int = None,
    #              z_min: float = None, z_max: float = None, **args):
    #     '''
    #         start_point: 起始点坐标 [x,y]  # 起始行其实列
    #         scan_size: 平扫时的行、列数。即x方向和y方向的个数 # 最大为：x方向30个， y方向50个
    #         mark_size: 打点时的行、列数。即x方向和y方向的个数
    #     '''
    #     self.initialization('SurfaceFitting')
    #     self.initAlgorithm(start_x, start_y, scan_size_x, scan_size_y, z_min, z_min)
    #
    # def initValue(self):
    #     super(SurfaceFitting, self).initValue()
    #     self.IsInitialize = False
    #     self.IsAvailable = False
    #
    # def isValid(self) -> bool:
    #     return self.isInitialize and self.isAvailable
    #
    # def initAlgorithm(self, start_x: int, start_y: int, scan_size_x: int, scan_size_y: int, z_min: float = None,
    #                   z_max: float = None, **args):
    #     self.Max_X = 30 if not isinstance(scan_size_x, int) else abs(scan_size_x)
    #     self.Max_Y = 50 if not isinstance(scan_size_y, int) else abs(scan_size_y)
    #     max_z: int = None if not isinstance(z_max, (float, int)) else abs(int(z_max))
    #     min_z: int = None if not isinstance(z_min, (float, int)) else abs(int(z_min))
    #     self.start_point = [max(0, start_x - 1), max(0, start_y - 1), 0]
    #     scan_size = [self.Max_X, self.Max_Y]
    #     expand_size = [6, 6]  # 必须是偶数
    #     # 初始化平扫网格点
    #     self.face_points = None  # 平扫的点，100*100*3
    #     self.z_value_scope = [min_z, max_z] if isinstance(max_z, int) and isinstance(min_z, int) else None  # z轴范围
    #
    #     def_x = scan_size[0] + expand_size[0]
    #     def_y = scan_size[1] + expand_size[1]
    #
    #     # 初始化平扫平面网格
    #     x = np.linspace(0, def_x - 1, scan_size[0] + expand_size[0])
    #     y = np.linspace(0, def_y - 1, scan_size[1] + expand_size[1])
    #     self.grid_x, self.grid_y = np.meshgrid(x, y)
    #
    #     # 计算扩张后的
    #     self.start_grid_point = [expand_size[0] // 2, expand_size[1] // 2, 0]
    #
    #     # 平扫时相对于扩张后的平面的起止点的索引
    #     self.start_grid_x = expand_size[0] // 2
    #     self.start_grid_y = expand_size[1] // 2
    #     self.end_grid_x = expand_size[0] // 2 + scan_size[0]
    #     self.end_grid_y = expand_size[1] // 2 + scan_size[1]
    #     self.IsInitialize = True
    #     self.IsAvailable = False
    #
    # def start_update(self, x_row: int, y_col: int, z: int):
    #     # 预设平面的四个角点
    #     self.start_z = z
    #     self.def_points_x = [0, 0, self.end_grid_x, self.end_grid_x]
    #     self.def_points_y = [0, self.end_grid_y, 0, self.end_grid_y]
    #     self.def_points_z = [0, 0, 0, 0]
    #     self.def_points_update(max(0, x_row - 1), max(0, y_col - 1), z)
    #     self.IsAvailable = True
    #
    # def def_points_update(self, x_row: int, y_col: int, z: int):
    #     self.def_points_x.append(max(0, x_row - 1) - self.start_point[0] + self.start_grid_point[0])
    #     self.def_points_y.append(max(0, y_col - 1) - self.start_point[1] + self.start_grid_point[1])
    #     self.def_points_z.append(z - self.start_z)
    #
    # def surface_fitting_scan(self, ):
    #     ############## 090909hzh新加
    #     if len(self.def_points_x) > 8:
    #         x = np.array(self.def_points_x[4:])
    #         y = np.array(self.def_points_y[4:])
    #         z = np.array(self.def_points_z[4:])
    #         x_max = np.max(x)
    #         x_min = np.min(x)
    #         x_max = x == x_max
    #         x_min = x == x_min
    #         y_max = np.max(y)
    #         y_min = np.min(y)
    #         y_max = y == y_max
    #         y_min = y == y_min
    #         bool_l = np.logical_and(x_min, y_min)
    #         if bool_l.sum() == 1:
    #             self.def_points_z[0] = z[np.where(bool_l)[0]][0]
    #         bool_l = np.logical_and(x_min, y_max)
    #         if bool_l.sum() == 1:
    #             self.def_points_z[1] = z[np.where(bool_l)[0]][0]
    #         bool_l = np.logical_and(x_max, y_min)
    #         if bool_l.sum() == 1:
    #             self.def_points_z[2] = z[np.where(bool_l)[0]][0]
    #         bool_l = np.logical_and(x_max, y_max)
    #         if bool_l.sum() == 1:
    #             self.def_points_z[3] = z[np.where(bool_l)[0]][0]
    #     #######################################################################
    #
    #     # 平扫曲面拟合
    #     zi = griddata((self.def_points_x, self.def_points_y), self.def_points_z, (self.grid_x, self.grid_y),
    #                   method='cubic')
    #     face_points = np.stack([self.grid_x, self.grid_y, zi], axis=-1)  # 100*100*3
    #     self.face_points = face_points[self.start_grid_y:self.end_grid_y, self.start_grid_x:self.end_grid_x, :]
    #     self.face_points = self.face_points + np.array([[self.start_point]]) - np.array([[self.start_grid_point]])
    #     z_value = self.face_points[:, :, 2] + self.start_z
    #     z_mean = np.mean(z_value)
    #     z_value[z_value <= self.z_value_scope[0]] = z_mean
    #     z_value[z_value >= self.z_value_scope[1]] = z_mean
    #
    #     self.face_points[:, :, 2] = z_value
    #
    # def get_point_scan(self, x_row: int, y_col: int) -> int:
    #     # 输入网格索引[行,列]， 返回网格索引的xyz坐标
    #     return self.face_points[max(0, y_col - self.start_point[1] - 1), max(0, x_row - self.start_point[0] - 1), 2]
    #
    # def get_point_scan_list(self, x_row: int, y_col: int) -> list[int]:
    #     # 输入网格索引[行,列]， 返回网格索引的xyz坐标
    #     return self.face_points[
    #         max(0, y_col - 1) - self.start_point[1], max(0, max(0, x_row - 1) - self.start_point[0])].tolist()
    #
    # def get_point_scan_all(self) -> np.ndarray:
    #     # 输入网格索引[行,列]， 返回网格索引的xyz坐标
    #     return self.face_points
    #
    # def set_begin_point(self, x_row: int, y_col: int, z: int):
    #     self.start_update(max(0, x_row - 1), max(0, y_col - 1), z)
    #     self.surface_fitting_scan()
    #
    # def append_point(self, x_row: int, y_col: int, z: int):
    #     self.def_points_update(max(0, x_row - 1), max(0, y_col - 1), z)
    #     self.surface_fitting_scan()
    #
    # def get_point(self, x_row: int, y_col: int) -> (int, int, int):
    #     buffer: list = self.face_points[
    #         max(0, y_col - self.start_point[1] - 1), max(0, x_row - self.start_point[0] - 1)].tolist()
    #     return int(buffer[0]), int(buffer[1]), int(buffer[2])
    #
    # def get_z_point(self, x_row: int, y_col: int) -> int:
    #     buffer: list = self.face_points[
    #         max(0, y_col - self.start_point[1] - 1), max(0, x_row - self.start_point[0] - 1)].tolist()
    #     return int(buffer[2])