import sys
import os
import json
import inspect
from pathlib import Path
from datetime import datetime

from PyQt6.QtWidgets import (QApplication, QMainWindow, QMessageBox,
                             QFileDialog, QProgressBar, QLabel)
from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot, QUrl, QVariant, QTimer
from PyQt6.QtGui import QMovie, QPixmap
from PyQt6.uic import loadUi
from win32com.client import CDispatch, Dispatch

# Proje modüllerini import et
sys.path.append(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()) + "\\..\\..")))
_current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
_project_root = os.path.dirname(os.path.dirname(os.path.dirname(_current_dir)))
sys.path.insert(0, _project_root)
sys.path.insert(0, _current_dir)

from IFikatia import IFikatia
from IGosterim3d import IGosterim3d


class IFikoCikariciDialog(QMainWindow):
    """
    FikoYT Çıkarıcı Dialog - Minimal & Futuristik

    CATIA dökümanlarından aşağıdaki işlemleri gerçekleştirir:
    - WRL (VRML) formatında 3D dosya dışa aktarma
    - Görüntüyü ekranda gösterme
    - Teknik dokümantasyon oluşturma
    - Ağırlık hesaplama
    """

    def __init__(self, parent=None, flags=None, Catia=None):
        """
        Dialog constructor

        Args:
            parent: Parent widget (None - standalone)
            flags: Qt window flags (Qt.WindowType)
            Catia: CATIA COM Application nesnesi
        """
        # CATIA COM nesnesi kontrolü
        if Catia is None:
            raise ValueError("CATIA parametresi boş olamaz!")

        # PyQt6 initialization (super çağrısından önce)
        if not QApplication.instance():
            self.app = QApplication([sys.argv])
        else:
            self.app = QApplication.instance()

        # Default flags ayarla
        if flags is None:
            flags = Qt.WindowType.Window

        # Parent widget olmamalı (standalone dialog)
        super(IFikoCikariciDialog, self).__init__(None)

        # CATIA COM nesnesi sarmalama
        self.__fikatia = IFikatia(obj=Catia)
        self.__dokuman = self.__fikatia.aktifDokuman

        # Script dosyasının yolu
        self.ScriptYolu = str(Path(__file__).parent)

        # Proje kök dizini
        self.ProjeKlasoru = self.__dokuman.klasoru if self.__dokuman else ""

        # .fikoyt gizli klasörü yolu (henüz oluşturulmamış)
        self.FikoytKlasoru = os.path.join(self.ProjeKlasoru, ".fikoyt")

        # PyQt6 UI dosyasını yükle
        try:
            ui_path = os.path.join(self.ScriptYolu, "formAna.ui")
            self.ui = loadUi(ui_path, self)
        except Exception as e:
            QMessageBox.critical(self, "Hata", f"UI dosyası yüklenemedi: {e}")
            self.close()
            return

        # Pencere özelliklerini ayarla
        self.setWindowFlags(
            Qt.WindowType.Window |
            Qt.WindowType.WindowCloseButtonHint |
            Qt.WindowType.CustomizeWindowHint |
            Qt.WindowType.WindowStaysOnTopHint
        )

        self.setWindowTitle("FikoYT")
        self.setGeometry(100, 100, 300, 320)

        # Loading animasyonu
        self._setup_loading_animation()

        # Signal-slot bağlantıları
        self._connect_signals()

    def _setup_loading_animation(self):
        """Loading GIF animasyonunu ekle"""
        try:
            # Resimler klasöründen GIF dosyasını yükle
            gif_path = os.path.join(self.ScriptYolu, "Resimler", "hiper.gif")
            if hasattr(self.ui, 'labelLoading') and os.path.exists(gif_path):
                movie = QMovie(gif_path)
                self.loading_movie = movie
        except Exception as e:
            print(f"Loading animasyonu yüklenemedi: {e}")

    def _connect_signals(self):
        """PyQt6 signal-slot bağlantılarını kur"""
        # Ana İşlem Butonu
        if hasattr(self.ui, 'btnIslemYap'):
            self.ui.btnIslemYap.clicked.connect(self.islem_yap)

    def _malzeme_yogunluk_al(self):
        """
        ComboBox'tan seçilen malzemenin yoğunluk değerini al

        Returns:
            float: Seçilen malzemenin yoğunluğu (kg/m³)
        """
        # Malzeme-Yoğunluk eşleştirmesi
        malzemeler = {
            "Alm 2710": 2710,
            "Çelik 7850": 7850,
            "P.Çelik 8000": 8000,
            "Bakır 8960": 8960,
            "Titanyum 4500": 4500,
            "ABS 1040": 1040,
            "PLA 1200": 1200,
            "Mg 1800": 1800,
            "Ni 8902": 8902,
            "Pirinç 8470": 8470,
            "Bronz 8700": 8700,
            "Fe 7874": 7874,
            "Al2O3 3950": 3950
        }

        # ComboBox'tan seçilen öğeyi al
        secilen_text = self.ui.comboMalzeme.currentText()
        yogunluk = malzemeler.get(secilen_text, 2710)  # Varsayılan: 2710

        print(f"[DEBUG] Seçilen malzeme: {secilen_text}")
        print(f"[DEBUG] Yoğunluk değeri: {yogunluk} kg/m³")

        return yogunluk

    def _fikoyt_klasoru_kontrol_et(self):
        """
        .fikoyt gizli klasörünü kontrol et ve oluştur
        """
        if not os.path.exists(self.FikoytKlasoru):
            try:
                os.makedirs(self.FikoytKlasoru)
                # Windows'ta gizli klasör yap
                import ctypes
                ctypes.windll.kernel32.SetFileAttributesW(self.FikoytKlasoru, 2)
            except Exception as e:
                QMessageBox.critical(self, "Hata",
                                   f".fikoyt klasörü oluşturulamadı: {e}")
                return False
        return True

    @pyqtSlot()
    def islem_yap(self):
        """
        Tüm işlemleri (WRL, Resim, Dokümantasyon) yapan ana method
        """
        try:
            # 1. Klasör kontrol et
            if not self._fikoyt_klasoru_kontrol_et():
                return

            # 2. WRL kaydet
            print("[DEBUG] WRL kaydediliyor...")
            self.ui.statusBar().showMessage("⏳ WRL kaydediliyor...")
            self.app.processEvents()

            doc_com = self.__dokuman.catNesne
            wrl_dosya = os.path.join(self.FikoytKlasoru, "model.wrl")

            if os.path.exists(wrl_dosya):
                try:
                    os.remove(wrl_dosya)
                except Exception:
                    pass

            doc_com.ExportData(wrl_dosya, "wrl")

            if not os.path.exists(wrl_dosya):
                raise RuntimeError(f"WRL dosyası oluşturulamadı")

            print(f"[DEBUG] ✓ WRL kaydedildi: {wrl_dosya}")

            # 3. Resim çek
            print("[DEBUG] Resim çekiliyor...")
            self.ui.statusBar().showMessage("⏳ Resim çekiliyor...")
            self.app.processEvents()

            gosterim = self.__dokuman.gosterim

            if gosterim is None:
                raise ValueError("Gosterim nesnesi alınamadı!")

            if hasattr(gosterim, 'catNesne'):
                gosterim_com = gosterim.catNesne
            else:
                gosterim_com = gosterim

            jpeg_dosya = os.path.join(self.FikoytKlasoru, "screenshot.jpg")

            pencere = self.__dokuman.pencere

            # Ürün ağacını gizle
            try:
                if pencere and hasattr(pencere, 'catNesne'):
                    pencere.catNesne.SpecificationPanelVisible = False
            except Exception as e:
                print(f"[DEBUG] Ürün ağacı gizleme hatası: {e}")

            try:
                if not isinstance(gosterim, IGosterim3d):
                    gosterim = IGosterim3d(obj=gosterim_com, sahip=self.__dokuman)
                    gosterim.anaProgram = self.__fikatia

                gosterim.resimDosyaya(
                    jpeg_dosya,
                    Format=5,           # JPEG
                    ArkaPlan=[1, 1, 1], # Beyaz
                    Agac=True
                )
            finally:
                # Ürün ağacını geri göster
                try:
                    if pencere and hasattr(pencere, 'catNesne'):
                        pencere.catNesne.SpecificationPanelVisible = True
                except Exception:
                    pass

            if not os.path.exists(jpeg_dosya):
                raise RuntimeError(f"JPEG dosyası oluşturulamadı")

            print(f"[DEBUG] ✓ JPEG kaydedildi: {jpeg_dosya}")

            # 4. Resmi formda göster
            print("[DEBUG] Resim gösteriliyor...")
            self._resim_goster(jpeg_dosya)

            # 5. Ağırlık hesapla
            print("[DEBUG] Ağırlık hesaplanıyor...")
            self.ui.statusBar().showMessage("⏳ Ağırlık hesaplanıyor...")
            self.app.processEvents()

            secilen_yogunluk = self._malzeme_yogunluk_al()
            agirlik = None
            hacim = None

            try:
                # 1. Direkt GetTotalMass() dene
                if hasattr(doc_com, 'GetTotalMass'):
                    try:
                        agirlik = doc_com.GetTotalMass()
                        print(f"[DEBUG] GetTotalMass() = {agirlik}")
                    except Exception as e:
                        print(f"[DEBUG] GetTotalMass() hatası: {e}")

                # 2. Part nesnesi üzerinden ağırlık hesapla
                if not agirlik and hasattr(doc_com, 'Part'):
                    try:
                        part_com = doc_com.Part

                        # SPAWorkbench ile Body hacimlerini topla
                        try:
                            spa_workbench = doc_com.GetWorkbench("SPAWorkbench")
                            hacim = 0.0

                            if hasattr(part_com, 'Bodies'):
                                bodies = part_com.Bodies
                                for i in range(1, bodies.Count + 1):
                                    try:
                                        body = bodies.Item(i)
                                        body_ref = part_com.CreateReferenceFromObject(body)
                                        measurable = spa_workbench.GetMeasurable(body_ref)
                                        body_vol = measurable.Volume
                                        hacim += body_vol
                                        print(f"[DEBUG] Body {i} Volume = {body_vol} m³")
                                    except Exception as e:
                                        print(f"[DEBUG] Body {i} hatası: {e}")

                            if hacim > 0:
                                print(f"[DEBUG] Toplam Volume = {hacim} m³")

                        except Exception as e:
                            print(f"[DEBUG] SPAWorkbench hatası: {e}")
                            if hasattr(part_com, 'Volume'):
                                hacim = part_com.Volume
                                print(f"[DEBUG] part_com.Volume = {hacim}")

                        # Ağırlık hesapla
                        if hacim and secilen_yogunluk:
                            agirlik = hacim * secilen_yogunluk
                            print(f"[DEBUG] Hesaplanan ağırlık = {agirlik} kg")

                    except Exception as e:
                        print(f"[DEBUG] Part işleme hatası: {e}")

            except Exception as e:
                print(f"[ERROR] Ağırlık hesaplama hatası: {e}")
                import traceback
                traceback.print_exc()

            # 6. Ağırlık göster
            self._agirlik_goster(agirlik)

            # 6.5. Ölçüleri hesapla ve göster
            print("[DEBUG] Ölçüler hesaplanıyor...")
            olculer = self._olculer_hesapla_ve_goster()

            # 7. Dosya adı göster
            secilen_text = self.ui.comboMalzeme.currentText()
            malzeme_adi = secilen_text.split(" - ")[0] if " - " in secilen_text else secilen_text

            dokuman_adi = self.__dokuman.ismi
            self.ui.labelDosyaIsmi.setText(f"📁 {dokuman_adi}")

            # 8. Dokümantasyon JSON oluştur
            print("[DEBUG] JSON oluşturuluyor...")
            self.ui.statusBar().showMessage("⏳ Dokümantasyon kaydediliyor...")
            self.app.processEvents()

            dokumantasyon = {
                "olusturma_tarihi": datetime.now().isoformat(),
                "dokuman_adi": dokuman_adi,
                "dokuman_tam_yolu": self.__dokuman.tamIsmi,
                "belge_turu": self.__dokuman.turu,
                "kaydedilmis": self.__dokuman.kaydedilmismi,
                "proje_klasoru": self.ProjeKlasoru,
                "fikoyt_klasoru": self.FikoytKlasoru,
                "agirlik_kg": agirlik,
                "agirlik_hesaplama": {
                    "malzeme": malzeme_adi,
                    "yogunluk_kg_m3": secilen_yogunluk,
                    "hacim_m3": hacim,
                    "birim": "kg",
                    "hesaplama_metodu": "Volume × Density" if hacim and secilen_yogunluk else "GetTotalMass()"
                },
                "olculer_mm": olculer if olculer else {"x": None, "y": None, "z": None},
                "islemler": {
                    "wrl_kayit": os.path.exists(wrl_dosya),
                    "resim_cikti": os.path.exists(jpeg_dosya),
                    "dokumantasyon": True
                }
            }

            json_dosya = os.path.join(self.FikoytKlasoru, "dokumantasyon.json")
            with open(json_dosya, 'w', encoding='utf-8') as f:
                json.dump(dokumantasyon, f, indent=2, ensure_ascii=False)

            print(f"[DEBUG] ✓ JSON kaydedildi: {json_dosya}")

            # 9. Başarılı mesaj
            self.ui.statusBar().showMessage("✅ Tüm işlemler tamamlandı!")

        except Exception as e:
            print(f"[ERROR] {str(e)}")
            import traceback
            traceback.print_exc()
            self.ui.statusBar().showMessage(f"❌ Hata: {str(e)}")
            QMessageBox.critical(self, "Hata", f"İşlem başarısız:\n{str(e)}")

    def _resim_goster(self, jpeg_dosya):
        """
        JPEG resimini formda göster

        Args:
            jpeg_dosya: JPEG dosyasının tam yolu
        """
        try:
            if not os.path.exists(jpeg_dosya):
                print(f"[ERROR] Resim dosyası bulunamadı: {jpeg_dosya}")
                return

            pixmap = QPixmap(jpeg_dosya)
            if pixmap.isNull():
                print(f"[ERROR] Resim yüklenemedi")
                return

            # Resimi label'ın içine sığdır
            label_size = self.ui.labelResim.size()
            scaled_pixmap = pixmap.scaledToWidth(label_size.width() - 10, Qt.TransformationMode.SmoothTransformation)

            self.ui.labelResim.setPixmap(scaled_pixmap)
            print(f"[DEBUG] ✓ Resim gösterildi")

        except Exception as e:
            print(f"[ERROR] Resim gösterme hatası: {e}")

    def _agirlik_goster(self, agirlik):
        """
        Ağırlık değerini formda göster

        Args:
            agirlik: Ağırlık değeri (kg)
        """
        if agirlik:
            agirlik_text = f"⚖️ {agirlik:.2f} kg"
            self.ui.labelAgirlik.setText(agirlik_text)
            print(f"[DEBUG] Ağırlık gösterildi: {agirlik_text}")
        else:
            self.ui.labelAgirlik.setText("⚖️ Ağırlık: -")

    def _olculer_hesapla_ve_goster(self):
        """
        Part'ın gerçek X, Y, Z ölçülerini hesapla (UDF ile - kutukSinirOlculeri mantığı)
        Returns: dict with 'x', 'y', 'z' keys (mm), or None on error
        """
        try:
            from IYardimciSiniflar import SifirKonum, XYZ
            import os

            doc_com = self.__dokuman.catNesne

            if not hasattr(doc_com, 'Part'):
                self.ui.labelOlculer.setText("📐 X:- Y:- Z:-")
                return None

            part_com_obj = doc_com.Part
            bodies = part_com_obj.Bodies

            if bodies.Count == 0:
                self.ui.labelOlculer.setText("📐 X:- Y:- Z:-")
                return None

            # UDF script dosyasının yolunu al
            current_dir = os.path.dirname(__file__)
            idialoglar = os.path.dirname(current_dir)
            fikatia_root = os.path.dirname(idialoglar)
            script_dosya = os.path.join(fikatia_root, "Prg", "FikoDosya", "FikoMfg0.FikoDll")

            print(f"[DEBUG] UDF dosya: {script_dosya}, var: {os.path.exists(script_dosya)}")

            # HybridBody oluştur veya al
            try:
                fiko_body = part_com_obj.hybridBodies.item("FikoMantasyon")
                part_com_obj.InWorkObject = fiko_body
            except:
                fiko_body = part_com_obj.hybridBodies.add()
                fiko_body.name = "FikoMantasyon"

            # İlk body'yi al (COM object olarak)
            body = bodies.Item(1)

            # InstanceFactory ile UDF çağır (kutukSinirOlculeri gibi)
            factory = part_com_obj.GetCustomerFactory("InstanceFactory")
            factory.BeginInstanceFactory("Fiko0", script_dosya)
            factory.BeginInstantiate()

            # Body'nin COM nesnesini gönder (kutukSinirOlculeri gibi)
            factory.PutInputData("ParcaBody", body)

            instance = factory.Instantiate()
            factory.EndInstantiate()
            factory.EndInstanceFactory()

            # UDF'e merkez ve axes gönder (global axes kullan)
            # SPAWorkbench ile COG al
            spa_workbench = doc_com.GetWorkbench("SPAWorkbench")
            body_ref = part_com_obj.CreateReferenceFromObject(body)
            measurable = spa_workbench.GetMeasurable(body_ref)

            # COG al
            cog = (0.0, 0.0, 0.0)
            measurable.GetCOG(cog)
            print(f"[DEBUG] COG: {cog}")

            # Part'ın eksenlerini al (IPart wrapper'ı kullan)
            from IPart import IPart
            part_wrapper = IPart(part_com_obj)
            eksenler = part_wrapper.eksenler

            # Son ekseni bul (son eklenen eksen)
            if eksenler.sayisi > 0:
                son_eksen = eksenler.item(eksenler.sayisi)
                print(f"[DEBUG] Son eksen bulundu: {son_eksen.ismi}")

                # Origin (Merkez - COG)
                instance.GetParameter("O.X").value = cog[0]
                instance.GetParameter("O.Y").value = cog[1]
                instance.GetParameter("O.Z").value = cog[2]

                # Son eksenin X, Y, Z yönlerini al
                # son_eksen.x, son_eksen.y, son_eksen.z vektörleri
                x_dir = son_eksen.x
                y_dir = son_eksen.y
                z_dir = son_eksen.z

                # X axis
                instance.GetParameter("X.X").value = x_dir.X
                instance.GetParameter("X.Y").value = x_dir.Y
                instance.GetParameter("X.Z").value = x_dir.Z

                # Y axis
                instance.GetParameter("Y.X").value = y_dir.X
                instance.GetParameter("Y.Y").value = y_dir.Y
                instance.GetParameter("Y.Z").value = y_dir.Z

                # Z axis
                instance.GetParameter("Z.X").value = z_dir.X
                instance.GetParameter("Z.Y").value = z_dir.Y
                instance.GetParameter("Z.Z").value = z_dir.Z

                print(f"[DEBUG] UDF parametreleri ayarlandı (son eksen kullanıldı)")
            else:
                # Eksen yoksa global axes kullan
                print(f"[DEBUG] Eksen yok, global axes kullanılıyor")
                instance.GetParameter("O.X").value = cog[0]
                instance.GetParameter("O.Y").value = cog[1]
                instance.GetParameter("O.Z").value = cog[2]

                instance.GetParameter("X.X").value = 1.0
                instance.GetParameter("X.Y").value = 0.0
                instance.GetParameter("X.Z").value = 0.0

                instance.GetParameter("Y.X").value = 0.0
                instance.GetParameter("Y.Y").value = 1.0
                instance.GetParameter("Y.Z").value = 0.0

                instance.GetParameter("Z.X").value = 0.0
                instance.GetParameter("Z.Y").value = 0.0
                instance.GetParameter("Z.Z").value = 1.0

            # Part'ı güncelle (UDF parametreleri hesaplansın)
            print(f"[DEBUG] Part güncelleniyor...")
            part_com_obj.Update()

            # Parametreleri oku
            xmin = instance.GetParameter("Xmin").value
            xmax = instance.GetParameter("Xmax").value
            ymin = instance.GetParameter("Ymin").value
            ymax = instance.GetParameter("Ymax").value
            zmin = instance.GetParameter("Zmin").value
            zmax = instance.GetParameter("Zmax").value

            print(f"[DEBUG] UDF Params: Xmin={xmin}, Xmax={xmax}, Ymin={ymin}, Ymax={ymax}, Zmin={zmin}, Zmax={zmax}")

            # SifirKonum ile ölçüleri hesapla
            sifrkonum = SifirKonum(xmin, xmax, ymin, ymax, zmin, zmax)

            x_olcu = sifrkonum.XOlcusu
            y_olcu = sifrkonum.YOlcusu
            z_olcu = sifrkonum.ZOlcusu

            print(f"[DEBUG] Ölçüler: X={x_olcu:.1f}mm, Y={y_olcu:.1f}mm, Z={z_olcu:.1f}mm")

            # UI'ye yazdir
            olculer_text = f"📐 X:{x_olcu:.1f} Y:{y_olcu:.1f} Z:{z_olcu:.1f}"
            self.ui.labelOlculer.setText(olculer_text)

            # Ölçüleri return et (JSON'a eklemek için)
            return {
                "x": round(x_olcu, 1),
                "y": round(y_olcu, 1),
                "z": round(z_olcu, 1)
            }

        except Exception as e:
            print(f"[ERROR] Ölçü hesaplama hatası: {e}")
            import traceback
            traceback.print_exc()
            self.ui.labelOlculer.setText("📐 X:- Y:- Z:-")
            return None

    def closeEvent(self, event):
        """Pencere kapatılırken çağrılır"""
        event.accept()

    def __str__(self):
        return f"IFikoCikariciDialog(dokuman={self.__dokuman.ismi})"


if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setStyle("fusion")

    cizgiYumusatici = IFikoCikariciDialog(Catia=Dispatch("Catia.Application"))
    cizgiYumusatici.show()

    sys.exit(app.exec())
