#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
FreeCAD Simple Info - Bounding Box ve Alüminyum Ağırlık Hesaplayıcı
FreeCADCmd ile uyumlu basit bilgi çıkarma scripti
STP dosyasını otomatik olarak açıp tüm nesneleri analiz eder
fcinfo.py SAT 234-240, 375-377, 623-630, 3459-3491 AYNEN ALINMIŞTIR
"""

import FreeCAD
import FreeCAD as App
import sys
import os
import json

# GLOBAL DEĞİŞKENLER (fcinfo.py satır 375-377, 623-630)
seTDecimalValue = 4  # fcinfo.py satır 375-377
uniteM  = 1.0   # mm (fcinfo.py satır 623)
uniteMs = "mm"  # fcinfo.py satır 624
uniteS  = 1.0   # mm² (fcinfo.py satır 625)
uniteSs = "mm²" # fcinfo.py satır 626
uniteV  = 1.0   # mm³ (fcinfo.py satır 627)
uniteVs = "mm³" # fcinfo.py satır 628
uniteP  = 1.0   # gram (fcinfo.py satır 629)
unitePs = "g"   # fcinfo.py satır 630

def Around(a):
    """
    fcinfo.py satır 234-240 AYNEN ALINDI
    """
    global seTDecimalValue

    if seTDecimalValue == -1:
        seTDecimalValue = 1000
    ard = round(a, seTDecimalValue)
    return ard

def get_object_info(obj):
    """
    fcinfo.py satır 3459-3491 AYNEN ALINDI - SHAPE İÇİN
    """
    global seTDecimalValue, uniteM, uniteMs, uniteV, uniteVs, uniteP, unitePs

    # Alüminyum yoğunluğu (fcinfo.py satır 2138)
    seTDensiteValue = 2.71  # kg/dm³ - Metal Aluminium (Al)

    try:
        print(f"\n[DEBUG] ===== Nesne: {obj.Name} =====")
        print(f"[DEBUG] TypeId: {obj.TypeId if hasattr(obj, 'TypeId') else 'N/A'}")

        # GERÇEK İSMİ BULMAK İÇİN TÜM OLASI ALANLARI KONTROL ET
        real_name = None
        name_candidates = {}

        # 1. Label özellikleri
        if hasattr(obj, 'Label'):
            name_candidates['Label'] = obj.Label
            print(f"[DEBUG] obj.Label = '{obj.Label}'")

        if hasattr(obj, 'Label2'):
            name_candidates['Label2'] = obj.Label2
            print(f"[DEBUG] obj.Label2 = '{obj.Label2}'")

        # 2. Name (internal)
        if hasattr(obj, 'Name'):
            name_candidates['Name'] = obj.Name
            print(f"[DEBUG] obj.Name = '{obj.Name}'")

        # 3. OldLabel (eski isim)
        if hasattr(obj, 'OldLabel'):
            name_candidates['OldLabel'] = obj.OldLabel
            print(f"[DEBUG] obj.OldLabel = '{obj.OldLabel}'")

        # 4. FullName (tam yol)
        if hasattr(obj, 'FullName'):
            name_candidates['FullName'] = obj.FullName
            print(f"[DEBUG] obj.FullName = '{obj.FullName}'")

        # 5. LinkedObject (bağlı nesne)
        try:
            linked_obj = obj.getLinkedObject()
            if linked_obj and linked_obj != obj:
                if hasattr(linked_obj, 'Label'):
                    name_candidates['LinkedObject.Label'] = linked_obj.Label
                    print(f"[DEBUG] obj.getLinkedObject().Label = '{linked_obj.Label}'")
                if hasattr(linked_obj, 'Name'):
                    name_candidates['LinkedObject.Name'] = linked_obj.Name
                    print(f"[DEBUG] obj.getLinkedObject().Name = '{linked_obj.Name}'")
        except Exception as e:
            print(f"[DEBUG] getLinkedObject() HATA: {e}")

        # 6. Parent zincirini EN ÜSTE KADAR TAKİP ET
        print(f"[DEBUG] === Parent Zinciri Araştırması ===")
        current_obj = obj
        parent_level = 0
        max_parent_depth = 10  # Sonsuz döngü önleme

        while parent_level < max_parent_depth:
            try:
                parent = current_obj.getParent()
                if parent and parent != current_obj:
                    parent_level += 1
                    print(f"[DEBUG] Parent Level {parent_level}:")

                    if hasattr(parent, 'Label') and parent.Label:
                        key = f'Parent.Level{parent_level}.Label'
                        name_candidates[key] = parent.Label
                        print(f"[DEBUG]   {key} = '{parent.Label}'")

                    if hasattr(parent, 'Name') and parent.Name:
                        key = f'Parent.Level{parent_level}.Name'
                        name_candidates[key] = parent.Name
                        print(f"[DEBUG]   {key} = '{parent.Name}'")

                    if hasattr(parent, 'Label2') and parent.Label2:
                        key = f'Parent.Level{parent_level}.Label2'
                        name_candidates[key] = parent.Label2
                        print(f"[DEBUG]   {key} = '{parent.Label2}'")

                    current_obj = parent  # Bir üst seviyeye çık
                else:
                    print(f"[DEBUG] Parent zinciri sonu (Level {parent_level})")
                    break
            except Exception as e:
                print(f"[DEBUG] getParent() Level {parent_level} HATA: {e}")
                break

        # 7. ParentGroup zincirini kontrol et
        print(f"[DEBUG] === ParentGroup Araştırması ===")
        try:
            parent_group = obj.getParentGroup()
            if parent_group:
                if hasattr(parent_group, 'Label') and parent_group.Label:
                    name_candidates['ParentGroup.Label'] = parent_group.Label
                    print(f"[DEBUG] obj.getParentGroup().Label = '{parent_group.Label}'")
                if hasattr(parent_group, 'Name') and parent_group.Name:
                    name_candidates['ParentGroup.Name'] = parent_group.Name
                    print(f"[DEBUG] obj.getParentGroup().Name = '{parent_group.Name}'")
        except Exception as e:
            print(f"[DEBUG] getParentGroup() HATA: {e}")

        # 8. Parents (çoğul) - tüm üst nesneler
        print(f"[DEBUG] === Parents (Tümü) Araştırması ===")
        try:
            if hasattr(obj, 'Parents') and obj.Parents:
                print(f"[DEBUG] obj.Parents toplam: {len(obj.Parents)}")
                for idx, parent_tuple in enumerate(obj.Parents):
                    try:
                        parent_obj = parent_tuple[0] if isinstance(parent_tuple, tuple) else parent_tuple
                        if hasattr(parent_obj, 'Label') and parent_obj.Label:
                            key = f'Parents[{idx}].Label'
                            name_candidates[key] = parent_obj.Label
                            print(f"[DEBUG]   {key} = '{parent_obj.Label}'")
                    except Exception as e:
                        print(f"[DEBUG]   Parents[{idx}] HATA: {e}")
        except Exception as e:
            print(f"[DEBUG] Parents HATA: {e}")

        # 8. Shape içindeki tag/name (varsa)
        if hasattr(obj, 'Shape') and hasattr(obj.Shape, 'Tag'):
            try:
                name_candidates['Shape.Tag'] = str(obj.Shape.Tag)
                print(f"[DEBUG] obj.Shape.Tag = '{obj.Shape.Tag}'")
            except:
                pass

        # GERÇEK İSMİ BELİRLE: Öncelik sırası
        # Label2 > Label > OldLabel > LinkedObject.Label > Parent seviyeler > ParentGroup > Parents > FullName > Name
        real_name = None
        real_name_source = None

        # Öncelik 1: Label2
        if 'Label2' in name_candidates and name_candidates['Label2']:
            real_name = name_candidates['Label2']
            real_name_source = 'Label2'
        # Öncelik 2: Label
        elif 'Label' in name_candidates and name_candidates['Label']:
            real_name = name_candidates['Label']
            real_name_source = 'Label'
        # Öncelik 3: OldLabel
        elif 'OldLabel' in name_candidates and name_candidates['OldLabel']:
            real_name = name_candidates['OldLabel']
            real_name_source = 'OldLabel'
        # Öncelik 4: LinkedObject.Label
        elif 'LinkedObject.Label' in name_candidates and name_candidates['LinkedObject.Label']:
            real_name = name_candidates['LinkedObject.Label']
            real_name_source = 'LinkedObject.Label'
        else:
            # Öncelik 5: Parent seviyelerini EN ÜSTTEN başlayarak kontrol et
            parent_found = False
            for level in range(max_parent_depth, 0, -1):  # 10'dan 1'e geriye doğru
                key = f'Parent.Level{level}.Label'
                if key in name_candidates and name_candidates[key]:
                    real_name = name_candidates[key]
                    real_name_source = key
                    parent_found = True
                    break

            if not parent_found:
                # Öncelik 6: ParentGroup
                if 'ParentGroup.Label' in name_candidates and name_candidates['ParentGroup.Label']:
                    real_name = name_candidates['ParentGroup.Label']
                    real_name_source = 'ParentGroup.Label'
                # Öncelik 7: Parents listesi
                elif any(k.startswith('Parents[') and k.endswith('].Label') for k in name_candidates):
                    for key in sorted(name_candidates.keys()):
                        if key.startswith('Parents[') and key.endswith('].Label') and name_candidates[key]:
                            real_name = name_candidates[key]
                            real_name_source = key
                            break
                # Öncelik 8: FullName
                elif 'FullName' in name_candidates and name_candidates['FullName']:
                    real_name = name_candidates['FullName']
                    real_name_source = 'FullName'
                # Öncelik 9: Name (internal)
                elif 'Name' in name_candidates and name_candidates['Name']:
                    real_name = name_candidates['Name']
                    real_name_source = 'Name'

        if not real_name:
            real_name = "Unknown"
            real_name_source = "None"

        print(f"[DEBUG] *** GERÇEK İSİM BULUNDU: '{real_name}' (Kaynak: {real_name_source}) ***")
        print(f"[DEBUG] İsim adayları: {json.dumps(name_candidates, indent=2, ensure_ascii=False)}")

        # Shape kontrolü
        if not hasattr(obj, "Shape"):
            print(f"[DEBUG] ✗ Shape YOK, atlanıyor\n")
            return None

        # fcinfo.py satır 3459: surface (object)
        try:
            surface = obj.Shape.Area
            print(f"[DEBUG] surface = obj.Shape.Area = {surface}")
        except Exception as e:
            surface = 0.0
            print(f"[DEBUG] surface HATA: {e}, = 0.0")

        # fcinfo.py satır 3470: volume
        try:
            volume_ = obj.Shape.Volume
            print(f"[DEBUG] volume_ = obj.Shape.Volume = {volume_}")
        except Exception as e:
            volume_ = 0.0
            print(f"[DEBUG] volume_ HATA: {e}, = 0.0")

        # fcinfo.py satır 3475: poids (TAM FORMÜL)
        poids = ((volume_ * seTDensiteValue) * uniteP) / 1000.0
        print(f"[DEBUG] poids = ((volume_ * seTDensiteValue) * uniteP) / 1000.0")
        print(f"[DEBUG] poids = (({volume_} * {seTDensiteValue}) * {uniteP}) / 1000.0 = {poids}")

        # AXIS-ALIGNED BoundBox (eski yöntem - YANLIŞ yönlerde)
        try:
            print(f"[DEBUG] Axis-Aligned BoundBox (YANLIŞ yönler):")
            boundBox_aa = obj.Shape.BoundBox
            boundBoxLX_aa = boundBox_aa.XLength
            boundBoxLY_aa = boundBox_aa.YLength
            boundBoxLZ_aa = boundBox_aa.ZLength
            print(f"[DEBUG]   X={boundBoxLX_aa}, Y={boundBoxLY_aa}, Z={boundBoxLZ_aa}")
        except Exception as e:
            print(f"[DEBUG] Axis-Aligned BoundBox HATA: {e}")
            boundBoxLX_aa = boundBoxLY_aa = boundBoxLZ_aa = 0.0

        # OPTIMAL BoundBox (DOĞRU yönlerde - Oriented Bounding Box)
        try:
            print(f"[DEBUG] Optimal BoundBox (DOĞRU yönler - OBB):")
            import Part
            obb = obj.Shape.optimalBoundingBox()
            print(f"[DEBUG] optimalBoundingBox() = {obb}")

            boundBoxLX = obb.XLength
            boundBoxLY = obb.YLength
            boundBoxLZ = obb.ZLength
            boundBoxDiag = obb.DiagonalLength

            print(f"[DEBUG] ✓ OBB X={boundBoxLX}, Y={boundBoxLY}, Z={boundBoxLZ}, Diag={boundBoxDiag}")

            # String formatı
            boundBox_S = "[X: "+str(Around(boundBoxLX * uniteM))+" "+uniteMs+"] x [Y: "+str(Around(boundBoxLY * uniteM))+" "+uniteMs+"] x [Z: "+str(Around(boundBoxLZ * uniteM))+" "+uniteMs+"] Diagonal: " + str(Around(boundBoxDiag * uniteM))+" "+uniteMs
            print(f"[DEBUG] boundBox_S = {boundBox_S}")

            # Min değerler
            boundBoxXMin = obb.XMin
            boundBoxYMin = obb.YMin
            boundBoxZMin = obb.ZMin
            print(f"[DEBUG] OBB Min: X={boundBoxXMin}, Y={boundBoxYMin}, Z={boundBoxZMin}")

        except Exception as e:
            print(f"[DEBUG] ✗ Optimal BoundBox HATA: {e}")
            print(f"[DEBUG] Axis-Aligned değerleri kullanılıyor...")
            boundBoxLX = boundBoxLX_aa
            boundBoxLY = boundBoxLY_aa
            boundBoxLZ = boundBoxLZ_aa
            try:
                boundBoxDiag = boundBox_aa.DiagonalLength
            except:
                boundBoxDiag = 0.0
            boundBox_S = "[FALLBACK] [X: "+str(Around(boundBoxLX * uniteM))+" "+uniteMs+"] x [Y: "+str(Around(boundBoxLY * uniteM))+" "+uniteMs+"] x [Z: "+str(Around(boundBoxLZ * uniteM))+" "+uniteMs+"]"
            boundBoxXMin = boundBoxYMin = boundBoxZMin = 0.0

        # Matrix of Inertia
        try:
            matrixOfInertia = obj.Shape.MatrixOfInertia
            matrixOfInertiaRaw = str(matrixOfInertia)
            print(f"[DEBUG] Matrix of Inertia: {matrixOfInertiaRaw}")
        except Exception as e:
            matrixOfInertiaRaw = ""
            print(f"[DEBUG] Matrix of Inertia HATA: {e}")

        # Sonuçlar - TÜM PARAMETRELER
        result = {
            'object_name': real_name,  # GERÇEK İSİM (Label2/Label/Parent/Linked object'ten)
            'object_name_source': real_name_source,  # İsmin bulunduğu kaynak
            'object_name_internal': obj.Name if hasattr(obj, 'Name') else 'Unknown',  # FreeCAD internal ismi
            'object_label': obj.Label if hasattr(obj, 'Label') else obj.Name,
            'object_type': obj.TypeId if hasattr(obj, 'TypeId') else 'Unknown',
            'name_search_results': name_candidates,  # İsim arama sonuçları (tüm adaylar)

            # Boyutlar - Optimal BoundBox (DOĞRU)
            'bounding_box': {
                'x_length_mm': Around(boundBoxLX * uniteM),
                'y_length_mm': Around(boundBoxLY * uniteM),
                'z_length_mm': Around(boundBoxLZ * uniteM),
                'diagonal_mm': Around(boundBoxDiag * uniteM),
                'x_min': Around(boundBoxXMin),
                'y_min': Around(boundBoxYMin),
                'z_min': Around(boundBoxZMin),
                'string': boundBox_S,
                'type': 'optimal_oriented'
            },

            # Boyutlar - Axis-Aligned (eski/yanlış yönler)
            'bounding_box_axis_aligned': {
                'x_length_mm': Around(boundBoxLX_aa * uniteM),
                'y_length_mm': Around(boundBoxLY_aa * uniteM),
                'z_length_mm': Around(boundBoxLZ_aa * uniteM),
                'type': 'axis_aligned'
            },

            # Hacim ve Yüzey
            'surface_mm2': Around(surface),
            'volume_mm3': Around(volume_),
            'volume_cm3': Around(volume_ / 1000.0),

            # Ağırlık - Alüminyum (Yoğunluk: 2.7 kg/dm³)
            'weight_aluminium': {
                'grams': Around(poids),
                'kilograms': Around(poids / 1000.0),
                'density_kg_dm3': seTDensiteValue
            },

            # Matrix of Inertia
            'matrix_of_inertia': {
                'raw': matrixOfInertiaRaw
            }
        }

        print(f"[DEBUG] ✓ BAŞARILI")
        print(f"[DEBUG] Sonuç OBB: X={result['bounding_box']['x_length_mm']} Y={result['bounding_box']['y_length_mm']} Z={result['bounding_box']['z_length_mm']}")
        print(f"[DEBUG] =====================================\n")
        return result

    except Exception as e:
        print(f"[DEBUG] ✗✗✗ GENEL HATA: {e}\n")
        import traceback
        traceback.print_exc()
        return None


def print_object_info(result):
    """Sonuçları yazdır"""
    if result:
        print("\n" + "="*70)
        print(f"Nesne: {result['object_label']} ({result['object_name']})")
        print(f"Tip  : {result['object_type']}")
        print("="*70)
        print(f"Optimal BoundBox (DOĞRU):")
        print(f"  X Length     : {result['bounding_box']['x_length_mm']} mm")
        print(f"  Y Length     : {result['bounding_box']['y_length_mm']} mm")
        print(f"  Z Length     : {result['bounding_box']['z_length_mm']} mm")
        print(f"  Diagonal     : {result['bounding_box']['diagonal_mm']} mm")
        print(f"  String       : {result['bounding_box']['string']}")
        print(f"\nAxis-Aligned BoundBox (eski):")
        print(f"  X: {result['bounding_box_axis_aligned']['x_length_mm']} mm")
        print(f"  Y: {result['bounding_box_axis_aligned']['y_length_mm']} mm")
        print(f"  Z: {result['bounding_box_axis_aligned']['z_length_mm']} mm")
        print(f"\nYüzey ve Hacim:")
        print(f"  Surface      : {result['surface_mm2']} mm²")
        print(f"  Volume       : {result['volume_mm3']} mm³ ({result['volume_cm3']} cm³)")
        print(f"\nAlüminyum Ağırlık:")
        print(f"  Weight       : {result['weight_aluminium']['grams']} g")
        print(f"               : {result['weight_aluminium']['kilograms']} kg")
        print(f"  Density      : {result['weight_aluminium']['density_kg_dm3']} kg/dm³")
        print("="*70)


def analyze_step_file(file_path):
    """
    STP/STEP dosyasını açıp tüm nesneleri analiz eder

    Args:
        file_path: STP/STEP dosya yolu

    Returns:
        list: Tüm nesnelerin bilgilerini içeren liste
    """
    # Dosya kontrolü
    if not os.path.exists(file_path):
        print(f"HATA: Dosya bulunamadı: {file_path}")
        return []

    print(f"\nDosya açılıyor: {file_path}")
    print("Lütfen bekleyin...\n")

    try:
        # STEP dosyasını import et
        import Part
        print("[DEBUG] Part modülü yüklendi")

        doc = FreeCAD.newDocument("TempDoc")
        print(f"[DEBUG] Yeni doküman oluşturuldu: {doc.Name}")

        print("[DEBUG] STEP dosyası import ediliyor...")
        Part.insert(file_path, doc.Name)
        print("[DEBUG] STEP dosyası import edildi")

        # Tüm nesneleri al
        objects = doc.Objects
        print(f"[DEBUG] doc.Objects uzunluğu: {len(objects)}")

        if objects:
            print("[DEBUG] Nesne tipleri:")
            for i, obj in enumerate(objects):
                obj_type = obj.TypeId if hasattr(obj, 'TypeId') else 'Unknown'
                has_shape = hasattr(obj, 'Shape')
                has_mesh = hasattr(obj, 'Mesh')
                print(f"  [{i}] {obj.Name} - Type: {obj_type}, HasShape: {has_shape}, HasMesh: {has_mesh}")

        if not objects:
            print("HATA: Dosyada nesne bulunamadı!")
            FreeCAD.closeDocument(doc.Name)
            return []

        print(f"\nToplam {len(objects)} nesne bulundu.\n")

        results = []
        total_weight_g = 0

        # Her nesneyi analiz et
        print("="*60)
        print("NESNELER ANALİZ EDİLİYOR:")
        print("="*60)
        for i, obj in enumerate(objects):
            print(f"\n[{i+1}/{len(objects)}] Analiz ediliyor: {obj.Name}")
            result = get_object_info(obj)
            if result:
                results.append(result)
                print_object_info(result)
                total_weight_g += result['weight_aluminium']['grams']
            else:
                print(f"  → Atlandı (veri alınamadı)\n")

        # Toplam özet
        print("\n" + "="*60)
        if results:
            print("\n" + "█"*60)
            print("TOPLAM ÖZET")
            print("█"*60)
            print(f"Analiz edilen nesne sayısı: {len(results)}")
            print(f"Toplam alüminyum ağırlık  : {round(total_weight_g, 2)} g")
            print(f"                           : {round(total_weight_g / 1000.0, 4)} kg")
            print("█"*60 + "\n")

            # JSON ÇIKTISI
            json_output = {
                'file_path': file_path,
                'total_objects': len(results),
                'total_weight_aluminium_grams': round(total_weight_g, 4),
                'total_weight_aluminium_kg': round(total_weight_g / 1000.0, 4),
                'objects': results
            }

            # JSON dosyasına kaydet
            json_file = file_path.replace('.stp', '_analysis.json').replace('.step', '_analysis.json')
            with open(json_file, 'w', encoding='utf-8') as f:
                json.dump(json_output, f, indent=2, ensure_ascii=False)

            print(f"\n✓ JSON çıktısı kaydedildi: {json_file}\n")

            # Console'a da yazdır
            print("\n" + "="*60)
            print("JSON ÇIKTISI:")
            print("="*60)
            print(json.dumps(json_output, indent=2, ensure_ascii=False))
            print("="*60 + "\n")
        else:
            print("\n[UYARI] Hiçbir nesne analiz edilemedi!")
            print("Muhtemel sebepler:")
            print("  - Nesnelerde Shape/Mesh özelliği yok")
            print("  - BoundBox hesaplanamıyor")
            print("  - Dosya formatı uyumsuz")
            print("="*60 + "\n")

        # Belgeyi kapat
        FreeCAD.closeDocument(doc.Name)

        return results

    except Exception as e:
        print(f"\n[HATA] Dosya açılırken hata oluştu: {str(e)}")
        import traceback
        print("\nDetaylı hata:")
        traceback.print_exc()
        return []


# FreeCADCmd kullanımı için ana fonksiyon
# FreeCADCmd ile if __name__ == "__main__" çalışmayabilir, direkt çalıştır

# Argümanları debug için yazdır
print(f"[DEBUG] Script başlatıldı")
print(f"[DEBUG] sys.argv: {sys.argv}")
print(f"[DEBUG] Toplam argüman sayısı: {len(sys.argv)}")
for i, arg in enumerate(sys.argv):
    print(f"[DEBUG] sys.argv[{i}] = {arg}")
print()

# Dosya yolunu bul
step_file = None

# freecadcmd bazen farklı şekillerde argüman geçirebilir
# Önce .stp veya .step uzantılı dosyayı ara
for arg in sys.argv[1:]:
    if arg.lower().endswith('.stp') or arg.lower().endswith('.step'):
        step_file = arg
        break

if step_file is None:
    # Uzantı bulunamazsa, son argümanı al (eğer varsa)
    if len(sys.argv) >= 2:
        step_file = sys.argv[-1]  # Son argüman

if step_file and os.path.exists(step_file):
    print(f"[DEBUG] Kullanılacak dosya: {step_file}\n")
    analyze_step_file(step_file)
else:
    print("\n[HATA] Geçerli bir STEP dosyası bulunamadı!")
    print("\nKullanım:")
    print("  freecadcmd free.py <dosya.stp>")
    print("\nÖrnek:")
    print('  freecadcmd free.py "C:\\path\\dosya.stp"')
    if step_file:
        print(f"\nBulunan dosya yolu: {step_file}")
        print(f"Dosya var mı: {os.path.exists(step_file)}")
    print()
