import win32com.client
import os
from datetime import datetime

def get_catia():
    """CATIA uygulamasına bağlan"""
    try:
        catia = win32com.client.Dispatch("CATIA.Application")
        return catia
    except:
        print("CATIA bulunamadı veya çalışmıyor!")
        return None

def detect_unit_scale_factor(part):
    """CATIA'daki birim faktörünü tespit et"""
    try:
        # Parameters'den birim bilgisini al
        doc = part.Parent
        
        # Document birimlerini kontrol et
        # CATIA Settings'den birim al
        try:
            # SettingControllers üzerinden birim kontrolü
            settings = catia.SettingControllers
            
            # Parameters and Measure settings
            param_settings = settings.Item("CATSysParmMeasureSettingCtrl")
            
            # Length unit
            length_unit = param_settings.LengthUnit
            
            # 0=mm, 1=inch, 2=meter, 3=feet vb.
            unit_map = {
                0: (1.0, "mm"),
                1: (25.4, "inch"),
                2: (1000.0, "m"),
                3: (304.8, "ft")
            }
            
            if length_unit in unit_map:
                factor, name = unit_map[length_unit]
                print(f"✓ CATIA Birimi: {name} (Faktör: {factor})")
                return factor
        except:
            pass
        
        # Varsayılan: mm
        print("ℹ️  Birim tespit edilemedi, mm varsayıldı")
        return 1.0
        
    except:
        return 1.0

def get_all_bodies_measurements(part, density=2710, apply_scale=True):
    """Katı modeller için tüm gövdeleri ölçüm"""
    try:
        doc = part.Parent
        bodies = part.Bodies
        
        if bodies.Count == 0:
            print("⚠ Parçada Body bulunamadı!")
            return None, None
        
        print(f"✓ Toplam Body sayısı: {bodies.Count}")
        
        # Birim faktörünü tespit et veya kullanıcıdan al
        if apply_scale:
            print("\n📏 BİRİM ÖLÇEK FAKTÖRÜ")
            print("-" * 60)
            print("CATIA API bazen yanlış birim döndürebilir.")
            print("Eğer değerler çok küçükse, çarpan uygulanabilir:")
            print("  • 1     = Değiştirme (mm varsayımı)")
            print("  • 1000  = m → mm dönüşümü")
            print("  • 25.4  = inch → mm dönüşümü")
            print("-" * 60)
            
            scale_input = input("Ölçek çarpanı [Enter=1]: ").strip()
            
            if scale_input == "":
                scale_factor = 1.0
            else:
                try:
                    scale_factor = float(scale_input)
                    print(f"✓ Ölçek faktörü: {scale_factor}x")
                except:
                    scale_factor = 1.0
        else:
            scale_factor = 1.0
        
        # SPA Workbench
        spa_workbench = doc.GetWorkbench("SPAWorkbench")
        
        total_volume = 0
        total_area = 0
        all_cog_x, all_cog_y, all_cog_z = [], [], []
        catia_mass_total = 0
        
        # Bounding box için limit değerler
        global_min_x, global_min_y, global_min_z = float('inf'), float('inf'), float('inf')
        global_max_x, global_max_y, global_max_z = float('-inf'), float('-inf'), float('-inf')
        
        print(f"\n{'='*60}")
        print("BODY ANALİZİ")
        print(f"{'='*60}")
        
        # Her Body'yi işle
        for i in range(1, bodies.Count + 1):
            body = bodies.Item(i)
            body_name = body.Name
            
            print(f"\n📦 Body {i}/{bodies.Count}: {body_name}")
            
            try:
                # Reference oluştur
                ref = part.CreateReferenceFromObject(body)
                measurable = spa_workbench.GetMeasurable(ref)
                
                # Hacim
                try:
                    vol_raw = measurable.Volume
                    vol = vol_raw * (scale_factor ** 3)  # Hacim için küp
                    total_volume += vol
                    print(f"  ✓ Hacim: {vol:,.2f} mm³ (raw: {vol_raw:.4f})")
                except Exception as e:
                    print(f"  ✗ Hacim alınamadı: {e}")
                
                # Alan
                try:
                    area_raw = measurable.Area
                    area = area_raw * (scale_factor ** 2)  # Alan için kare
                    total_area += area
                    print(f"  ✓ Alan: {area:,.2f} mm² (raw: {area_raw:.4f})")
                except Exception as e:
                    print(f"  ✗ Alan alınamadı")
                
                # Kütle (CATIA malzemesi varsa)
                try:
                    mass = measurable.Mass
                    if mass > 0:
                        catia_mass_total += mass
                        print(f"  ✓ CATIA Kütle: {mass:.3f} kg")
                except:
                    pass
                
                # Ağırlık merkezi
                try:
                    cog = (0.0, 0.0, 0.0)
                    cog = measurable.GetCOG(cog)
                    if cog and cog != (0, 0, 0):
                        cog_scaled = (
                            cog[0] * scale_factor,
                            cog[1] * scale_factor,
                            cog[2] * scale_factor
                        )
                        all_cog_x.append(cog_scaled[0])
                        all_cog_y.append(cog_scaled[1])
                        all_cog_z.append(cog_scaled[2])
                        print(f"  ✓ COG: ({cog_scaled[0]:.1f}, {cog_scaled[1]:.1f}, {cog_scaled[2]:.1f})")
                except:
                    pass
                
                # Bu Body'nin bounding box'ı
                try:
                    bbox = [0.0] * 6
                    measurable.GetBoundingBox(bbox)
                    
                    min_x = bbox[0] * scale_factor
                    min_y = bbox[1] * scale_factor
                    min_z = bbox[2] * scale_factor
                    max_x = bbox[3] * scale_factor
                    max_y = bbox[4] * scale_factor
                    max_z = bbox[5] * scale_factor
                    
                    # Global min/max güncelle
                    global_min_x = min(global_min_x, min_x)
                    global_min_y = min(global_min_y, min_y)
                    global_min_z = min(global_min_z, min_z)
                    global_max_x = max(global_max_x, max_x)
                    global_max_y = max(global_max_y, max_y)
                    global_max_z = max(global_max_z, max_z)
                    
                    width = abs(max_x - min_x)
                    depth = abs(max_y - min_y)
                    height = abs(max_z - min_z)
                    
                    print(f"  ✓ Bbox: {width:.1f} × {depth:.1f} × {height:.1f} mm")
                    
                except Exception as e:
                    print(f"  ✗ Bbox alınamadı: {e}")
                
            except Exception as e:
                print(f"  ✗ Body {i} işlenemedi: {e}")
        
        # Sonuçları topla
        print(f"\n{'='*60}")
        print(f"TOPLAM SONUÇLAR")
        print(f"{'='*60}")
        print(f"Toplam Hacim: {total_volume:,.2f} mm³ ({total_volume/1e6:,.2f} L)")
        print(f"Toplam Alan: {total_area:,.2f} mm²")
        
        # Kütle hesaplama
        if catia_mass_total > 0:
            final_mass = catia_mass_total
            mass_source = "CATIA Malzeme"
            print(f"CATIA Kütle: {final_mass:.3f} kg")
        elif total_volume > 0:
            volume_m3 = total_volume / 1e9
            final_mass = volume_m3 * density
            mass_source = "Hesaplanan (Yoğunluk)"
            print(f"Hesaplanan Kütle: {final_mass:.3f} kg (ρ={density} kg/m³)")
        else:
            final_mass = 0
            mass_source = "Hesaplanamadı"
        
        # Ortalama ağırlık merkezi
        if all_cog_x:
            avg_cog = (
                sum(all_cog_x) / len(all_cog_x),
                sum(all_cog_y) / len(all_cog_y),
                sum(all_cog_z) / len(all_cog_z)
            )
        else:
            avg_cog = (0, 0, 0)
        
        # Properties
        properties = {
            'mass': final_mass,
            'volume': total_volume,
            'area': total_area,
            'center_of_gravity': avg_cog,
            'mass_source': mass_source,
            'density_used': density
        }
        
        # Bounding box
        measurements = None
        if global_min_x != float('inf'):
            width = abs(global_max_x - global_min_x)
            depth = abs(global_max_y - global_min_y)
            height = abs(global_max_z - global_min_z)
            
            print(f"Global Bbox: {width:.1f} × {depth:.1f} × {height:.1f} mm")
            
            if width > 0.1 and depth > 0.1 and height > 0.1:
                dimensions = sorted([width, depth, height], reverse=True)
                
                measurements = {
                    'width': dimensions[0],
                    'depth': dimensions[1],
                    'height': dimensions[2],
                    'bbox_raw': (global_min_x, global_min_y, global_min_z, 
                               global_max_x, global_max_y, global_max_z)
                }
                print(f"✓ Boyutlar başarıyla alındı!")
            else:
                print(f"⚠ Bbox değerleri çok küçük!")
        
        print(f"{'='*60}\n")
        
        return measurements, properties
        
    except Exception as e:
        print(f"❌ Ölçüm hatası: {str(e)}")
        import traceback
        traceback.print_exc()
        return None, None

def try_manual_bbox_calculation(part):
    """Manuel bounding box hesaplama - Geometri taraması"""
    try:
        doc = part.Parent
        selection = doc.Selection
        selection.Clear()
        
        # Tüm Part'ı seç
        selection.Add(part)
        
        # CATIA Measure komutunu çağır
        # Tools > Measure > Measure Between
        
        print("\n🔍 Geometri analizi yapılıyor...")
        
        # HybridShapeFactory ile ölçüm kutusu oluştur
        hsf = part.HybridShapeFactory
        
        bodies = part.Bodies
        if bodies.Count == 0:
            return None
        
        # İlk gövdenin referansı
        main_body = bodies.Item(1)
        ref = part.CreateReferenceFromObject(main_body)
        
        # Ekstrem noktaları bul
        # X, Y, Z eksenlerinde minimum ve maksimum
        
        selection.Clear()
        return None
        
    except Exception as e:
        print(f"Manuel bbox hatası: {e}")
        return None

def use_catia_measure_command(catia, part):
    """CATIA'nın Measure komutunu kullan"""
    try:
        print("\n📐 CATIA Measure komutu kullanılıyor...")
        
        doc = part.Parent
        selection = doc.Selection
        selection.Clear()
        
        # Part'ı seç
        bodies = part.Bodies
        main_body = bodies.Item(1)
        selection.Add(main_body)
        
        # Measure Inertia komutunu çalıştır
        # StartCommand "Measure Inertia"
        catia.StartCommand("Measure Inertia")
        
        # Sonuçları okumaya çalış
        # Bu interactive bir komut olduğu için sonuç okuma zor olabilir
        
        selection.Clear()
        return None
        
    except Exception as e:
        print(f"Measure komut hatası: {e}")
        return None

def manual_dimension_input():
    """Kullanıcıdan manuel boyut ve ağırlık girişi"""
    print("\n" + "="*60)
    print("MANUEL ÖLÇÜM GİRİŞİ")
    print("="*60)
    print("Lütfen parçanın bilgilerini girin:")
    print("-"*60)
    
    try:
        width = float(input("En (Genişlik) [mm]: "))
        depth = float(input("Boy (Derinlik) [mm]: "))
        height = float(input("Yükseklik [mm]: "))
        
        use_weight = input("\nAğırlığı biliyor musunuz? (E/H) [H]: ").strip().upper()
        
        mass = 0
        mass_source = "Hesaplanan"
        
        if use_weight == 'E':
            mass_input = input("Ağırlık [kg]: ").strip()
            try:
                mass = float(mass_input)
                mass_source = "Manuel Giriş"
            except:
                pass
        
        dimensions = sorted([width, depth, height], reverse=True)
        
        measurements = {
            'width': dimensions[0],
            'depth': dimensions[1],
            'height': dimensions[2],
            'bbox_raw': (0, 0, 0, dimensions[0], dimensions[1], dimensions[2]),
            'manual': True
        }
        
        # Hacim hesapla
        volume = width * depth * height
        
        properties = {
            'mass': mass,
            'volume': volume,
            'area': 0,
            'center_of_gravity': (0, 0, 0),
            'mass_source': mass_source,
            'density_used': 0,
            'manual': True
        }
        
        return measurements, properties
        
    except:
        print("Geçersiz giriş!")
        return None, None

def set_isometric_view(catia, document):
    """İsometrik görünüm ayarla"""
    try:
        window = catia.ActiveWindow
        active_viewer = window.ActiveViewer
        viewpoint = active_viewer.Viewpoint3D
        
        # Fit All
        active_viewer.Reframe()
        
        # İsometrik görünüm
        origin = (0.0, 0.0, 0.0)
        sight = (1.0, 1.0, 1.0)
        up = (0.0, 0.0, 1.0)
        
        viewpoint.PutOrigin(origin)
        viewpoint.PutSightDirection(sight)
        viewpoint.PutUpDirection(up)
        
        active_viewer.Reframe()
        active_viewer.Update()
        
        return True
        
    except Exception as e:
        print(f"İsometrik görünüm hatası: {str(e)}")
        return False

def capture_screen(catia, output_path, part_name):
    """Ekran görüntüsü al"""
    try:
        window = catia.ActiveWindow
        active_viewer = window.ActiveViewer
        
        # Temiz dosya adı
        clean_name = "".join(c for c in part_name if c.isalnum() or c in (' ', '-', '_'))
        clean_name = clean_name.replace('.CATPart', '').strip()
        
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"{clean_name}_{timestamp}.png"
        full_path = os.path.join(output_path, filename)
        
        active_viewer.Reframe()
        active_viewer.Update()
        active_viewer.CaptureToFile(0, full_path)
        
        print(f"Görüntü kaydedildi: {full_path}")
        return full_path
        
    except Exception as e:
        print(f"Ekran görüntüsü hatası: {str(e)}")
        return None

def print_measurements(measurements, properties, part_name):
    """Ölçüm sonuçlarını yazdır"""
    print("\n" + "="*60)
    print(f"PARÇA ÖLÇÜM RAPORU")
    print("="*60)
    print(f"Dosya: {part_name}")
    print(f"Tarih: {datetime.now().strftime('%d/%m/%Y %H:%M:%S')}")
    print("="*60)
    
    if measurements:
        is_manual = measurements.get('manual', False)
        
        print("\n📏 BOYUTLAR (mm):")
        
        if is_manual:
            print("  ℹ️  Not: Manuel girilen boyutlar")
        
        print(f"  En (Genişlik)    : {measurements['width']:.2f} mm")
        print(f"  Boy (Derinlik)   : {measurements['depth']:.2f} mm")
        print(f"  Yükseklik        : {measurements['height']:.2f} mm")
        
        if not is_manual:
            bbox = measurements['bbox_raw']
            print(f"\n  Bounding Box Koordinatları:")
            print(f"    X: {bbox[0]:.2f} → {bbox[3]:.2f} (Δ={bbox[3]-bbox[0]:.2f})")
            print(f"    Y: {bbox[1]:.2f} → {bbox[4]:.2f} (Δ={bbox[4]-bbox[1]:.2f})")
            print(f"    Z: {bbox[2]:.2f} → {bbox[5]:.2f} (Δ={bbox[5]-bbox[2]:.2f})")
    else:
        print("\n⚠ Boyutlar alınamadı!")
    
    if properties:
        is_manual = properties.get('manual', False)
        
        print("\n⚖️  KÜTLE ÖZELLİKLERİ:")
        
        mass_kg = properties.get('mass', 0)
        volume_mm3 = properties.get('volume', 0)
        
        if mass_kg > 0:
            print(f"  Kütle            : {mass_kg:.3f} kg ({mass_kg*1000:.2f} g)")
        
        if volume_mm3 > 0:
            print(f"  Hacim            : {volume_mm3:.2f} mm³")
            print(f"                     {volume_mm3/1000:.2f} cm³")
            print(f"                     {volume_mm3/1e6:.4f} m³")
        
        if properties.get('area', 0) > 0:
            print(f"  Yüzey Alanı      : {properties['area']:.2f} mm²")
        
        if not is_manual and properties.get('density_used', 0) > 0:
            print(f"  Yoğunluk         : {properties['density_used']} kg/m³")
        
        print(f"  Kaynak           : {properties['mass_source']}")
        
        cog = properties.get('center_of_gravity', (0, 0, 0))
        if cog != (0, 0, 0):
            print(f"  Ağırlık Merkezi  : X={cog[0]:.2f}, Y={cog[1]:.2f}, Z={cog[2]:.2f} mm")
    else:
        print("\n⚠ Kütle bilgisi yok!")
    
    print("="*60 + "\n")

def main():
    """Ana fonksiyon"""
    catia = get_catia()
    if not catia:
        return
    
    try:
        doc = catia.ActiveDocument
        part = doc.Part
        part_name = doc.Name
    except:
        print("Aktif CATIA Part dokümanı bulunamadı!")
        print("Lütfen bir .CATPart dosyası açık olduğundan emin olun.")
        return
    
    print(f"\n🔍 Analiz ediliyor: {part_name}\n")
    
    # Kullanıcıya seçenek sun
    print("="*60)
    print("ÖLÇÜM YÖNTEMİ SEÇİMİ")
    print("="*60)
    print("1. Otomatik CATIA ölçümü (deneysel)")
    print("2. Manuel boyut ve ağırlık girişi (önerilen)")
    print("="*60)
    print("ℹ️  Not: STEP dosyalarında genellikle manuel giriş daha doğru")
    print("   sonuç verir. Otomatik ölçüm başarısız olursa manuel giriş")
    print("   yapmanız istenecektir.")
    print("="*60)
    
    choice = input("\nSeçiminiz (1/2) [2]: ").strip()
    
    measurements = None
    properties = None
    
    if choice == "1":
        # Yoğunluk al
        print("\n📊 MALZEME YOĞUNLUĞU")
        print("-" * 40)
        print("Yaygın malzemeler:")
        print("  • Alüminyum (6061, 7075): 2710 kg/m³")
        print("  • Çelik (Mild Steel): 7850 kg/m³")
        print("  • Paslanmaz Çelik: 8000 kg/m³")
        print("  • Bakır: 8960 kg/m³")
        print("  • Titanyum: 4500 kg/m³")
        print("  • Plastik (ABS): 1040 kg/m³")
        print("-" * 40)
        
        density_input = input("Yoğunluk (kg/m³) [Enter=2710]: ").strip()
        
        if density_input == "":
            density = 2710
        else:
            try:
                density = float(density_input)
            except:
                density = 2710
        
        print(f"✓ Yoğunluk: {density} kg/m³\n")
        
        # Otomatik ölçüm dene
        print("📐 Otomatik ölçüm deneniyor...")
        measurements, properties = get_all_bodies_measurements(part, density)
        
        # Şüpheli sonuç kontrolü
        is_suspicious = False
        if not measurements:
            is_suspicious = True
            print("⚠ Bounding box alınamadı!")
        
        if properties:
            volume = properties.get('volume', 0)
            if volume < 1000:  # 1000 mm³'den küçükse şüpheli
                is_suspicious = True
                print(f"⚠ Şüpheli hacim değeri: {volume:.2f} mm³ (çok küçük)")
        else:
            is_suspicious = True
            print("⚠ Kütle özellikleri alınamadı!")
        
        if is_suspicious:
            print("\n" + "="*60)
            print("⚠️  UYARI: Otomatik ölçüm şüpheli sonuçlar verdi!")
            print("="*60)
            print("Bu durum şu nedenlerle oluşabilir:")
            print("  • STEP dosyası düzgün import edilmemiş")
            print("  • Geometri çok küçük veya hatalı")
            print("  • CATIA birim ayarları yanlış")
            print("  • COM API uyumluluk sorunu")
            print("="*60)
            
            retry = input("\nManuel girişe geçmek ister misiniz? (E/H) [E]: ").strip().upper()
            
            if retry != 'H':
                measurements, properties = manual_dimension_input()
    else:
        # Manuel giriş
        measurements, properties = manual_dimension_input()
        
        # Eğer kütle girilmemişse ve hacim varsa, yoğunluktan hesapla
        if properties and properties.get('mass', 0) == 0 and properties.get('volume', 0) > 0:
            print("\n📊 MALZEME YOĞUNLUĞU")
            print("-" * 40)
            print("  • Alüminyum: 2710 kg/m³")
            print("  • Çelik: 7850 kg/m³")
            print("  • Paslanmaz: 8000 kg/m³")
            print("-" * 40)
            
            density_input = input("Yoğunluk (kg/m³) [2710]: ").strip()
            density = 2710 if density_input == "" else float(density_input)
            
            volume_m3 = properties['volume'] / 1e9
            properties['mass'] = volume_m3 * density
            properties['density_used'] = density
            properties['mass_source'] = "Hesaplanan"
    
    # Sonuçları yazdır
    if measurements or properties:
        print_measurements(measurements, properties, part_name)
    else:
        print("\n⚠️  Hiçbir ölçüm alınamadı!")
        print("Program sonlandırılıyor...\n")
        return
    
    # İsometrik görünüm
    print("📷 İsometrik görünüm ayarlanıyor...")
    if set_isometric_view(catia, doc):
        print("✓ İsometrik görünüm ayarlandı")
        
        output_path = os.path.join(os.path.expanduser("~"), "Desktop")
        print(f"📸 Görüntü kaydediliyor...")
        image_path = capture_screen(catia, output_path, part_name)
        
        if image_path:
            print(f"\n✅ İŞLEM TAMAMLANDI!")
            print(f"📊 Rapor konsola yazdırıldı")
            print(f"🖼️  Görüntü: {os.path.basename(image_path)}")
        else:
            print("⚠ Görüntü kaydedilemedi!")
    else:
        print("⚠ İsometrik görünüm ayarlanamadı!")

if __name__ == "__main__":
    main()