import copy as cp
from asyncio.windows_events import NULL
from concurrent.futures import thread
from copy import copy
from email.policy import default
import os
import pdb
from threading import Thread
import numpy as np
# from plugins.basic_change.main import MyDialog
from rscder.gui.actions import ActionManager
from rscder.plugins.basic import BasicPlugin
from PyQt5.QtWidgets import QAction, QDialog, QHBoxLayout, QVBoxLayout, QPushButton,QWidget,QLabel,QLineEdit,QPushButton,QComboBox
from PyQt5.QtGui import QIcon,QPixmap
from PyQt5.QtCore import Qt
from rscder.gui.layercombox import LayerCombox,PairLayerCombox
from rscder.utils.icons import IconInstance
from rscder.utils.geomath import geo2imageRC, imageRC2geo
from rscder.utils.project import Project, RasterLayer, PairLayer,ResultPointLayer,MultiBandRasterLayer
from In_one.otsu import OTSU
from osgeo import gdal
from plugins.In_one import pic
import math
from skimage.filters import rank
from skimage.morphology import disk, rectangle

def Meanfilter(x_size,y_size,layer:MultiBandRasterLayer):
    x_size = int(x_size)
    y_size = int(y_size)
    pth = layer.path
    if pth is None:
        return
    
    ds = gdal.Open(pth)
    band_count = ds.RasterCount
    out_path = os.path.join(Project().other_path, '{}_mean_filter.tif'.format(layer.name))
    out_ds = gdal.GetDriverByName('GTiff').Create(out_path, ds.RasterXSize, ds.RasterYSize, band_count, ds.GetRasterBand(1).DataType)
    out_ds.SetProjection(ds.GetProjection())
    out_ds.SetGeoTransform(ds.GetGeoTransform())

    for i in range(band_count):
        band = ds.GetRasterBand(i+1)
        data = band.ReadAsArray()
        
        data = rank.mean(data, rectangle(y_size, x_size))

        out_band = out_ds.GetRasterBand(i+1)
        out_band.WriteArray(data)

    out_ds.FlushCache()
    del out_ds
    del ds
    return out_path

def basic_cd(pth1:str,pth2:str,layer_parent:PairLayer,send_message):
    ds1:gdal.Dataset=gdal.Open(pth1)
    ds2:gdal.Dataset=gdal.Open(pth2)
    
    cell_size = layer_parent.cell_size
    xsize = layer_parent.size[0]
    ysize = layer_parent.size[1]

    band = ds1.RasterCount
    yblocks = ysize // cell_size[1]

    driver = gdal.GetDriverByName('GTiff')
    out_tif = os.path.join(Project().cmi_path, 'temp.tif')
    out_ds = driver.Create(out_tif, xsize, ysize, 1, gdal.GDT_Float32)
    geo=layer_parent.grid.geo
    proj=layer_parent.grid.proj
    out_ds.SetGeoTransform(geo)
    out_ds.SetProjection(proj)

    max_diff = 0
    min_diff = math.inf
    
    start1x,start1y=geo2imageRC(ds1.GetGeoTransform(),layer_parent.mask.xy[0],layer_parent.mask.xy[1])
    end1x,end1y=geo2imageRC(ds1.GetGeoTransform(),layer_parent.mask.xy[2],layer_parent.mask.xy[3])

    start2x,start2y=geo2imageRC(ds2.GetGeoTransform(),layer_parent.mask.xy[0],layer_parent.mask.xy[1])
    end2x,end2y=geo2imageRC(ds2.GetGeoTransform(),layer_parent.mask.xy[2],layer_parent.mask.xy[3])

    for j in range(yblocks + 1):#该改这里了
        
        send_message.emit(f'计算{j}/{yblocks}')
        block_xy1 = (start1x, start1y+j * cell_size[1])
        block_xy2 = (start2x,start2y+j*cell_size[1])
        block_xy=(0,j * cell_size[1])
        if block_xy1[1] > end1y or block_xy2[1] > end2y:
            break
        block_size=(xsize, cell_size[1])
        block_size1 = (xsize, cell_size[1])  
        block_size2 = (xsize,cell_size[1])
        if block_xy[1] + block_size[1] > ysize:
            block_size = (xsize, ysize - block_xy[1])
        if block_xy1[1] + block_size1[1] > end1y:
            block_size1 = (xsize,end1y - block_xy1[1])
        if block_xy2[1] + block_size2[1] > end2y:
            block_size2 = (xsize, end2y - block_xy2[1]) 
        block_data1 = ds1.ReadAsArray(*block_xy1, *block_size1)
        block_data2 = ds2.ReadAsArray(*block_xy2, *block_size2)

        if band == 1:
            block_data1 =  block_data1[None, ...]
            block_data2 =  block_data2[None, ...]
        # pdb.set_trace()
        block_diff = block_data1.sum(0) - block_data2.sum(0)
        block_diff = block_diff.astype(np.float32)
        block_diff = np.abs(block_diff)
        
        min_diff = min(min_diff, block_diff[block_diff > 0].min())
        max_diff = max(max_diff, block_diff.max())
        out_ds.GetRasterBand(1).WriteArray(block_diff, *block_xy)

        send_message.emit(f'完成{j}/{yblocks}')
    del ds2
    del ds1
    out_ds.FlushCache()
    del out_ds
    send_message.emit('归一化概率中...')
    temp_in_ds = gdal.Open(out_tif) 
    
    out_normal_tif = os.path.join(Project().cmi_path, '{}_{}_cmi.tif'.format(layer_parent.name, int(np.random.rand() * 100000)))
    out_normal_ds = driver.Create(out_normal_tif, xsize, ysize, 1, gdal.GDT_Byte)
    out_normal_ds.SetGeoTransform(geo)
    out_normal_ds.SetProjection(proj)
    # hist = np.zeros(256, dtype=np.int32)
    for j in range(yblocks+1):
        block_xy = (0, j * cell_size[1])
        if block_xy[1] > ysize:
            break
        block_size = (xsize, cell_size[1])
        if block_xy[1] + block_size[1] > ysize:
            block_size = (xsize, ysize - block_xy[1])
        block_data = temp_in_ds.ReadAsArray(*block_xy, *block_size)
        block_data = (block_data - min_diff) / (max_diff - min_diff) * 255
        block_data = block_data.astype(np.uint8)
        out_normal_ds.GetRasterBand(1).WriteArray(block_data, *block_xy)
        # hist_t, _ = np.histogram(block_data, bins=256, range=(0, 256))
        # hist += hist_t
    # print(hist)
    del temp_in_ds
    del out_normal_ds
    try:
        os.remove(out_tif)
    except:
        pass

    # raster_result_layer = SingleBandRasterLayer(None, True, out_normal_tif, BasicLayer.BOATH_VIEW)

    # layer1.layer_parent.add_result_layer(point_result_lalyer)
    # layer1.layer_parent.add_result_layer(raster_result_layer)

    # self.send_message.emit('完成计算变化表格')
                
    send_message.emit('差分法计算完成')
    return out_normal_tif

def otsu(pth,name,send_message):
    ds = gdal.Open(pth)
    band = ds.GetRasterBand(1)
    # band_count = ds.RasterCount
    
    hist = np.zeros(256, dtype=np.int)
    xsize = ds.RasterXSize
    ysize = ds.RasterYSize

    max_pixels = 1e7
    max_rows = max_pixels // xsize
    if max_rows < 1:
        max_rows = 1
    if max_rows > ysize:
        max_rows = ysize
    block_count = ysize // max_rows + 1
    for i in range(block_count):
        start_row = i * max_rows
        end_row = min((i + 1) * max_rows, ysize)
        block = band.ReadAsArray(0, start_row, xsize, end_row - start_row)
        hist += np.histogram(block.flatten(), bins=256, range=(0, 255))[0]
    hist = hist.astype(np.float32)
    gap = OTSU(hist)
    send_message.emit('阈值为：{}'.format(gap))

    out_th = os.path.join(Project().bcdm_path, '{}_otsu_bcdm.tif'.format(name))
    out_ds = gdal.GetDriverByName('GTiff').Create(out_th, xsize, ysize, 1, gdal.GDT_Byte)
    out_ds.SetGeoTransform(ds.GetGeoTransform())
    out_ds.SetProjection(ds.GetProjection())
    out_band = out_ds.GetRasterBand(1)

    for i in range(block_count):
        start_row = i * max_rows
        end_row = min((i + 1) * max_rows, ysize)
        block = band.ReadAsArray(0, start_row, xsize, end_row - start_row)
        out_band.WriteArray(block > gap, 0, start_row)
    out_band.FlushCache()
    out_ds = None
    ds = None
    send_message.emit('OTSU阈值完成')
    return out_th,gap
    #otsu_layer = SingleBandRasterLayer(path = out_th, style_info={})
    #layer.layer_parent.add_result_layer(otsu_layer)

def thresh(pth,gap,name,send_message):
    ds = gdal.Open(pth)
    band = ds.GetRasterBand(1)
    # band_count = ds.RasterCount
    
    
    xsize = ds.RasterXSize
    ysize = ds.RasterYSize

    max_pixels = 1e7
    max_rows = max_pixels // xsize
    if max_rows < 1:
        max_rows = 1
    if max_rows > ysize:
        max_rows = ysize
    block_count = ysize // max_rows + 1
    # for i in range(block_count):
    #     start_row = i * max_rows
    #     end_row = min((i + 1) * max_rows, ysize)
    #     block = band.ReadAsArray(0, start_row, xsize, end_row - start_row)
        # hist += np.histogram(block.flatten(), bins=256, range=(0, 255))[0]
    # hist = hist.astype(np.float32)
    send_message.emit('阈值为：{}'.format(gap))

    out_th = os.path.join(Project().bcdm_path, '{}_thresh{}_bcdm.tif'.format(name,gap))
    out_ds = gdal.GetDriverByName('GTiff').Create(out_th, xsize, ysize, 1, gdal.GDT_Byte)
    out_ds.SetGeoTransform(ds.GetGeoTransform())
    out_ds.SetProjection(ds.GetProjection())
    out_band = out_ds.GetRasterBand(1)

    for i in range(block_count):
        start_row = i * max_rows
        end_row = min((i + 1) * max_rows, ysize)
        block = band.ReadAsArray(0, start_row, xsize, end_row - start_row)
        out_band.WriteArray(block > gap, 0, start_row)
    out_band.FlushCache()
    out_ds = None
    ds = None
    send_message.emit('自定义阈值分割完成')
    return out_th
    #otsu_layer = SingleBandRasterLayer(path = out_th, style_info={})
    #layer.layer_parent.add_result_layer(otsu_layer)

def table_layer(pth,layer,name,send_message,dict):
    send_message.emit('正在计算表格结果...')
    cell_size = layer.layer_parent.cell_size
    ds = gdal.Open(pth)
    xsize = ds.RasterXSize
    ysize = ds.RasterYSize
    geo = ds.GetGeoTransform()

    out_csv = os.path.join(Project().other_path, f'{name}_table_result.csv')
    yblocks = ysize // cell_size[1] + 1
    xblocks = xsize // cell_size[0] + 1
    with open(out_csv, 'w') as f:
        f.write('x,y,diff,status\n')
        for j in range(yblocks):
            block_xy = (0, j * cell_size[1])
            block_size = (xsize, cell_size[1])
            if block_xy[1] + block_size[1] > ysize:
                block_size = (xsize, ysize - block_xy[1])
            block_data = ds.ReadAsArray(*block_xy, *block_size)
            for i in range(xblocks):
                start_x = i * cell_size[0]
                end_x = start_x + cell_size[0]
                if end_x > xsize:
                    end_x = xsize
                block_data_xy = block_data[:, start_x:end_x]
                
                center_x = start_x + cell_size[0] // 2
                center_y = j * cell_size[1] + cell_size[1] // 2
                center_x = center_x * geo[1] + geo [0]
                center_y = center_y * geo[5] + geo [3]
                f.write(f'{center_x},{center_y},{block_data_xy.mean() * 100},{int(block_data_xy.mean() > 0.5)}\n')

    result_layer = ResultPointLayer(out_csv, enable=True, proj=layer.proj, geo=layer.geo,result_path=dict)
    # print(result_layer.result_path)
    layer.layer_parent.add_result_layer(result_layer)
    send_message.emit('计算完成')

class LockerButton(QPushButton):
    def __init__(self,parent=NULL):
        super(LockerButton,self).__init__(parent)
        m_imageLabel =  QLabel(self)
        m_imageLabel.setFixedWidth(20)
        m_imageLabel.setScaledContents(True)
        m_imageLabel.setStyleSheet("QLabel{background-color:transparent;}")
        m_textLabel =  QLabel(self)
        m_textLabel.setStyleSheet("QLabel{background-color:transparent;}")
        self.m_imageLabel=m_imageLabel
        self.m_textLabel=m_textLabel
        self.hide_=1
        mainLayout =  QHBoxLayout()
        
        mainLayout.addWidget(self.m_imageLabel)
        mainLayout.addWidget(self.m_textLabel)
        mainLayout.setContentsMargins(0,0,0,0)
        mainLayout.setSpacing(0)
        self.setLayout(mainLayout)
    def SetImageLabel(self, pixmap:QPixmap):
        self.m_imageLabel.setPixmap(pixmap)
    def SetTextLabel(self, text):
        self.m_textLabel.setText(text)


class selectCombox(QComboBox):
    def __init__(self, parent,list:list,default='--') :
        super(selectCombox,self).__init__(parent)
        self.choose=None
        self.list=list
        self.default=default
        self.addItem(default, None)
        self.addItems(list)
        self.currentIndexChanged.connect(self.on_change)
    
    def on_change(self,index):
        if index == 0:
            self.choose=self.default
        else:
            self.choose=self.list[index-1]
        # print(self.choose)

class AllInOne(QDialog):
    def __init__(self, pre,cd,threshold,parent=None):
        super(AllInOne, self).__init__(parent)
        self.setWindowTitle('变化检测')
        self.setWindowIcon(IconInstance().LOGO)
        self.pre=pre#['均值滤波','test滤波']
        self.cd=cd#['差分法','test法']
        self.threshold=threshold#['OTSU']
        self.initUI()

    def initUI(self):
        #图层
        self.layer_combox = PairLayerCombox(self)
        layerbox = QHBoxLayout()
        layerbox.addWidget(self.layer_combox)

        #预处理
        filterWeight=QWidget(self)
        filterlayout=QVBoxLayout()
        filerButton =LockerButton(filterWeight)
        filerButton.setObjectName("filerButton")
        filerButton.SetTextLabel("预处理")
        filerButton.SetImageLabel(QPixmap('plugins/In_one/pic/箭头_列表展开.png'))
        filerButton.setStyleSheet("#filerButton{background-color:transparent;border:none;}"
        "#filerButton:hover{background-color:rgba(195,195,195,0.4);border:none;}")
        self.pre_select=selectCombox(self,self.pre)
        
        x_size_input = QLineEdit(self)
        x_size_input.setText('3')
        y_size_input = QLineEdit(self)
        y_size_input.setText('3')
        size_label = QLabel(self)
        size_label.setText('窗口大小:')
        time_label = QLabel(self)
        time_label.setText('X')
        self.x_size_input = x_size_input
        self.y_size_input = y_size_input
        hlayout1 = QHBoxLayout()
        hlayout1.addWidget(size_label)
        hlayout1.addWidget(x_size_input)
        hlayout1.addWidget(time_label)
        hlayout1.addWidget(y_size_input)
        vlayout = QVBoxLayout()
        vlayout.addWidget(self.pre_select)
        vlayout.addLayout(hlayout1)
        filterWeight.setLayout(vlayout)
        filterlayout.addWidget(filerButton)
        filterlayout.addWidget(filterWeight)
        #变化检测
        changelayout=QVBoxLayout()
        changeWeight=QWidget(self)
        changeButton =LockerButton(changeWeight)
        changeButton.setObjectName("changeButton")
        changeButton.SetTextLabel("变化检测")
        changeButton.SetImageLabel(QPixmap('plugins/In_one/pic/箭头_列表展开.png'))
        changeButton.setStyleSheet("#changeButton{background-color:transparent;border:none;}"
        "#changeButton:hover{background-color:rgba(195,195,195,0.4);border:none;}")
        changeWeightlayout=QVBoxLayout()
        self.cd_select=selectCombox(self,self.cd)
        changeWeightlayout.addWidget(self.cd_select)
        changeWeight.setLayout(changeWeightlayout)
        changelayout.addWidget(changeButton)
        changelayout.addWidget(changeWeight)

        #阈值处理
        thresholdlayout=QVBoxLayout()
        thresholdWeight=QWidget(self)
        thresholdButton =LockerButton(thresholdWeight)
        thresholdButton.setObjectName("thresholdButton")
        thresholdButton.SetTextLabel("阈值处理")
        thresholdButton.SetImageLabel(QPixmap('plugins/In_one/pic/箭头_列表展开.png'))
        thresholdButton.setStyleSheet("#thresholdButton{background-color:transparent;border:none;}"
        "#thresholdButton:hover{background-color:rgba(195,195,195,0.4);border:none;}")
        self.threshold_select=selectCombox(self,self.threshold,default='手动阈值')
        self.threshold_input=QLineEdit(self)
        self.threshold_input.setText('0.5')
        self.threshold_select.currentIndexChanged.connect(lambda index:self.hide_(self.threshold_input,index==0))
        thresholdWeightlayout=QVBoxLayout()
        thresholdWeightlayout.addWidget(self.threshold_select)
        thresholdWeightlayout.addWidget(self.threshold_input)

        thresholdWeight.setLayout(thresholdWeightlayout)
        thresholdlayout.addWidget(thresholdButton)
        thresholdlayout.addWidget(thresholdWeight)

        #确认
        oklayout=QHBoxLayout()
        self.ok_button = QPushButton('确定', self)
        self.ok_button.setIcon(IconInstance().OK)
        self.ok_button.clicked.connect(self.accept)

        self.cancel_button = QPushButton('取消', self)
        self.cancel_button.setIcon(IconInstance().CANCEL)
        self.cancel_button.clicked.connect(self.reject)
        oklayout.addWidget(self.ok_button,0,alignment=Qt.AlignHCenter)
        oklayout.addWidget(self.cancel_button,0,alignment=Qt.AlignHCenter)


        totalvlayout=QVBoxLayout()
        totalvlayout.addLayout(layerbox)
        totalvlayout.addLayout(filterlayout)
        totalvlayout.addLayout(changelayout)
        totalvlayout.addLayout(thresholdlayout)
        totalvlayout.addLayout(oklayout)
        totalvlayout.addStretch()
        
        self.setLayout(totalvlayout)


        filerButton.clicked.connect(lambda: self.hide(filerButton,filterWeight))
        changeButton.clicked.connect(lambda: self.hide(changeButton,changeWeight))
        thresholdButton.clicked.connect(lambda: self.hide(thresholdButton,thresholdWeight))



    def hide(self,button:LockerButton,weight:QWidget):
        if ((button.hide_)%2)==1:
            weight.setVisible(False)
            button.SetImageLabel(QPixmap('plugins/In_one/pic/箭头_列表向右.png'))
        else:
            weight.setVisible(True)
            button.SetImageLabel(QPixmap('plugins/In_one/pic/箭头_列表展开.png'))
        button.hide_=(button.hide_)%2+1
    def hide_(self,widget:QWidget,h:bool):
        if h:
            widget.setVisible(True)
        else:
            widget.setVisible(False)
    def hideWidget(self,widget:QWidget):
        if widget.isVisible:
            widget.setVisible(False)
        else:
            widget.setVisible(True)
    @property
    def param(self):
        class param(object):
            pass
        p=param()
        p.pre=self.pre
        p.cd=self.cd
        p.threshold=self.threshold
        p.layer_combox=self.layer_combox
        p.pre_select=self.pre_select
        p.x_size_input=self.x_size_input
        p.y_size_input=self.y_size_input
        p.threshold_select=self.threshold_select
        p.threshold_input=self.threshold_input
        p.cd_select=self.cd_select
        return p
class InOnePlugin(BasicPlugin):
    pre={"均值滤波":Meanfilter}#可添加其他方法
    cd={'差分法':basic_cd}#可添加其他方法
    threshold={'OTSU阈值':otsu}#可添加其他方法


    @staticmethod
    def info():
        return {
            'name': 'AllinOne',
            'description': 'AllinOne',
            'author': 'RSCDER',
            'version': '1.0.0',
        }

    def set_action(self):

        basic_diff_method_in_one = QAction('差分法')
        # ActionManager().change_detection_menu.addAction(basic_diff_method_in_one)
        ActionManager().unsupervised_menu.addAction(basic_diff_method_in_one)
        self.basic_diff_method_in_one = basic_diff_method_in_one
        basic_diff_method_in_one.triggered.connect(self.run) 


    def run(self):
        myDialog=AllInOne(list(self.pre.keys()),list(self.cd.keys()),list(self.threshold.keys()),self.mainwindow)
        myDialog.show()
        if myDialog.exec_()==QDialog.Accepted:
            w=myDialog.param
            t=Thread(target=self.run_alg,args=(w,))
            t.start()

    def run_alg(self,w:AllInOne):
        dict={}
        layer1=w.layer_combox.layer1
        
        
        pth1 = w.layer_combox.layer1.path
        pth2 = w.layer_combox.layer2.path
        name=layer1.layer_parent.name
    # 预处理
        # 若添加的预处理函数接口相同，则无需判断是哪种方法
        # if w.pre_select.choose==self.pre.keys()[0]:
        #     pass
        # el
        preKey=w.pre_select.choose
        pth1=self.pre[preKey](w.x_size_input.text(),w.y_size_input.text(),w.layer_combox.layer1)
        self.send_message.emit('{}图像{}'.format(preKey,w.layer_combox.layer1.name))
        pth2=self.pre[preKey](w.x_size_input.text(),w.y_size_input.text(),w.layer_combox.layer2)
        self.send_message.emit('{}图像{}'.format(preKey,w.layer_combox.layer2.name))
        name=name+'_mean_filter'
        dict['预处理']=[preKey,'|'.format(pth1,pth2)]


        cdpth=None
    #变化检测
        # if w.cd_select.choose==self.cd[0]:
        cdKey=w.cd_select.choose
        cdpth=self.cd[cdKey](pth1,pth2,w.layer_combox.layer1.layer_parent,self.send_message)
        name += '_basic_cd'
        dict['变化检测算法']=[cdKey,cdpth]

    #阈值处理
    #例如手动阈值和otsu参数不同，则要做区分
        thpth=None
        if w.threshold_select.choose=='手动阈值':
            thpth=thresh(cdpth,float(w.threshold_input.text()),w.layer_combox.layer1.layer_parent.name,self.send_message)
            dict['后处理']=['手动阈值',[float(w.threshold_input.text())],thpth]
        else:
            thpth,gap=otsu(cdpth,w.layer_combox.layer1.layer_parent.name,self.send_message)
            name+='_otsu'
            dict['后处理']=['OTSU阈值',gap,cdpth]

        table_layer(thpth,layer1,name,self.send_message,dict)

