from operator import itemgetter

from osgeo import ogr, osr

from rosreestrXml.ui.tools.KPT.egrn_tool.EgrnInfo import InfoEgr
from rosreestrXml.ui.tools.KPT.egrn_tool.egrn import parseString
from rosreestrXml.ui.tools.KPT.kptTools.DbKptDirectory import KptСatalog


class EgrnObjectPoints:
    __name='точки_земельного_участка'
    __nameProcess='Импорт точек земельного участка'
    def __init__(self,xmlTagObject_part,cadNumber,properties=None):
        self.__baseXmlTag = xmlTagObject_part
        self.__cadNumber = cadNumber
        self.__properties = properties
        self.__points=None
        self.__prepare()
    def __prepare(self):
        self.__points=[]
        for pnt in self.__baseXmlTag:
            x_xml=pnt.x
            y_xml=pnt.y
            ord_nmb=pnt.ord_nmb
            point_inf={}
            try:
                if self.__properties['reverseXY']:
                    point_inf['x']=float(y_xml)
                    point_inf['y']=float(x_xml)
                else:
                    point_inf['y'] = float(y_xml)
                    point_inf['x'] = float(x_xml)
                point_inf['ord_nmb']=ord_nmb
                self.__points.append(point_inf)
            except :
                err=0
        return
    def getLinearRing(self):
        geo=ogr.Geometry(ogr.wkbLinearRing)
        for pnt in self.__points:
            geo.AddPoint(pnt['x'],pnt['y'])
        return geo
    @property
    def Points(self):
        return self.__points
class PoinsPartForSave:
    __name = 'точки_земельного_участка'
    __nameProcess = 'Импорт точек земельного участка'
    def __init__(self,points,properties):
        self.__pointObjects=points
        self.__properies=properties
        self.__cs_wkt = self.__properies['cs']
        self.__str_style_mi = self.__properies['points_parts']['style']
    @property
    def NameLayer(self):
        return self.__name

    @property
    def NameProcess(self):
        return self.__nameProcess
    def CreateLayer(self, ds, createNewLayer=True):

        layer = None
        if not createNewLayer:
            layer = ds.GetLayer(self.__name)
            if layer is not None:
                return layer
        srs = osr.SpatialReference()
        srs.ImportFromWkt(self.__cs_wkt)
        layer = ds.CreateLayer(self.__name, srs, geom_type=ogr.wkbPoint)
        for fld in PointsContourForSave.struct():
            # create fields
            field_cur = ogr.FieldDefn(fld['name'], fld['type'])
            type_field = fld['type']
            if fld['type'] == ogr.OFTString:
                field_cur.SetWidth(fld['width'])
            layer.CreateField(field_cur)
        return layer
    def Write(self, layer, dbDictionary: KptСatalog, dbDocuments):
        if self.__pointObjects is None:
            return
        if isinstance(self.__pointObjects,list):
            for pnt_obj in self.__pointObjects:


                if not isinstance(pnt_obj,list):
                    points = pnt_obj.Points
                    for pnt in points:
                        self.__PointWrite(layer,pnt)
                else:
                    ()
            return

        for pnt in self.__pointObjects.Points:
            self.__PointWrite(layer, pnt)
    def __PointWrite(self, layer,pnt):
        #geometry=self.__createGeometry()
        att=[]
        #att.append({'name': 'кадастровый_номер', 'value': self.__cadNumber})
        att.append({'name': 'x', 'value': pnt['x']})
        att.append({'name': 'y', 'value': pnt['y']})
        att.append({'name':'ord_nmb', 'value': pnt['ord_nmb']})
        att.append({'name': 'MI_STYLE', 'value':self.__str_style_mi})
        feature = ogr.Feature(layer.GetLayerDefn())
        for atr_item in att:
            # print(atr_item['name'])
            # print(atr_item['value'])
            try:
                feature.SetField(atr_item['name'], atr_item['value'])
            except Exception as ex:
                jkl=0
        geometry=ogr.Geometry(ogr.wkbPoint)
        geometry.AddPoint(pnt['x'],pnt['y'])
        if geometry is not None:
            feature.SetGeometry(geometry)

        layer.CreateFeature(feature)
        geometry = None
        feature = None
        return
class PointsContourForSave:
    __name = 'точки_земельного_участка'
    __nameProcess = 'Импорт точек земельного участка'
    def __init__(self,points,properties,cad_number):
        self.__pointObjects=points
        self.__properies=properties
        self.__cadNumber=cad_number
        self.__cs_wkt = self.__properies['cs']
        self.__str_style_mi=self.__properies['contours_points']['style']

    @property
    def NameLayer(self):
        return self.__name

    @property
    def NameProcess(self):
        return self.__nameProcess

    @staticmethod
    def struct():
        struct = []
        struct.append({'name': 'кадастровый_номер', 'type': ogr.OFTString, 'width': 22})
        struct.append({'name': 'x', 'type': ogr.OFTReal})
        struct.append({'name': 'y', 'type': ogr.OFTReal})
        struct.append({'name': 'ord_nmb', 'type': ogr.OFTInteger})
        struct.append({'name': 'MI_STYLE', 'type': ogr.OFTString, 'width': 128})
        return struct

    def CreateLayer(self, ds, createNewLayer=True):

        layer = None
        if not createNewLayer:
            layer = ds.GetLayer(self.__name)
            if layer is not None:
                return layer
        srs = osr.SpatialReference()
        srs.ImportFromWkt(self.__cs_wkt)
        layer = ds.CreateLayer(self.__name, srs, geom_type=ogr.wkbPoint)
        for fld in PointsContourForSave.struct():
            # create fields
            field_cur = ogr.FieldDefn(fld['name'], fld['type'])
            type_field = fld['type']
            if fld['type'] == ogr.OFTString:
                field_cur.SetWidth(fld['width'])
            layer.CreateField(field_cur)
        return layer
    def Write(self, layer, dbDictionary: KptСatalog, dbDocuments):
        if self.__pointObjects is None:
            return
        if isinstance(self.__pointObjects,list):
            for pnt_obj in self.__pointObjects:
                for pnt in pnt_obj.Points:
                    self.__PointWrite(layer,pnt)
            return

        for pnt in self.__pointObjects.Points:
            self.__PointWrite(layer, pnt)
    def __PointWrite(self, layer,pnt):
        #geometry=self.__createGeometry()
        att=[]
        att.append({'name': 'кадастровый_номер', 'value': self.__cadNumber})
        att.append({'name': 'x', 'value': pnt['x']})
        att.append({'name': 'y', 'value': pnt['y']})
        att.append({'name':'ord_nmb', 'value': pnt['ord_nmb']})
        att.append({'name': 'MI_STYLE', 'value':self.__str_style_mi})
        feature = ogr.Feature(layer.GetLayerDefn())
        for atr_item in att:
            # print(atr_item['name'])
            # print(atr_item['value'])
            try:
                feature.SetField(atr_item['name'], atr_item['value'])
            except Exception as ex:
                jkl=0
        geometry=ogr.Geometry(ogr.wkbPoint)
        geometry.AddPoint(pnt['x'],pnt['y'])
        if geometry is not None:
            feature.SetGeometry(geometry)

        layer.CreateFeature(feature)
        geometry = None
        feature = None
        return
class EgrnPartContour:
    def __init__(self,xmlTag,cadNumber,properties=None):
        self.__baseXmlTag = xmlTag
        self.__cadNumber = cadNumber
        self.__properties = properties
        self.__objPoints=None
        self.__preparePoints()
    def __preparePoints(self):

        obj_spatial=self.__baseXmlTag.entity_spatial.spatials_elements.spatial_element
        if len(obj_spatial)>1:
            self.__objPoints=[]
            for obj_points in obj_spatial:
                self.__objPoints.append(EgrnObjectPoints(obj_points.ordinates.ordinate,self.__cadNumber,self.__properties))
            return
        if self.__properties is None:
            self.__objPoints = EgrnObjectPoints(obj_spatial[0].ordinates.ordinate, self.__cadNumber, {'reverseXY':False})
        else:
            self.__objPoints=EgrnObjectPoints(obj_spatial[0].ordinates.ordinate,self.__cadNumber,self.__properties)
    @property
    def Points(self):
        return self.__objPoints
    def build(self):
        self.__preparePoints()
class EgrnObjectPart:
    __name='части_земельных_участков'
    __nameProcess='Импорт частей земельного участка'
    def __init__(self,xmlTagObject_part,cadNumber,properties=None):
        self.__baseXmlTag = xmlTagObject_part
        self.__cadNumber = cadNumber
        self.__properties = properties
        self.__cs_wkt=properties['cs']
        self.__contours=None
        self.__prepareContours()

    def __prepareContours(self):
        obj_contours=self.__baseXmlTag.contours.contour
        self.__contours=[]
        for contour in obj_contours:
            obj_contour=EgrnPartContour(contour,self.__cadNumber,self.__properties)
            self.__contours.append(obj_contour)
        self.__number_part=self.__baseXmlTag.part_number
    def getPoints(self):
        obj_points=[]
        for contour in self.__contours:
            obj_points.append(contour.Points)
        return obj_points

    @property
    def NameLayer(self):
        return self.__name

    @property
    def NameProcess(self):
        return self.__nameProcess
    @staticmethod
    def struct():
        struct = []
        struct.append({'name': 'кадастровый_номер', 'type': ogr.OFTString, 'width': 22})
        struct.append({'name': 'номер_участка', 'type': ogr.OFTInteger})
        struct.append({'name': 'MI_STYLE', 'type': ogr.OFTString, 'width': 128})
        return struct
    def __build(self):
        pass

    def CreateLayer(self, ds, createNewLayer=True):

        layer = None
        if not createNewLayer:
            layer = ds.GetLayer(self.__name)
            if layer is not None:
                return layer
        srs = osr.SpatialReference()
        srs.ImportFromWkt(self.__cs_wkt)
        layer = ds.CreateLayer(self.__name, srs, geom_type=ogr.wkbUnknown)
        for fld in EgrnObjectPart.struct():
            # create fields
            field_cur = ogr.FieldDefn(fld['name'], fld['type'])
            type_field = fld['type']
            if fld['type'] == ogr.OFTString:
                field_cur.SetWidth(fld['width'])
            layer.CreateField(field_cur)
        return layer

    def Write(self, layer, dbDictionary: KptСatalog, dbDocuments):
        geometry=self.__createGeometry()
        att=[]
        att.append({'name': 'кадастровый_номер', 'value': self.__cadNumber})
        att.append({'name': 'номер_участка', 'value': self.__number_part})
        att.append({'name': 'MI_STYLE', 'value':self.__properties['parts']['style']})
        feature = ogr.Feature(layer.GetLayerDefn())
        for atr_item in att:
            # print(atr_item['name'])
            # print(atr_item['value'])
            try:
                feature.SetField(atr_item['name'], atr_item['value'])
            except Exception as ex:
                jkl=0
        if geometry is not None:
            feature.SetGeometry(geometry)

        layer.CreateFeature(feature)
        geoObj = None
        feature = None
        return

    def __createGeometry(self):
        if self.__contours is None or len(self.__contours)==0:
            return None
        type_geometry=ogr.wkbPolygon
        if len(self.__contours)>1:
            type_geometry=ogr.wkbMultiPolygon
        geometry=ogr.Geometry(type_geometry)
        for contour in self.__contours:

            geo_contour=self.__geometryFromPoinsObj(contour.Points)
            if type_geometry==ogr.wkbPolygon:
                geometry=None
                return geo_contour
            if geo_contour is not None:
                geometry.AddGeometry(geo_contour)
            else:
                print("Error geometry is None")
            geo_contour=None
        return geometry
    def __geometryFromPoinsObj(self,pointsObj):
        if not isinstance(pointsObj,list):
            geometry = ogr.Geometry(ogr.wkbPolygon)
            geo_lineRing = pointsObj.getLinearRing()

            geometry.AddGeometry(geo_lineRing)
            geo_lineRing = None
            return geometry
        type_geometry=ogr.wkbPolygon
        list_polygons = []
        for poly_points in pointsObj:
            geo_poly = ogr.Geometry(type_geometry)
            geo_lineRing = poly_points.getLinearRing()
            if geo_lineRing is None:
                continue
            geo_poly.AddGeometry(geo_lineRing)
            # geo_lineRing = None
            area = geo_poly.GetArea()
            list_polygons.append({"area": area, "geo_poly": geo_poly, 'line_ring': geo_lineRing})
        if len(list_polygons) == 0:
            return None
        if len(list_polygons) == 1:
            list_polygons[0]['line_ring'] = None
            return list_polygons[0]['geo_poly']
        sorted_list_poly = sorted(list_polygons, key=itemgetter('area'), reverse=True)
        geometry = ogr.Geometry(type_geometry)
        count_poly = len(sorted_list_poly)
        # base_polygon=sorted_list_poly[0]['geo_poly']
        geometry.AddGeometry(sorted_list_poly[0]['line_ring'])
        if geometry is None:
            jkl=0
        for i in range(1, count_poly):
            # for poly in sorted_list_poly:
            poly = sorted_list_poly[i]
            if geometry is None:
                jkl=0
                continue
            isHole = geometry.Contains(poly['geo_poly'])
            if not isHole:
                isHole = geometry.Intersect(poly['geo_poly'])
            if isHole:
                if geometry.GetGeometryName() == "POLYGON":
                    geometry.AddGeometry(poly['line_ring'])
                else:
                    geometry_new = geometry.Difference(poly['geo_poly'])
                    geometry=None
                    geometry = geometry_new
            else:
                if geometry.GetGeometryName() == "POLYGON":
                    new_geometry = ogr.Geometry(ogr.wkbMultiPolygon)
                    new_geometry.AddGeometry(geometry)
                    new_geometry.AddGeometry(poly['geo_poly'])
                    geometry = None
                    geometry = new_geometry.Clone()
                    new_geometry=None

            '''
            geometry.AddGeometry(poly['line_ring'])
            poly['line_ring']=None
            poly['geo_poly']=None
            '''
        return geometry


class EgrnObject_parts:
    def __init__(self,xmlTagObject_parts,cadNumber,properties=None):
        self.__baseXmlTag=xmlTagObject_parts
        if xmlTagObject_parts is None:
            self.__valid=False
            return
        self.__valid = True
        self.__cadNumber=cadNumber
        self.__properties=properties
        self.__prepareParts()
    @property
    def IsValid(self):
        return self.__valid
    def __prepareParts(self):
        self.__object_parts=[]
        obj_parts=self.__baseXmlTag.object_part
        for part in obj_parts:
            self.__object_parts.append(EgrnObjectPart(part,self.__cadNumber,self.__properties))
        return
    @property
    def PartObjects(self):
        return self.__object_parts
class EgrnContourLocation:
    __name='границы_земельных_участков'
    __nameProcess='Импорт границ земельного участка'

    def __init__(self,xmlTag,cadNumber,properies):
        self.__xmlTag=xmlTag
        self.__properties=properies
        self.__cadNumberParent=cadNumber
        self.__cadNumber=None
        self.__objPoints=None
        self.__number_pp=None
        self.__cs_wkt = self.__properties['cs']
        self.__prepare()
    def __prepare(self):
        self.__cadNumber=self.__xmlTag.cad_number
        if self.__cadNumber is None:
            self.__cadNumber=self.__cadNumberParent
        self.__number_pp=self.__xmlTag.number_pp
        obj_spatial=None
        try:
            obj_spatial = self.__xmlTag.entity_spatial.spatials_elements.spatial_element
        except:
            obj_spatial = None
            self.__objPoints=None
        if obj_spatial is None:
            return
        if len(obj_spatial) > 1:
            self.__objPoints = []
            for obj_points in obj_spatial:
                self.__objPoints.append(
                    EgrnObjectPoints(obj_points.ordinates.ordinate, self.__cadNumber, self.__properties))
            return
        if self.__properties is None:
            self.__objPoints = EgrnObjectPoints(obj_spatial[0].ordinates.ordinate, self.__cadNumber,
                                                {'reverseXY': False})
        else:
            self.__objPoints = EgrnObjectPoints(obj_spatial[0].ordinates.ordinate, self.__cadNumber, self.__properties)
    @property
    def CadNumber(self):
        return self.__cadNumber
    @property
    def Points(self):
        return self.__objPoints
    @property
    def NameLayer(self):
        return self.__name

    @property
    def NameProcess(self):
        return self.__nameProcess

    @staticmethod
    def struct():
        struct = []
        struct.append({'name': 'кадастровый_номер', 'type': ogr.OFTString, 'width': 22})
        struct.append({'name': 'номер_границы', 'type': ogr.OFTInteger})
        struct.append({'name': 'MI_STYLE', 'type': ogr.OFTString, 'width': 128})
        return struct
    def CreateLayer(self, ds, createNewLayer=True):

        layer = None
        if not createNewLayer:
            layer = ds.GetLayer(self.__name)
            if layer is not None:
                return layer
        srs = osr.SpatialReference()
        srs.ImportFromWkt(self.__cs_wkt)
        layer = ds.CreateLayer(self.__name, srs, geom_type=ogr.wkbUnknown)
        for fld in EgrnContourLocation.struct():
            # create fields
            field_cur = ogr.FieldDefn(fld['name'], fld['type'])
            type_field = fld['type']
            if fld['type'] == ogr.OFTString:
                field_cur.SetWidth(fld['width'])
            layer.CreateField(field_cur)
        return layer
    def Write(self, layer, dbDictionary: KptСatalog, dbDocuments):
        geometry=self.__createGeometry()
        att=[]
        att.append({'name': 'кадастровый_номер', 'value': self.__cadNumber})
        att.append({'name': 'номер_границы', 'value': self.__number_pp})
        att.append({'name': 'MI_STYLE', 'value':self.__properties['contours']['style']})
        feature = ogr.Feature(layer.GetLayerDefn())
        for atr_item in att:
            # print(atr_item['name'])
            # print(atr_item['value'])
            try:
                feature.SetField(atr_item['name'], atr_item['value'])
            except Exception as ex:
                jkl=0
        if geometry is not None:
            feature.SetGeometry(geometry)

        layer.CreateFeature(feature)
        geoObj = None
        feature = None
        return
    def __createGeometry(self):
        if self.__objPoints is None :
            return None
        type_geometry = ogr.wkbPolygon
        if isinstance(self.__objPoints, list):

            list_polygons=[]
            for poly_points in self.__objPoints:
                geo_poly = ogr.Geometry(type_geometry)
                geo_lineRing = poly_points.getLinearRing()
                if geo_lineRing is None:
                    continue
                geo_poly.AddGeometry(geo_lineRing)
                #geo_lineRing = None
                area=geo_poly.GetArea()
                list_polygons.append({"area":area,"geo_poly":geo_poly,'line_ring':geo_lineRing})
            if len(list_polygons)==0:
                return None
            if len(list_polygons)==1:
                list_polygons[0]['line_ring']=None
                return list_polygons[0]['geo_poly']
            sorted_list_poly= sorted(list_polygons, key=itemgetter('area'), reverse=True)
            geometry=ogr.Geometry(type_geometry)
            count_poly=len(sorted_list_poly)
            #base_polygon=sorted_list_poly[0]['geo_poly']
            geometry.AddGeometry(sorted_list_poly[0]['line_ring'])

            for i in range(1,count_poly):
            #for poly in sorted_list_poly:
                poly=sorted_list_poly[i]


                isHole=geometry.Contains( poly['geo_poly'])
                if not isHole:
                    isHole=geometry.Intersect(poly['geo_poly'])
                if isHole:
                    if geometry.GetGeometryName() == "POLYGON":
                        geometry.AddGeometry(poly['line_ring'])
                    else:
                        geometry_new=geometry.Difference(poly['geo_poly'])
                        geometry=new_geometry
                else:
                    if geometry.GetGeometryName()=="POLYGON":
                        new_geometry=ogr.Geometry(ogr.wkbMultiPolygon)
                        new_geometry.AddGeometry(geometry)
                        new_geometry.AddGeometry(poly['geo_poly'])
                        geometry=None
                        geometry=new_geometry

                '''
                geometry.AddGeometry(poly['line_ring'])
                poly['line_ring']=None
                poly['geo_poly']=None
                '''
            return geometry


        else:
            geo_poly = ogr.Geometry(type_geometry)
            geo_lineRing = self.__objPoints.getLinearRing()
            geo_poly.AddGeometry(geo_lineRing)
            geo_lineRing=None
            return geo_poly
        '''    
        if isinstance(self.__objPoints,list):
            #Полигон с дырками
            geometry = ogr.Geometry(type_geometry)
            for poly in self.__objPoints:
                
            return geometry
        '''

        '''
        #geometry=ogr.Geometry(type_geometry)
        geo_lineRing=self.__objPoints.getLinearRing()
        geo_polygon= ogr.Geometry(ogr.wkbPolygon)
        geo_polygon.AddGeometry(geo_lineRing)
        #geometry=None
        return geo_polygon
        '''
class EgrnContours_location:
    def __init__(self,xmlTag,cadNumber,properies):
        self.__xmlTag=xmlTag
        self.__properties=properies
        self.__cadNumber=cadNumber
        self.__contours=None
        self.__prepare()
    def __prepare(self):
        self.__contours=[]
        xml_contour_tag=None
        try:
            xml_contour_tag=self.__xmlTag.contours.contour
        except:
            print("Сontour tag not find")
            xml_contour_tag=None
        if xml_contour_tag is None:
            return
        for contour in self.__xmlTag.contours.contour:
            self.__contours.append(EgrnContourLocation(contour,self.__cadNumber,self.__properties))
        return
    @property
    def Contours(self):
        if len(self.__contours)==0:
            return None
        return self.__contours
def FactoryEgrnObj(objEgrn,properties=None):
    if objEgrn is None:
        return None
    str_cadNumber=''
    try:
        str_cadNumber=objEgrn.land_record.object.common_data.cad_number
    except:
        pass
    contours=None
    if properties['contours']['enable'] or properties['contours_points']['enable']:
        contours=EgrnContours_location(objEgrn.land_record.contours_location,str_cadNumber,properties)
    objEgrnObject_parts=EgrnObject_parts(objEgrn.land_record.object_parts,str_cadNumber,properties)
    list_saveObj=[]
    if objEgrnObject_parts is None:
        return None
    all_parts=objEgrnObject_parts.PartObjects
    if properties['points_parts']['enable']:
        for part in all_parts:
            points_object=part.getPoints()
            if points_object is None:
                continue
            list_saveObj.append(PoinsPartForSave(points_object,properties))
    if properties['parts']['enable']:
        list_saveObj.extend(all_parts)
    if properties['contours']['enable'] and contours is not None:
        contours_save=contours.Contours
        if contours_save is not None:
            list_saveObj.extend(contours_save)
    if properties['contours_points']['enable'] and contours is not None:
        for contour in contours.Contours:
            list_saveObj.append(PointsContourForSave(contour.Points, properties,contour.CadNumber))
    return list_saveObj
def FactoryEgrnFromStringObj(source_xml,properties=None):
    objEgrnObject_parts=None
    objEgrn=parseString(source_xml,True)
    if objEgrn is None:
        return None
    str_cadNumber = ''
    try:
        str_cadNumber = objEgrn.land_record.object.common_data.cad_number
    except:
        pass
    egrnInfo=InfoEgr(objEgrn.land_record)
    contours = None
    if properties['contours']['enable'] or properties['contours_points']['enable']:
        contours = EgrnContours_location(objEgrn.land_record.contours_location, str_cadNumber, properties)
    if properties['parts']['enable'] or properties['points_parts']['enable']:
        objEgrnObject_parts = EgrnObject_parts(objEgrn.land_record.object_parts, str_cadNumber, properties)
        if not objEgrnObject_parts.IsValid:
            objEgrnObject_parts= None
    list_saveObj = []
    '''
    if objEgrnObject_parts is None:
        return None
    '''
    if objEgrnObject_parts is not None:
        all_parts = objEgrnObject_parts.PartObjects
        if properties['points_parts']['enable']:
            for part in all_parts:
                points_object = part.getPoints()
                if points_object is None:
                    continue
                list_saveObj.append(PoinsPartForSave(points_object, properties))
        if properties['parts']['enable']:
            list_saveObj.extend(all_parts)

    if properties['contours']['enable'] and contours is not None:
        contours_save = contours.Contours
        if contours_save is not None:
            list_saveObj.extend(contours_save)
    if properties['contours_points']['enable'] and contours is not None:
        if contours.Contours is not  None:

            for contour in contours.Contours:
                list_saveObj.append(PointsContourForSave(contour.Points, properties, contour.CadNumber))
    if len(list_saveObj)>0:
        list_saveObj.append(egrnInfo)
    return list_saveObj
