from rdflib import Graph, Namespace, URIRef, Literal, RDF, RDFS, XSD


class shape():

    def __init__(self, IfcShapeRepresentation, g, ontology, resource):
        self.IfcShapeRepresentation = IfcShapeRepresentation
        self.g = g
        self.ontology = ontology
        self.resource = resource

    def ifcShapeRepresentationFunction(self):
        for shape in self.IfcShapeRepresentation:
            shapeID = shape['globalId']
            # basic information
            self.g.add((self.resource[shapeID], RDF.type, self.ontology.Shape))
            try:  # name is not always there
                shapeIdentifier = shape['representationIdentifier']
                self.g.add((self.resource[shapeID], self.ontology.identifier, Literal(shapeIdentifier)))
            except Exception:
                pass
            try:  # description is not always there
                shapeType = shape['representationType']
                self.g.add((self.resource[shapeID], self.ontology.type, Literal(shapeType)))
            except Exception:
                pass
            try:
                iscontext = shape['contextOfItems']['ref']
                self.g.add((self.resource[iscontext], RDF.type, self.ontology.Context))
                self.g.add((self.resource[shapeID], self.ontology.hasContext, self.resource[iscontext]))
            except Exception:
                pass
            try:  # material is not always there
                count = 1
                for layer in shape['layerAssignments']:
                    layerType = layer['type']
                    layerName = layer['name']
                    self.g.add((self.resource["layer_" + shapeID + "_" + str(count)], RDF.type, self.ontology.Layer))
                    self.g.add((self.resource[shapeID], self.ontology.hasLayer, self.resource["layer_" + shapeID + "_" + str(count)]))
                    self.g.add((self.resource["layer_" + shapeID + "_" + str(count)], self.ontology.name, Literal(layerName)))
                    self.g.add((self.resource["layer_" + shapeID + "_" + str(count)], self.ontology.type, Literal(layerType)))
                    for item in layer['assignedItems']:
                        itemID = item['ref']
                        self.g.add(
                                (self.resource[itemID], RDF.type, self.ontology.Shape))
                        self.g.add((self.resource["layer_" + shapeID + "_" + str(count)], self.ontology.hasShape,
                                    self.resource[itemID]))
                    count += 1
            except Exception:
                pass
            try:  # material is not always there
                for representationMap in shape['representationMap']:
                    repID = representationMap['mappedRepresentation']['ref']
                    self.g.add((self.resource["mapped_representation_" + repID], RDF.type, self.ontology.MappedRepresentation))
                    self.g.add((self.resource[shapeID], self.ontology.hasMappedRepresentation, self.resource["mapped_representation_" + repID]))
                    self.g.add((self.resource["mapped_representation_" + repID], self.ontology.xlocation,
                                Literal(representationMap['mappingOrigin']['location'][
                                            'coordinates'][0])))
                    self.g.add((self.resource["mapped_representation_" + repID], self.ontology.ylocation,
                                Literal(representationMap['mappingOrigin']['location'][
                                            'coordinates'][1])))
                    self.g.add((self.resource["mapped_representation_" + repID], self.ontology.zlocation,
                                Literal(representationMap['mappingOrigin']['location'][
                                            'coordinates'][2])))
            except Exception:
                pass
            try:  # material is not always there
                for representationType in shape['ofProductRepresentation']:
                    for rep in representationType['representations']:
                        repID = rep['ref']
                        self.g.add((self.resource["representation_" + repID], RDF.type, self.ontology.Representation))
                        self.g.add((self.resource[shapeID], self.ontology.hasRepresentation, self.resource["representation_" + repID]))
            except Exception:
                pass
            try:  # material is not always there
                for shapeAspect in shape['ofShapeAspect']:
                    self.g.add((self.resource[shapeID], self.ontology.name,
                                Literal(shapeAspect["name"])))
                    self.g.add((self.resource[shapeID], self.ontology.definition,
                                Literal(shapeAspect["productDefinitional"])))
                    for rep in shapeAspect['shapeRepresentations']:
                        repID = rep['ref']
                        self.g.add((self.resource["shape_representation_" + repID], RDF.type, self.ontology.ShapeRepresentation))
                        self.g.add((self.resource[shapeID], self.ontology.hasShapeRepresentation, self.resource["shape_representation_" + repID]))
                    if 'representations' in list(shapeAspect['partOfProductDefinitionShape'].keys()):
                        for rep in shapeAspect['partOfProductDefinitionShape']['representations']:
                            repID = rep['ref']
                            self.g.add((self.resource["mapped_representation_" + repID], RDF.type, self.ontology.MappedRepresentation))
                            self.g.add((self.resource[shapeID], self.ontology.hasMappedRepresentation, self.resource["mapped_representation_" + repID]))
                    elif 'mappingOrigin' in list(shapeAspect['partOfProductDefinitionShape'].keys()):
                        self.g.add((self.resource["mapped_representation_" + shapeAspect['partOfProductDefinitionShape']['mappedRepresentation']['ref']], RDF.type,
                                    self.ontology.MappedRepresentation))
                        self.g.add((self.resource[shapeID], self.ontology.hasMappedRepresentation,
                                    self.resource["mapped_representation_" + shapeAspect['partOfProductDefinitionShape']['mappedRepresentation']['ref']]))
                        self.g.add((self.resource["mapped_representation_" + shapeAspect['partOfProductDefinitionShape']['mappedRepresentation']['ref']], self.ontology.hasxlocation,
                                    Literal(shapeAspect['partOfProductDefinitionShape']['mappingOrigin']['location']['coordinates'][0])))
                        self.g.add((self.resource["mapped_representation_" + shapeAspect['partOfProductDefinitionShape']['mappedRepresentation']['ref']], self.ontology.hasylocation,
                                    Literal(shapeAspect['partOfProductDefinitionShape']['mappingOrigin']['location']['coordinates'][1])))
                        self.g.add((self.resource["mapped_representation_" + shapeAspect['partOfProductDefinitionShape']['mappedRepresentation']['ref']], self.ontology.haszlocation,
                                    Literal(shapeAspect['partOfProductDefinitionShape']['mappingOrigin']['location']['coordinates'][2])))
            except Exception:
                pass
            try:  # material is not always there
                countpolygonalFace, countPolygonalCoord = 1, 1
                for item in shape['items']:
                    if item['type'] == "IfcExtrudedAreaSolid":
                        if 'depth' in list(item.keys()):
                            depth = item['depth']
                            self.g.add((self.resource[shapeID], self.ontology.depth, Literal(depth)))
                        if 'extrudedDirection' in list(item.keys()):
                            self.g.add((self.resource[shapeID], self.ontology.xextrudedDirection, Literal(item['extrudedDirection']['directionRatios'][0])))
                            self.g.add((self.resource[shapeID], self.ontology.yextrudedDirection, Literal(item['extrudedDirection']['directionRatios'][1])))
                            self.g.add((self.resource[shapeID], self.ontology.zextrudedDirection, Literal(item['extrudedDirection']['directionRatios'][2])))
                        if 'position' in list(item.keys()):
                            if 'location' in list(item['position'].keys()):
                                self.g.add((self.resource[shapeID], self.ontology.xlocation, Literal(item['position']['location']['coordinates'][0])))
                                self.g.add((self.resource[shapeID], self.ontology.ylocation, Literal(item['position']['location']['coordinates'][1])))
                                self.g.add((self.resource[shapeID], self.ontology.zlocation, Literal(item['position']['location']['coordinates'][2])))
                        if 'position' in list(item.keys()):
                            if 'axis' in list(item['position'].keys()):
                                self.g.add((self.resource[shapeID], self.ontology.xaxis, Literal(item['position']['axis']['directionRatios'][0])))
                                self.g.add((self.resource[shapeID], self.ontology.yaxis, Literal(item['position']['axis']['directionRatios'][1])))
                                self.g.add((self.resource[shapeID], self.ontology.zaxis, Literal(item['position']['axis']['directionRatios'][2])))
                        if 'position' in list(item.keys()):
                            if 'refDirection' in list(item['position'].keys()):
                                self.g.add((self.resource[shapeID], self.ontology.xrefDirection, Literal(item['position']['refDirection']['directionRatios'][0])))
                                self.g.add((self.resource[shapeID], self.ontology.yrefDirection, Literal(item['position']['refDirection']['directionRatios'][1])))
                                self.g.add((self.resource[shapeID], self.ontology.zrefDirection, Literal(item['position']['refDirection']['directionRatios'][2])))
                        if 'profileType' in list(item['sweptArea'].keys()):
                            self.g.add((self.resource[shapeID], self.ontology.sweptAreaType, Literal(item['sweptArea']['profileType'])))
                        if 'profileName' in list(item['sweptArea'].keys()):
                            self.g.add((self.resource[shapeID], self.ontology.sweptAreaName, Literal(item['sweptArea']['profileName'])))
                        if 'sweptArea' in list(item.keys()):
                            count = 1
                            for coord in item['sweptArea']['outerCurve']['points']['coordList']:
                                self.g.add(
                                    (self.resource["coord_" + shapeID + "_" + str(count)], RDF.type, self.ontology.Coord))
                                self.g.add((self.resource[shapeID], self.ontology.hasCoord,
                                            self.resource["coord_" + shapeID + "_" + str(count)]))
                                for constant in coord:
                                    self.g.add(
                                        (self.resource["coord_" + shapeID + "_" + str(count)], self.ontology.coord,
                                         Literal(constant)))
                                count += 1
                            countInner = 1
                            if "innerCurves" in list(item['sweptArea'].keys()):
                                for inner in item['sweptArea']['innerCurves']:
                                    for coord in inner['points']['coordList']:
                                        self.g.add(
                                            (self.resource["coordInner_" + shapeID + "_" + str(countInner)], RDF.type,
                                             self.ontology.InnerCoord))
                                        self.g.add((self.resource[shapeID], self.ontology.hasInnerCoord,
                                                    self.resource["coordInner_" + shapeID + "_" + str(countInner)]))

                                        for constant in coord:
                                            self.g.add(
                                                (self.resource["coordInner_" + shapeID + "_" + str(countInner)], self.ontology.innercoord,
                                                 Literal(constant)))
                                        countInner += 1
                    if item['type'] == "IfcMappedItem":
                        if "mappingTarget" in list(item.keys()):
                            if len(item['mappingTarget']['localOrigin']['coordinates']) == 3:
                                self.g.add((self.resource[shapeID], self.ontology.xlocation, Literal(item['mappingTarget']['localOrigin']['coordinates'][0])))
                                self.g.add((self.resource[shapeID], self.ontology.ylocation, Literal(item['mappingTarget']['localOrigin']['coordinates'][1])))
                                self.g.add((self.resource[shapeID], self.ontology.zlocation, Literal(item['mappingTarget']['localOrigin']['coordinates'][2])))
                            elif len(item['mappingTarget']['localOrigin']['coordinates']) == 2:
                                self.g.add((self.resource[shapeID], self.ontology.xlocation, Literal(item['mappingTarget']['localOrigin']['coordinates'][0])))
                                self.g.add((self.resource[shapeID], self.ontology.ylocation, Literal(item['mappingTarget']['localOrigin']['coordinates'][1])))
                            elif len(item['mappingTarget']['localOrigin']['coordinates']) == 1:
                                self.g.add((self.resource[shapeID], self.ontology.xlocation, Literal(item['mappingTarget']['localOrigin']['coordinates'][0])))
                            else:
                                pass
                            if "scale" in list(item['mappingTarget'].keys()):
                                self.g.add((self.resource[shapeID], self.ontology.scale,
                                            Literal(item['mappingTarget']['scale'])))
                        if "mappingSource" in list(item.keys()):
                            if len(item['mappingSource']['mappingOrigin']['location']['coordinates']) == 3:
                                self.g.add((self.resource[shapeID], self.ontology.location2X, Literal(item['mappingSource']['mappingOrigin']['location']['coordinates'][0])))
                                self.g.add((self.resource[shapeID], self.ontology.location2Y, Literal(item['mappingSource']['mappingOrigin']['location']['coordinates'][1])))
                                self.g.add((self.resource[shapeID], self.ontology.location2Z, Literal(item['mappingSource']['mappingOrigin']['location']['coordinates'][2])))
                            elif len(item['mappingSource']['mappingOrigin']['location']['coordinates']) == 2:
                                self.g.add((self.resource[shapeID], self.ontology.location2X, Literal(item['mappingSource']['mappingOrigin']['location']['coordinates'][0])))
                                self.g.add((self.resource[shapeID], self.ontology.location2Y, Literal(item['mappingSource']['mappingOrigin']['location']['coordinates'][1])))
                            elif len(item['mappingSource']['mappingOrigin']['location']['coordinates']) == 1:
                                self.g.add((self.resource[shapeID], self.ontology.location2X, Literal(item['mappingSource']['mappingOrigin']['location']['coordinates'][0])))
                            else:
                                pass
                    if item['type'] == "IfcIndexedPolyCurve":
                        if "points" in list(item.keys()):
                            self.g.add(
                                (self.resource["placement_" + shapeID], RDF.type, self.ontology.Placement))
                            self.g.add((self.resource[shapeID], self.ontology.hasPlacement,
                                        self.resource["placement_" + shapeID]))
                            count = 1
                            for coord in item['points']['coordList']:
                                self.g.add(
                                    (self.resource["coord_" + shapeID + "_" + str(count)], RDF.type, self.ontology.Coord))
                                self.g.add((self.resource["placement_" + shapeID], self.ontology.hasCoord,
                                            self.resource["coord_" + shapeID + "_" + str(count)]))
                                for constant in coord:
                                    self.g.add(
                                        (self.resource["coord_" + shapeID + "_" + str(count)], self.ontology.coord,
                                         Literal(constant)))
                                count += 1
                    if item['type'] == "IfcPolygonalFaceSet":
                        if "coordinates" in list(item.keys()):
                            self.g.add(
                                (self.resource["placement_" + shapeID], RDF.type, self.ontology.Placement))
                            self.g.add((self.resource[shapeID], self.ontology.hasPlacement,
                                        self.resource["placement_" + shapeID]))

                            for coord in item['coordinates']['coordList']:
                                self.g.add(
                                    (self.resource["coord_" + shapeID + "_" + str(countPolygonalCoord)], RDF.type,
                                     self.ontology.Coord))
                                self.g.add((self.resource["placement_" + shapeID], self.ontology.hasCoord,
                                            self.resource["coord_" + shapeID + "_" + str(countPolygonalCoord)]))
                                for constant in coord:
                                    self.g.add(
                                        (self.resource["coord_" + shapeID + "_" + str(countPolygonalCoord)], self.ontology.coord,
                                         Literal(constant)))
                                countPolygonalCoord += 1
                        if "closed" in list(item.keys()):
                            self.g.add(
                                (self.resource[shapeID], self.ontology.closed,
                                 Literal(item['closed'])))
                        if "faces" in list(item.keys()):
                            self.g.add(
                                (self.resource["placementFacet_" + shapeID], RDF.type, self.ontology.Placement))
                            self.g.add((self.resource[shapeID], self.ontology.hasFacetPlacement,
                                        self.resource["placementFacet_" + shapeID]))
                            for face in item['faces']:
                                self.g.add(
                                    (self.resource["faces_" + shapeID + "_" + str(countpolygonalFace)], RDF.type,
                                     self.ontology.Face))
                                self.g.add((self.resource["placementFacet_" + shapeID], self.ontology.hasFace,
                                            self.resource["faces_" + shapeID + "_" + str(countpolygonalFace)]))
                                if face["type"] == "IfcIndexedPolygonalFace":
                                    for constant in face['coordIndex']:
                                        self.g.add(
                                            (self.resource["faces_" + shapeID + "_" + str(countpolygonalFace)], self.ontology.faceValue,
                                             Literal(constant)))
                                    countpolygonalFace += 1
                                if face["type"] == "IfcIndexedPolygonalFaceWithVoids":
                                    for constant in face['coordIndex']:
                                        self.g.add(
                                            (self.resource["faces_" + shapeID + "_" + str(countpolygonalFace)],
                                             self.ontology.faceValue,
                                             Literal(constant)))
                                    for constant in face['innerCoordIndices']:
                                        for innerconstant in constant:
                                            self.g.add(
                                                (self.resource["faces_" + shapeID + "_" + str(countpolygonalFace)], self.ontology.faceValueInner,
                                                 Literal(innerconstant)))
                                    countpolygonalFace += 1
                    if item['type'] == "IfcBooleanClippingResult":
                        if "operator" in list(item.keys()):
                            self.g.add((self.resource[shapeID], self.ontology.operator, Literal(item["operator"])))
                        if "firstOperand" in list(item.keys()):
                            self.g.add(
                                (self.resource["firstplacement_" + shapeID], RDF.type, self.ontology.Placement))
                            self.g.add((self.resource[shapeID], self.ontology.hasFirstPlacement,
                                        self.resource["firstplacement_" + shapeID]))
                            if "sweptArea" in list(item["firstOperand"].keys()):
                                self.g.add((self.resource["firstplacement_" + shapeID], self.ontology.type,
                                            Literal(item["firstOperand"]["sweptArea"]["profileType"])))
                                self.g.add((self.resource["firstplacement_" + shapeID], self.ontology.name,
                                            Literal(item["firstOperand"]["sweptArea"]["profileName"])))
                                self.g.add((self.resource["firstplacement_" + shapeID], self.ontology.xcoordinate,
                                            Literal(item["firstOperand"]["sweptArea"]["position"]["location"]["coordinates"][0])))
                                self.g.add((self.resource["firstplacement_" + shapeID], self.ontology.ycoordinate,
                                            Literal(item["firstOperand"]["sweptArea"]["position"]["location"]["coordinates"][1])))
                                self.g.add((self.resource["firstplacement_" + shapeID], self.ontology.xDim,
                                            Literal(item["firstOperand"]["sweptArea"]["xDim"])))
                                self.g.add((self.resource["firstplacement_" + shapeID], self.ontology.yDim,
                                            Literal(item["firstOperand"]["sweptArea"]["yDim"])))
                            if "position" in list(item["firstOperand"].keys()):
                                self.g.add((self.resource["firstplacement_" + shapeID], self.ontology.xlocation,
                                            Literal(item["firstOperand"]["position"]["location"]["coordinates"][0])))
                                self.g.add((self.resource["firstplacement_" + shapeID], self.ontology.ylocation,
                                            Literal(item["firstOperand"]["position"]["location"]["coordinates"][1])))
                                self.g.add((self.resource["firstplacement_" + shapeID], self.ontology.zlocation,
                                            Literal(item["firstOperand"]["position"]["location"]["coordinates"][2])))
                            if "extrudedDirection" in list(item["firstOperand"].keys()):
                                self.g.add((self.resource["firstplacement_" + shapeID], self.ontology.xextrudedDirection,
                                            Literal(item["firstOperand"]["extrudedDirection"]["directionRatios"][0])))
                                self.g.add((self.resource["firstplacement_" + shapeID], self.ontology.yextrudedDirection,
                                            Literal(item["firstOperand"]["extrudedDirection"]["directionRatios"][1])))
                                self.g.add((self.resource["firstplacement_" + shapeID], self.ontology.zextrudedDirection,
                                            Literal(item["firstOperand"]["extrudedDirection"]["directionRatios"][2])))
                            if "depth" in list(item["firstOperand"].keys()):
                                self.g.add((self.resource["firstplacement_" + shapeID], self.ontology.depth,
                                            Literal(item["firstOperand"]["depth"])))
                        if "secondOperand" in list(item.keys()):
                            self.g.add(
                                (self.resource["secondplacement_" + shapeID], RDF.type, self.ontology.Placement))
                            self.g.add((self.resource[shapeID], self.ontology.hasSecondPlacement,
                                        self.resource["secondplacement_" + shapeID]))
                            if "baseSurface" in list(item["secondOperand"].keys()):
                                if "position" in list(item["secondOperand"]["baseSurface"].keys()):
                                    if "location" in list(item["secondOperand"]["baseSurface"]["position"].keys()):
                                        self.g.add((self.resource["secondplacement_" + shapeID], self.ontology.xlocation,
                                                    Literal(item["secondOperand"]["baseSurface"]["position"]["location"]["coordinates"][0])))
                                        self.g.add((self.resource["secondplacement_" + shapeID], self.ontology.ylocation,
                                                    Literal(item["secondOperand"]["baseSurface"]["position"]["location"]["coordinates"][1])))
                                        self.g.add((self.resource["secondplacement_" + shapeID], self.ontology.zlocation,
                                                    Literal(item["secondOperand"]["baseSurface"]["position"]["location"]["coordinates"][2])))
                                    if "axis" in list(item["secondOperand"]["baseSurface"]["position"].keys()):
                                        self.g.add((self.resource["secondplacement_" + shapeID], self.ontology.xaxis,
                                                    Literal(item["secondOperand"]["baseSurface"]["position"]["axis"]["directionRatios"][0])))
                                        self.g.add((self.resource["secondplacement_" + shapeID], self.ontology.yaxis,
                                                    Literal(item["secondOperand"]["baseSurface"]["position"]["axis"]["directionRatios"][1])))
                                        self.g.add((self.resource["secondplacement_" + shapeID], self.ontology.zaxis,
                                                    Literal(item["secondOperand"]["baseSurface"]["position"]["axis"]["directionRatios"][2])))
                                    if "refDirection" in list(item["secondOperand"]["baseSurface"]["position"].keys()):
                                        self.g.add((self.resource["secondplacement_" + shapeID], self.ontology.xrefDirection,
                                                    Literal(item["secondOperand"]["baseSurface"]["position"]["refDirection"]["directionRatios"][0])))
                                        self.g.add((self.resource["secondplacement_" + shapeID], self.ontology.yrefDirection,
                                                    Literal(item["secondOperand"]["baseSurface"]["position"]["refDirection"]["directionRatios"][1])))
                                        self.g.add((self.resource["secondplacement_" + shapeID], self.ontology.zrefDirection,
                                                    Literal(item["secondOperand"]["baseSurface"]["position"]["refDirection"]["directionRatios"][2])))
            except Exception:
                pass
        # Create RDF triples
        # Serialize and save the RDF graph to a TTL file
        file_path = "output.ttl"
        self.g.serialize(destination=file_path, format="turtle")