import traceback

from osgeo import ogr, osr

def getInfoSpatialLayer(layer):
    name_geometry_col = layer.GetGeometryColumn()
    name_layer = layer.GetName()
    ext_layer = layer.GetExtent()
    return name_layer,name_geometry_col,ext_layer
class BaseData:
    def __init__(self,xmlTagBaseData,cad_number,property):
        self.__xmlObj=xmlTagBaseData
        self.__cadNumber=cad_number
        self.__property=property
    @property
    def ReverseXY(self):
        return self.__property['reverseXY']
    @staticmethod
    def struct():
        struct = []

        struct.append({'name': 'КадНомер', 'type': ogr.OFTString, 'width': 50})
        struct.append({'name': 'тип', 'type': ogr.OFTString, 'width': 250})
        struct.append({'name': 'код_типа', 'type': ogr.OFTString, 'width':14})
        return struct
    @staticmethod
    def struct_coast():
        return {'name': 'стоимость', 'type': ogr.OFTReal}
    def getValues(self):
        att = []
        att.append({'name': 'КадНомер', 'value': self.__xmlObj.object.common_data.cad_number})
        att.append({'name': 'тип', 'value': self.__xmlObj.object.common_data.type_.value})
        att.append({'name': 'код_типа', 'value': self.__xmlObj.object.common_data.type_.code})
        if self.__xmlObj.cost is not None:
            att.append({'name': 'стоимость', 'value': float(self.__xmlObj.cost.value)})

        return att
    def __createContourGeometry(self,spatial_elements,reverseXY,closeContour=True):
        if spatial_elements is None:
            return None
        xml_polygons = spatial_elements.spatial_element
        # .ordinates
        polygons = []
        linesRings = []
        geoObj=None
        for xml_poly in xml_polygons:
            geo_poly = ogr.Geometry(ogr.wkbPolygon)
            lineRing = ogr.Geometry(ogr.wkbLinearRing)
            list_points=[]
            for xmlPnt in xml_poly.ordinates.ordinate:
                if reverseXY:
                    y = xmlPnt.x
                    x = xmlPnt.y
                else:
                    x = xmlPnt.x
                    y = xmlPnt.y
                list_points.append((float(x),float(y)))
            if closeContour:
                if not (list_points[0]==list_points[-1]):
                    list_points.append(list_points[0])
            '''
            for xmlPnt in xml_poly.ordinates.ordinate:
                x = xmlPnt.x
                y = xmlPnt.y
                lineRing.AddPoint(float(x), float(y))
            '''
            if not (list_points[0] == list_points[-1]):
                ''' не полигон а полилиния'''
                geo_poly=None
                lineRing=None
                lineRing=ogr.Geometry(ogr.wkbLineString)
                for pnt in list_points:
                    lineRing.AddPoint(pnt[0], pnt[1])
                polygons.append(lineRing)
            else:
                for pnt in list_points:
                    lineRing.AddPoint(pnt[0], pnt[1])
                geo_poly.AddGeometry(lineRing)
                linesRings.append(lineRing)
                polygons.append(geo_poly)
        if len(polygons) == 1:

            return polygons[0]
        # self.__geoObj= ogr.Geometry(ogr.wkbMultiPolygon)
        geoObj = ogr.Geometry(ogr.wkbPolygon)
        for i,poly in enumerate(linesRings):
            if i == 0:
                geoObj.AddGeometry(poly)
                continue
            if geoObj is None:
                continue
            if geoObj.Contains(polygons[i]):
                geoObj.AddGeometry(poly)
                continue
            geoObj=geoObj.Union(polygons[i])
        if geoObj is None:
            return None
        return geoObj

    def GeometryContour(self,xmlTagContours,reverseXYZ=False):
        #xmlTagContours=self.__xmlObj.contours
        list_spatial_contour = []
        reverseXY=self.ReverseXY

        try:
            xmlTagContours = xmlTagContours.contours
            xmlTagContour=xmlTagContours.contour
        except:
            return None
        for contour in xmlTagContour:
            if contour.entity_spatial is None:
                continue
            try:
                geo_contour=self.__createContourGeometry(contour.entity_spatial.spatials_elements,reverseXY,False)
                if geo_contour is not None:
                    list_spatial_contour.append(geo_contour)
            except Exception as ex:
                print(ex)
                traceback.print_exc()
        if len(list_spatial_contour)==1:
            return list_spatial_contour[0]
        return

class Building(BaseData):
    def __init__(self,xmlTagBaseData,cad_number,property):
        super().__init__(xmlTagBaseData,cad_number,property)
        self.__xmlObj = xmlTagBaseData
        self.__cadNumber = cad_number
        self.__name='здания'
    @property
    def name_layer(self):
        return self.__name
    @staticmethod
    def struct():
        struct = []
        struct.extend(BaseData.struct())
        struct.append({'name': 'площадь', 'type': ogr.OFTReal})
        struct.append({'name': 'назначение', 'type':ogr.OFTString, 'width':250})
        struct.append({'name': 'код_назначения', 'type': ogr.OFTString, 'width':14})
        struct.append(BaseData.struct_coast())
        struct.append({'name': 'геометрия_существует', 'type': ogr.OFTInteger})
        return struct
    def Write(self,layer,mi_style,p2=None):
        att = []
        att.extend(super().getValues())
        att.append({'name': 'площадь', 'value': float(self.__xmlObj.params.area)})
        att.append({'name': 'назначение', 'value': self.__xmlObj.params.purpose.value})
        att.append({'name': 'код_назначения', 'value': self.__xmlObj.params.purpose.code})
        att.append({'name': 'MI_STYLE', 'value': mi_style})
        feature = ogr.Feature(layer.GetLayerDefn())

        for atr_item in att:
            # print(atr_item['name'])
            # print(atr_item['value'])
            feature.SetField(atr_item['name'], atr_item['value'])

        geometry=super().GeometryContour(self.__xmlObj)
        exist_geometry=0
        if geometry is not None:
            exist_geometry = 1
        feature.SetField('геометрия_существует', exist_geometry)
        if geometry is not None:
            feature.SetGeometry(geometry)

        layer.CreateFeature(feature)
        geoObj = None
        feature = None
    def getStruct(self):
        return Building.struct()
class Land(BaseData):
    def __init__(self,xmlTagBaseData,cad_number,property):
        super().__init__(xmlTagBaseData,cad_number,property)
        self.__xmlObj = xmlTagBaseData
        self.__cadNumber = cad_number
        self.__name='земельные_участки'
    @property
    def name_layer(self):
        return self.__name

    @staticmethod
    def struct():
        struct = []
        struct.extend(BaseData.struct())
        #struct.append({'name': 'площадь', 'type': ogr.OFTReal})
        struct.append({'name': 'вид', 'type': ogr.OFTString, 'width': 250})
        struct.append({'name': 'код', 'type': ogr.OFTInteger})
        struct.append({'name': 'категория', 'type': ogr.OFTString, 'width': 250})
        struct.append({'name': 'код_категории', 'type':ogr.OFTString, 'width': 16})
        struct.append(BaseData.struct_coast())
        struct.append({'name': 'геометрия_существует', 'type': ogr.OFTInteger})
        return struct
    def getStruct(self):
        return Land.struct()

    def Write(self, layer, mi_style, p2=None):
        att = []
        att.extend(super().getValues())
        #att.append({'name': 'площадь', 'value': float(self.__xmlObj.params.area)})

        att.append({'name': 'вид', 'value': self.__xmlObj.object.subtype.value})
        att.append({'name': 'код', 'value': self.__xmlObj.object.subtype.code})
        att.append({'name': 'категория', 'value': self.__xmlObj.params.category.type_.value})
        att.append({'name': 'код_категории', 'value': self.__xmlObj.params.category.type_.code})
        att.append({'name': 'MI_STYLE', 'value': mi_style})
        feature = ogr.Feature(layer.GetLayerDefn())

        for atr_item in att:
            # print(atr_item['name'])
            # print(atr_item['value'])
            feature.SetField(atr_item['name'], atr_item['value'])

        geometry = super().GeometryContour(self.__xmlObj.contours_location)
        exist_geometry = 0
        if geometry is not None:
            exist_geometry = 1
        feature.SetField('геометрия_существует', exist_geometry)
        if geometry is not None:
            feature.SetGeometry(geometry)

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

class Construction(BaseData):
    def __init__(self,xmlTagBaseData,cad_number,property):
        super().__init__(xmlTagBaseData,cad_number,property)
        self.__xmlObj = xmlTagBaseData
        self.__cadNumber = cad_number
        self.__name='сооружения'
    @property
    def name_layer(self):
        return self.__name

    @staticmethod
    def struct():
        struct = []
        struct.extend(BaseData.struct())
        struct.append(BaseData.struct_coast())
        struct.append({'name': 'геометрия_существует', 'type': ogr.OFTInteger})
        return struct
    def getStruct(self):

        return Construction.struct()
    def Write(self, layer, mi_style, p2=None):
        att = []
        att.extend(super().getValues())

        att.append({'name': 'MI_STYLE', 'value': mi_style})
        feature = ogr.Feature(layer.GetLayerDefn())

        for atr_item in att:
            # print(atr_item['name'])
            # print(atr_item['value'])
            feature.SetField(atr_item['name'], atr_item['value'])

        geometry = super().GeometryContour(self.__xmlObj)
        exist_geometry = 0
        if geometry is not None:
            exist_geometry = 1
        feature.SetField('геометрия_существует', exist_geometry)
        if geometry is not None:
            isValid=geometry.IsValid()
            feature.SetGeometry(geometry)

        layer.CreateFeature(feature)
        geometry=None
        geoObj = None
        feature = None
class BuildingLandConstruction:

    def __init__(self,xmlTag_base,cadNumber,property):
        self.__nameProcess = "Импорт зданий и сооружений"
        self.__xmlObj=xmlTag_base
        self.__cadNumber=cadNumber
        self.__properties=property
        self.__cs_prj4=self.__properties['cs']
        #self.__mi_style = self.__properties['land_building']['style']
        self.__mi_style=''
        self.__list_layers=[]
        self.__prepare()

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

    @property
    def NameProcess(self):
        return self.__nameProcess
    def __prepare(self):
        self.__buildings=[]
        objBulding=None
        try:
            objBulding=self.__xmlObj.build_records
        except:
            pass
        if objBulding is not None:
            for build in objBulding.build_record:
                self.__buildings.append(Building(build,self.__cadNumber,self.__properties))
        objLands=None
        try:
            objLands=self.__xmlObj.land_records
        except:
            pass
        self.__lands = []
        if objLands is not None:
            for land in objLands.land_record:
                self.__lands.append(Land(land,self.__cadNumber,self.__properties))
        objConstruction=None
        self.__construction=[]
        try:
            objConstruction = self.__xmlObj.construction_records
        except:
            pass
        if objConstruction is not None:
            for construction in objConstruction.construction_record:
                self.__construction.append(Construction(construction,self.__cadNumber,self.__properties))
        objUnderConstruction=None
        try:
            objUnderConstruction = self.__xmlObj.object_under_construction_records
        except:
            pass
        if objUnderConstruction is not None:
            for construction in objUnderConstruction.object_under_construction_record:
                self.__construction.append(Construction(construction, self.__cadNumber,self.__properties))
    def Write(self,layer,dbDictionary,mapCatalog=None):
        if len(self.__buildings)>0:
                ''' Получаем стиль из базы'''
                mi_style = dbDictionary.GetStyle('kpt_11_style', 'building')
                if mi_style[0] is None:
                    print(mi_style[1])
                    str_mi_style = 'Pen (2, 6, 16711680) Brush (1, 16777215, 16777215)'
                else:
                    str_mi_style = mi_style[0]
                layer_building=self.__сreateLayer(self.__curent_ds,self.__buildings[0],self.__createNewLayer)
                self.__list_layers.append(layer_building.GetName())
                for building in self.__buildings:
                    building.Write(layer_building,str_mi_style)
                #name_geometry=layer_building.GetGeometryColumn()
                #name_layer,name_geo_col,ext=getInfoSpatialLayer(layer_building)
                mapCatalog.addLayerInfoInMapCataalog(layer_building,self.__properties['cs_mi'])
                layer_building=None
                #mapCatalog.insertLayerDescription(name_layer,name_geo_col,None,self.__properties['cs_mi'],ext)
        if len(self.__lands)>0:
            ''' Получаем стиль из базы'''
            mi_style = dbDictionary.GetStyle('kpt_11_style', 'land')
            if mi_style[0] is None:
                print(mi_style[1])
                str_mi_style = 'Pen (2, 6, 16711680) Brush (1, 16777215, 16777215)'
            else:
                str_mi_style = mi_style[0]
            layer_land = self.__сreateLayer(self.__curent_ds, self.__lands[0], self.__createNewLayer)
            self.__list_layers.append(layer_land.GetName())
            for land in self.__lands:
                land.Write(layer_land, str_mi_style)
            mapCatalog.addLayerInfoInMapCataalog(layer_land, self.__properties['cs_mi'])
            #name_layer, name_geo_col, ext = getInfoSpatialLayer(layer_land)
            layer_land = None
            #mapCatalog.insertLayerDescription(name_layer, name_geo_col, None, self.__properties['cs_mi'], ext)
        if len(self.__construction)>0:
            ''' Получаем стиль из базы'''
            mi_style = dbDictionary.GetStyle('kpt_11_style', 'сonstruction')
            if mi_style[0] is None:
                print(mi_style[1])
                str_mi_style = 'Pen (2, 6, 16711680) Brush (1, 16777215, 16777215)'
            else:
                str_mi_style = mi_style[0]
            layer_construction = self.__сreateLayer(self.__curent_ds, self.__construction[0], self.__createNewLayer)
            self.__list_layers.append(layer_construction.GetName())
            for construction in self.__construction:
                construction.Write(layer_construction, str_mi_style)
            mapCatalog.addLayerInfoInMapCataalog(layer_construction, self.__properties['cs_mi'])
            #name_layer, name_geo_col, ext = getInfoSpatialLayer(layer_construction)

            layer_construction = None
            #mapCatalog.insertLayerDescription(name_layer, name_geo_col, None, self.__properties['cs_mi'], ext)
    def CreateLayer(self, ds, createNewLayer=True):
        self.__curent_ds=ds
        self.__createNewLayer=createNewLayer
        return None
    def __сreateLayer(self,ds,objectOut,createNewLayer=True):
        srs = osr.SpatialReference()
        srs.ImportFromWkt(self.__cs_prj4)
        layer=None
        if not createNewLayer:
            layer=ds.GetLayer(objectOut.name_layer)
            if layer is not None:
                return layer

        layer = ds.CreateLayer(objectOut.name_layer, srs, geom_type=ogr.wkbPolygon)
        struct_layer=objectOut.getStruct()
        struct_layer.append({'name': 'MI_STYLE', 'type':ogr.OFTString, 'width':250})
        for fld in struct_layer:
            # 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








