import FreeCAD as App
import unicodedata
import numpy as np
from .helper.is_solid import is_object_solid

## Программа, содержащая все проверки активного проекта FreeCAD в соответствии с issue#149

doc = App.ActiveDocument
Console = App.Console


#========== Проверка имен ===============
class FormalValidator:
    def __init__(self, document):
        self.document = document

    def is_unicode_safe(self, name):
        normalized_name = unicodedata.normalize('NFKD', name).encode('ASCII', 'ignore').decode()
        return name == normalized_name

    def fix_invalid_names(self):
        for obj in self.document.Objects:
            if not self.is_unicode_safe(obj.Label):
                safe_name = unicodedata.normalize('NFKD', obj.Label).encode('ASCII', 'ignore').decode()
                obj.Label = safe_name
        self.document.recompute()
    
    #запуск проверки и исправления имен файлов
    def validate(self):
        invalid_names = [obj.Label for obj in self.document.Objects if not self.is_unicode_safe(obj.Label)]
        if invalid_names:
            print("Некорректные имена:")
            for name in invalid_names:
                print(name)
            self.fix_invalid_names()
        else:
            print("Все имена корректны.")
            

class ModelValidator:
    def __init__(self, document):
        self.document = document
        self.partlist = [obj for obj in self.document.Objects if (is_object_solid(obj) and not obj.TypeId == 'App::Part') ]
        self.material_objects = [obj for obj in document.Objects if obj.TypeId == "App::MaterialObjectPython"]
        
    def get_parts(self):
        pass
        
        

    def validate_geometry(self):
        for obj in self.document.Objects:
            if hasattr(obj, "Shape") and not is_object_solid(obj):
                Console.PrintError(f"Объект '{obj.Name}' не является твердым телом.\n")
                
    def is_intersecting(self, obj1, obj2):
        if obj1.Shape.common(obj2.Shape).Volume > 0.0:
            return True

    def has_allow_intersections(self, obj):
        if hasattr(obj, 'Base_Allowed_Intersection') and getattr(obj, 'Base_Allowed_Intersection', True):
            return True

    def check_bodies_for_intersections(self):
        bodies = self.partlist
        num_bodies = len(bodies)
        
        for i in range(num_bodies):
            for j in range(i + 1, num_bodies):
                body1 = bodies[i]
                body2 = bodies[j]
                if self.is_intersecting(body1, body2):
                    if self.has_allow_intersections(body1) or self.has_allow_intersections(body2):
                        Console.PrintWarning(
                            f"Тела '{body1.Label}' и '{body2.Label}' ""предусмотренно пересекаются.\n")
                    else:
                        Console.PrintError(
                            f"Тела '{body1.Label}' и '{body2.Label}' ""непредусмотренно пересекаются.\n")
                        print('V = ' + str(body1.Shape.common(body2.Shape).Volume))

    
    def get_adjacency_matrix(self, padding = 1):
        # print(int(padding))
        bodies = self.partlist
        num_bodies = len(bodies)
        adjacency_matrix = np.zeros((num_bodies, num_bodies), dtype=bool)

        for i in range(num_bodies):
            shape1 = bodies[i].Shape
            for j in range(i + 1, num_bodies):
                shape2 = bodies[j].Shape

                if shape1.common(shape2).Volume > 0:
                    continue  

                distance = shape1.distToShape(shape2)
                if distance[0] < padding:
                    adjacency_matrix[i][j] = 1
                    adjacency_matrix[j][i] = 1

        return adjacency_matrix

    def check_bodies_without_contacts(self):
        adjacency_matrix = self.get_adjacency_matrix()  
        bodies = self.partlist

        isolated_bodies = []
        num_bodies = len(bodies)

        for i in range(num_bodies):
            # Проверяем, есть ли у тела касания с другими телами
            if not any(adjacency_matrix[i]):
                isolated_bodies.append(bodies[i].Label)

        if isolated_bodies:
            App.Console.PrintWarning("Следующие тела не касаются других:\n")
            for label in isolated_bodies:
                App.Console.PrintWarning(f"- {label}\n")
        else:
            App.Console.PrintMessage("Все тела имеют контакты.\n")
            

    def find_material_objects(document):
        pass
        
        

    def check_bodies_with_multiple_materials(self):
        material_objects = self.material_objects
        material_references = {}

        for material in material_objects:
            if hasattr(material, "References"):
                for reference in material.References:
                    if reference in material_references:
                        material_references[reference].append(material.Label)
                    else:
                        material_references[reference] = [material.Label]

        conflicts = {k: v for k, v in material_references.items() if len(v) > 1}

        if conflicts:
            App.Console.PrintWarning("Обнаружены конфликты между объектами материалов:\n")
            for ref, materials in conflicts.items():
                App.Console.PrintWarning(f"Деталь '{ref[0].Label}' используется в материалах: {', '.join(materials)}\n")
        else:
            App.Console.PrintMessage("Конфликтов в ссылках материалов не обнаружено.\n")
    
    def check_bodies_without_material(self):
        material_objects = self.material_objects
        partlist = self.partlist
        referenced_bodies = []
        for material in material_objects:
            if hasattr(material, "References"):
                for reference in material.References:
                    referenced_bodies.append(reference[0].Label)

        bodies_without_material = []
        for part in partlist:
            if part.Label not in referenced_bodies:
                bodies_without_material.append(part.Label)

        if bodies_without_material:
            App.Console.PrintWarning("Следующие тела не имеют назначенного материала:\n")
            for name in bodies_without_material:
                App.Console.PrintWarning(f"- {name}\n")
        else:
            App.Console.PrintMessage("Все тела имеют назначенные материалы.\n")
    


    def validate(self):
        self.validate_geometry()
        self.check_bodies_for_intersections()
        self.check_bodies_without_contacts()
        self.check_bodies_without_material()
        self.check_bodies_with_multiple_materials()
        
def validate_project():
    doc = App.ActiveDocument      
    FormalValidator(doc).validate()
    ModelValidator(doc).validate()


