"""
Sarfinet Asset Style Browser - 3ds Max Material Browser
.mat dosyasini okur, PhysicalMaterial ve Multi/Sub-Object materyallerini listeler.
Materyal ekleme, duzenleme ve silme ozelligi icerir.
"""
import sys
try:from pymxs import runtime as rt 
except:pass
if (rt.maxversion())[7] < 2026:
    from PySide2 import QtCore, QtWidgets, QtGui
    from PySide2.QtGui import QPixmap, QPainter
    from PySide2.QtCore import QRect, QThread, Signal, Qt
else:
    from PySide6 import QtCore, QtWidgets, QtGui
    from PySide6.QtGui import QPixmap, QPainter
    from PySide6.QtCore import QRect, QThread, Signal, Qt
import qtmax
import os
import json

json_path = os.path.join(rt.sarfinet.getdir.programs,"SarfinetMaterialBrowser.json")
with open(json_path, "r", encoding="utf-8") as f:
    localizations = json.load(f)

lang = (rt.getinisetting(rt.sarfinet.getdir.root+"/Startup/sarfinet.ini", "settings", "localization"))[1:3]

def tra(key, **kwargs):
    """Aktif dile göre çeviri döner, format argümanlarini uygular."""
    text = localizations.get(lang, localizations["en"]).get(key, key)
    return text.format(**kwargs) if kwargs else text

def gescre():
    app = QtWidgets.QApplication.instance() or QtWidgets.QApplication(sys.argv)
    screen = app.primaryScreen()
    size = screen.availableGeometry()
    dpi = screen.logicalDotsPerInch()
    return size.width(), size.height(), (dpi / 96.0)

screen_w, screen_h, scale = gescre()
iconsize = 80*scale
bitmapsize = 100*scale

def node_event():
    rt.execute("""
    (
        global SarfinetMaterialBrowserCurrent -- sarfinet material browser tarafindan belirlenir
        fn SarfinetMaterialBrowser_necb_fn event_name nodeid = (
            ho = getanimbyhandle nodeid[1]
            if SarfinetMaterialBrowserCurrent != undefined and superclassof SarfinetMaterialBrowserCurrent == material and ho.mat != SarfinetMaterialBrowserCurrent then -- materyal degisimi yapildiginda fonksiyon tekrar calismasin diye
            (
                ho.mat = SarfinetMaterialBrowserCurrent
                SarfinetMaterialBrowserCurrent = undefined
                redrawviews()
            )
        )
        local var = true
        for i in (showNodeEventCallbacks asarray:true) do if i[3] == "SarfinetMaterialBrowser_necb_fn" then var = false
        if var then SarfinetMaterialBrowser_necb = NodeEventCallback mouseUp:true materialStructured:SarfinetMaterialBrowser_necb_fn
    )
    """)

def make_color_icon(mat_type, r, g, b, size=bitmapsize):
    """Verilen RGB renginden kare ikon olusturur."""
    pixmap = QtGui.QPixmap(size, size)
    pixmap.fill(QtGui.QColor(int(r * 255), int(g * 255), int(b * 255), int(255)))
    painter = QtGui.QPainter(pixmap)
    r,g,b,a = (0, 255, 0, 60) if mat_type == "Multimaterial" else (255, 255, 255, 60)
    pen = QtGui.QPen(QtGui.QColor(r,g,b,a))
    pen.setWidth(2)
    painter.setPen(pen)
    painter.drawRect(1, 1, size - 2, size - 2)
    painter.end()
    return QtGui.QIcon(pixmap)

def put_color_from_material(mat):
    """ material uzerinde bitmap tanimli ise rt.bitmaptexture verir yoksa rt.color verir."""
    map_attribute_names = ["base_color_map","base_color_shader"] # physical material, arnold surface material
    color_attribute_names = ["base_color"]
    for i in map_attribute_names: # kaplama varsa
        if hasattr(mat, i) and mat.getmxsprop(i):
            bitmap = mat.getmxsprop(i)
            if bitmap is not None and str(rt.classOf(bitmap)) == "Bitmaptexture":
                return bitmap
    for i in color_attribute_names: # kaplama yoksa renk al
        if hasattr(mat, i):
            return mat.getmxsprop(i)
    return
    
def get_mat_icon(mat, thumb_dir, size=bitmapsize):
    """
    1. base_color_map varsa thumbnail dosyasi ara / olustur.
    2. Yoksa base_color renginden ikon uret.
    3. renk bilgisi de yoksa siyah ikon uret.
    """
    mat_type = str(rt.classOf(mat))
    mat1 = mat
    edgebandcolor = None
    if mat_type == "Multimaterial":
        mat1 = mat[0]
        edgebandcolor = put_color_from_material(mat[2])
    basecolor = put_color_from_material(mat1)

    icon = None
    icon = _icon_from_colormap(mat, basecolor, edgebandcolor, size)
    if icon:
        return icon
    else:
        return make_color_icon(mat_type,0.0, 0.0, 0.0, size)
    
def _icon_from_colormap(mat, basecolor, edgebandcolor, size):
    """ 
    base color ve edgeband kaplamasini tek bir icon uzerinde gosterir
    bu fonksiyon edit material sonrasi tek bir ikonu guncellemek icin kullanilir.
    toplu ikon olusturma islemi qthread ile yapılmaktadir
    """
    # basecolor ve edgebandcolor verileri ayni ise tek bitmap olustur, degilse 2 bitmapi birlestir
    ayni = (
        str(rt.classof(basecolor)) == "Bitmaptexture" and str(rt.classof(edgebandcolor)) == "Bitmaptexture" and
        str(rt.classof(basecolor)) == str(rt.classof(edgebandcolor)) and
        basecolor.filename == edgebandcolor.filename
        )
    if ayni or edgebandcolor == None:
        size2 = int(size) # tam boyut
    else:
        size2 = int(size * 0.80)  # %20 küçült
    
    if str(rt.classof(basecolor)) == "Bitmaptexture": # yuzey kaplamasi
        if os.path.isfile(basecolor.filename):
            pix_bc = QtGui.QPixmap(basecolor.filename)
            pix_bc = pix_bc.scaled(size2, size2,
                QtCore.Qt.IgnoreAspectRatio,
                QtCore.Qt.SmoothTransformation)
        else:
            # yuzey kaplama dosyasi bulunamadiysa "map not found" yaz
            pix_bc = QtGui.QPixmap(size,size)
            pix_bc.fill(QtGui.QColor(0,0,0))
            painter = QtGui.QPainter(pix_bc)
            font = QtGui.QFont("Segoe UI", 8, QtGui.QFont.Bold)
            painter.setFont(font)
            painter.setPen(QtGui.QColor(255, 255, 255, 180))
            painter.drawText(QtCore.QRect(0, 0, size, size), QtCore.Qt.AlignCenter | QtCore.Qt.TextWordWrap, "map not found")
    else:
        # 1. slot base_color_map yok renk olustur
        pix_bc = QtGui.QPixmap(size2, size2)
        col = mat[0].base_color if str(rt.classof(mat)) == "Multimaterial" else mat.base_color
        pix_bc.fill(QtGui.QColor(col.r,col.g,col.b,255))
        
    if str(rt.classof(edgebandcolor)) == "Bitmaptexture": # kenarband kaplamasi
        if os.path.isfile(edgebandcolor.filename):
            pix_eb = QtGui.QPixmap(edgebandcolor.filename)
        else:
            pix_eb = QtGui.QPixmap(size, size)
            # kenarband kaplama dosyasi bulunamadiysa kirmizi renkle goster
            pix_eb.fill(QtGui.QColor(255,0,0,255))
    else:
        # 3. slot base_color_map yok renk olustur
        pix_eb = QtGui.QPixmap(size, size)
        col = mat[2].base_color if str(rt.classof(mat)) == "Multimaterial" else mat.base_color
        pix_eb.fill(QtGui.QColor(col.r,col.g,col.b,255))

    merged = QtGui.QPixmap(size, size)
    painter = QPainter(merged)
    painter.setRenderHint(QPainter.SmoothPixmapTransform)
    if not ayni or edgebandcolor == None:
        # edgebandcolor
        x1 = (size - pix_eb.width()) // 2
        y1 = (size - pix_eb.height()) // 2
        painter.drawPixmap(x1, y1, pix_eb)
    #basecolor
    x2 = (size - pix_bc.width()) // 2
    y2 = (size - pix_bc.height()) // 2
    painter.drawPixmap(x2, y2, pix_bc)

    r,g,b,a = (0, 255, 0, 60) if str(rt.classof(mat)) == "Multimaterial" else (255, 255, 255, 60)
    pen = QtGui.QPen(QtGui.QColor(r,g,b,a))
    pen.setWidth(2)
    painter.setPen(pen) # icon etrafina yesil kenar ciz
    painter.drawRect(1, 1, size - 2, size - 2)
    
    painter.end()
    return merged

class MatIconData:
    """Ana thread'de rt ile toplanan tüm veri."""
    def __init__(self):
        self.mat_type = ""
        self.base_tex_path = ""   # Bitmaptexture ise dosya yolu
        self.base_color = None    # (r, g, b) tuple
        self.base_is_map = False  # True: bitmap, False: renk
        self.eb_tex_path = ""
        self.eb_color = None
        self.eb_is_map = False
        self.has_edgeband = False
        self.same_map = False     # ayni degiskeni

def collect_icon_data(mat):
    """
    Tüm rt çağrılarını ana thread'de yapar, MatIconData döner.
    get_mat_icon islemlerini icerir.
    """
    data = MatIconData()
    data.mat_type = str(rt.classOf(mat))

    mat1 = mat
    eb_source = None

    if data.mat_type == "Multimaterial":
        mat1 = mat[0]
        eb_source = put_color_from_material(mat[2])
        data.has_edgeband = True

    base_source = put_color_from_material(mat1)

    # --- Base color verisi ---
    if base_source is not None and str(rt.classOf(base_source)) == "Bitmaptexture":
        data.base_is_map = True
        data.base_tex_path = str(base_source.filename)
    elif base_source is not None:
        try:
            col = mat1.base_color
            data.base_color = (col.r, col.g, col.b)
        except Exception:
            data.base_color = (0, 0, 0)

    # --- Edgeband verisi ---
    if data.has_edgeband:
        if eb_source is not None and str(rt.classOf(eb_source)) == "Bitmaptexture":
            data.eb_is_map = True
            data.eb_tex_path = str(eb_source.filename)
            # ayni map kontrolü
            data.same_map = (
                data.base_is_map and
                data.base_tex_path == data.eb_tex_path
            )
        elif eb_source is not None:
            try:
                col = mat[2].base_color
                data.eb_color = (col.r, col.g, col.b)
            except Exception:
                data.eb_color = (255, 0, 0)

    return data


# ---------------------------------------------------------------------------
# Materyal Ekleme / Duzenleme Diyalogu
# ---------------------------------------------------------------------------

class MaterialEditDialog(QtWidgets.QDialog):
    def __init__(self, material=None, parent=None):
        super(MaterialEditDialog, self).__init__(parent)
        self.setWindowTitle(tra("edit"))
        self.setMinimumWidth(380)
        self.resize(parent.width()+150,0)
        self.setWindowFlags(QtCore.Qt.Dialog | QtCore.Qt.WindowCloseButtonHint)
        self.add_new = False if material != None else True
        mat1 = rt.physicalMaterial(name="Basecolor", base_color=rt.white)
        mat2 = rt.physicalMaterial(name="Raw", base_color=rt.blue)
        mat3 = rt.physicalMaterial(name="Edgeband", base_color=rt.white)
        self.multimat = rt.Multimaterial(count=3, materiallist=[mat1,mat2,mat3])
        material2 = rt.Copy(material)
        self.temp_mat = material2 if not self.add_new else self.multimat
        self.current_mat = material
        self.map_list = [None] * 3
        self.name_list = [None] * 3
        self.edt_list = [None] * 3
        self.mat_title = [tra("base_color_name"),tra("raw_color_name"),tra("edgeband_color_name")]
        self.color_list = [(255,255,255),(0,0,255),(255,255,255)]
        self.btn_title = ("No Map","No Map","No Map") #("Base Color","Raw Edge","Edge Band")
        self.map_btn_list = [None] * 3
        self.layout = QtWidgets.QVBoxLayout(self)
        self.layout.setSpacing(12)
        self.layout.setContentsMargins(16, 16, 16, 16)
        
        self.mat_type_combo = QtWidgets.QComboBox()
        self.mat_type_combo.setFixedHeight(30*scale)
        self.mat_type_combo.setMinimumWidth(180*scale)
        self.mat_type_combo.addItems(["Multimaterial","PhysicalMaterial"])

        if str(rt.classof(self.temp_mat)) == "Multimaterial": self.mat_type_combo.setCurrentIndex(0)
        else: self.mat_type_combo.setCurrentIndex(1)
        if self.add_new == False: self.mat_type_combo.setEnabled(False)
            
        self.mat_type_combo.currentIndexChanged.connect(self.select_mat_type)
        self.layout.addWidget(self.mat_type_combo)
        
        self.build_ui()
        
    def build_ui(self):
        form = QtWidgets.QFormLayout()
        self.name_edit = QtWidgets.QLineEdit(self.temp_mat.name)
        self.name_edit.textChanged.connect(self.change_name1)
        form.addRow(tra("mat_name"), self.name_edit)
        
        self.layout.addLayout(form)
        rng = range(0,3) if str(rt.classof(self.temp_mat)) == "Multimaterial" else range(0,1) if str(rt.classof(self.temp_mat)) == "PhysicalMaterial" else range(0,0)

        for i in rng:
            if not self.add_new and self.current_mat[i] == None:# alt materyaller silinmis ise yeniden olstur
                self.temp_mat[i] = rt.physicalMaterial()
            if str(rt.classof(self.temp_mat)) == "Multimaterial":
                if (
                    self.temp_mat[i] != None and
                    self.temp_mat[i].base_color_map != None and
                    rt.classof(self.temp_mat[i].base_color_map) == rt.bitmaptexture
                ):
                    self.map_list[i] = self.temp_mat[i].base_color_map.filename
                if self.temp_mat[i] != None:
                    self.color_list[i] = (self.temp_mat[i].base_color.r,self.temp_mat[i].base_color.g,self.temp_mat[i].base_color.b)
                    self.name_list[i] = self.temp_mat[i].name
            elif str(rt.classof(self.temp_mat)) == "PhysicalMaterial":
                if self.temp_mat.base_color_map != None and rt.classof(self.temp_mat.base_color_map) == rt.bitmaptexture:
                    self.map_list[i] = self.temp_mat.base_color_map.filename
                self.color_list[i] = (self.temp_mat.base_color.r,self.temp_mat.base_color.g,self.temp_mat.base_color.b)
            hlayout = QtWidgets.QHBoxLayout(self)
            id_edt = QtWidgets.QLineEdit()
            id_edt.setFixedWidth(120*scale)
            if str(rt.classof(self.temp_mat)) == "Multimaterial":
                id_edt.setText(self.name_list[i])
            # id_edt.setPlaceholderText("")
            id_edt.textChanged.connect(lambda text, idx=i : self.change_name2(text, idx))
            self.edt_list[i] = id_edt
            if str(rt.classof(self.temp_mat)) == "PhysicalMaterial":id_edt.setEnabled(False)
            color_btn = QtWidgets.QPushButton()
            color_btn.setMinimumWidth(20*scale)
            color_btn.setMinimumHeight(20*scale)
            color_btn.setMaximumWidth(20*scale)
            color_btn.setStyleSheet(f"background-color: rgb{(self.color_list[i][0], self.color_list[i][1], self.color_list[i][2])}; border: 1px solid #444;")
            color_btn.setProperty("color_value", (self.color_list[i][0], self.color_list[i][1], self.color_list[i][2]))
            color_btn.clicked.connect(lambda checked=False, idx=i, btn=color_btn : self.get_color(idx, btn))
            map_btn_title = self.btn_title[i] if self.map_list[i] == None else self.map_list[i]
            map_btn = QtWidgets.QPushButton(map_btn_title)
            map_btn.setToolTip(map_btn_title)
            map_btn.clicked.connect(lambda checked=False, idx=i, btn=map_btn: self.get_map(idx, btn))
            #map_btn.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
            self.map_btn_list[i] = map_btn
            clear_btn = QtWidgets.QPushButton("x")
            clear_btn.setMinimumWidth(20*scale)
            clear_btn.setMinimumHeight(20*scale)
            clear_btn.setMaximumWidth(20*scale)
            clear_btn.clicked.connect(lambda checked=False, idx=i, btn=map_btn: self.clear_map(idx, btn))
            hlayout.addWidget(id_edt)
            hlayout.addWidget(color_btn)
            hlayout.addWidget(map_btn)
            hlayout.addWidget(clear_btn)
            form.addRow(self.mat_title[i], hlayout)

        btn_layout = QtWidgets.QHBoxLayout()
        btn_ok = QtWidgets.QPushButton(tra("ok"))
        btn_ok.setFixedHeight(36*scale)
        btn_ok.setDefault(True)
        btn_cancel = QtWidgets.QPushButton(tra("cancel"))
        btn_cancel.setFixedHeight(36*scale)
        btn_ok.clicked.connect(self._on_ok)
        btn_cancel.clicked.connect(self.reject)
        btn_layout.addWidget(btn_ok)
        btn_layout.addWidget(btn_cancel)
        self.layout.addLayout(btn_layout)
        
    def change_name1(self, text):
        self.temp_mat.name = text.strip()

    def change_name2(self, text, i):
        if str(rt.classof(self.temp_mat)) == "Multimaterial":
            self.temp_mat[i].name = text
            self.name_list[i] = text
            
    def select_mat_type(self):
        self.temp_mat = rt.Multimaterial(count=3) if self.mat_type_combo.currentIndex() == 0 else rt.physicalmaterial()
        for i in range(0,3):
            if str(rt.classof(self.temp_mat)) == "PhysicalMaterial":
                self.map_btn_list[i].setEnabled(False)
                self.edt_list[i].setEnabled(False)
                self.clear_map(i, self.map_btn_list[i])
            else:
                self.map_btn_list[i].setEnabled(True)
                self.edt_list[i].setEnabled(True)
        self.map_btn_list[0].setEnabled(True)
        
        if self.name_edit.text() == "Multimaterial":
            self.name_edit.setText("PhysicalMaterial")
        elif self.name_edit.text() == "PhysicalMaterial":
            self.name_edit.setText("Multimaterial")
            
    def clear_map(self, i, btn):
        self.map_list[i] = None
        if str(rt.classof(self.temp_mat)) == "Multimaterial":
            self.temp_mat[i].base_color_map = None
        else:
            self.temp_mat.base_color_map = None
        btn.setText(self.btn_title[i])
        
    def get_map(self, i, btn):
        path, _ = QtWidgets.QFileDialog.getOpenFileName(
            self, tra("select_image"), "", "Picture Files (*.jpg *.jpeg *.png *.bmp *.tif *.tiff)"
        )
        if path:
            btn.setProperty("filename", path)
            bmt = rt.bitmapTexture(filename=path)
            color = rt.color(self.color_list[i][0],self.color_list[i][1],self.color_list[i][2])
            if str(rt.classof(self.temp_mat)) == "Multimaterial":
                dosya_tam_adi = os.path.basename(path) # veritabani.sqlite
                dosya_adi = os.path.splitext(dosya_tam_adi)[0]
                self.temp_mat[i] = rt.physicalMaterial(name=dosya_adi, base_color=color, base_color_map=bmt)
                self.edt_list[i].setText(dosya_adi)
            elif str(rt.classof(self.temp_mat)) == "PhysicalMaterial":
                self.temp_mat = rt.physicalMaterial(name=self.name_edit.text(), base_color=color, base_color_map=bmt)
            self.map_list[i] = path
            btn.setText(path)
        
    def get_color(self, i, btn):
        rgb = btn.property("color_value")
        maxcolor = rt.color(rgb[0],rgb[1],rgb[2])
        color = rt.colorPickerDlg(maxcolor, "Sarfinet")
        if color:
            r, g, b = int(color.r), int(color.g), int(color.b)
            btn.setStyleSheet(f"background-color: rgb({r}, {g}, {b}); border: 1px solid #444;")
            btn.setProperty("color_value", (r, g, b))
            if str(rt.classof(self.temp_mat)) == "Multimaterial":
                self.temp_mat[i].base_color = color
            elif str(rt.classof(self.temp_mat)) == "PhysicalMaterial":
                self.temp_mat.base_color = color
            self.color_list[i] = (color.r,color.g,color.b)
            
    def _on_ok(self):
        new_name = self.name_edit.text().strip() # strip basta ve sonda bosluk varsa siler
        if new_name:
            if not self.add_new:
                self.temp_mat.name = new_name
                self.current_mat.name = new_name
                if str(rt.classof(self.temp_mat)) == "Multimaterial":
                    for i in range(0,3):
                        if self.current_mat[i] == None:
                            self.current_mat[i] = self.temp_mat[i]
                        self.current_mat[i].name = self.temp_mat[i].name
                        self.current_mat[i].base_color = self.temp_mat[i].base_color
                        self.current_mat[i].base_color_map = self.temp_mat[i].base_color_map
                elif str(rt.classof(self.temp_mat)) == "PhysicalMaterial":
                    self.current_mat.base_color = self.temp_mat.base_color
                    self.current_mat.base_color_map = self.temp_mat.base_color_map
            else:
                self.current_mat = self.temp_mat
            self.accept()
        else:
            QtWidgets.QMessageBox.warning(self, "Sarfinet", tra("mat_name_blank_info"))
    def keyPressEvent(self, event):
        # Enter veya Return tuşuna basıldığında fonksiyonu boş dön (ignore et)
        if event.key() == Qt.Key.Key_Return or event.key() == Qt.Key.Key_Enter:
            return


# ---------------------------------------------------------------------------
# Liste
# ---------------------------------------------------------------------------
        
class MaterialListWidget(QtWidgets.QListWidget):
    def __init__(self, parent=None):
        super(MaterialListWidget, self).__init__(parent)
        self.setViewMode(QtWidgets.QListView.IconMode)
        self.setIconSize(QtCore.QSize(iconsize, iconsize))
        # self.setGridSize(QtCore.QSize(150, 150))
        self.setSpacing(8)
        self.setResizeMode(QtWidgets.QListView.Adjust)
        self.setUniformItemSizes(True)
        self.setWordWrap(True)
        self._drag_item = None
        self._drag_start = None
        self._drop_indicator_rect = None
        
    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.LeftButton:
            self._drag_item = self.itemAt(event.pos())
            self._drag_start = event.pos()
        super(MaterialListWidget, self).mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if self._drag_item is None:
            return
        if (event.pos() - self._drag_start).manhattanLength() < QtWidgets.QApplication.startDragDistance():
            return

        # Fare viewport disina çiktiysa sahneye sürükleme yap
        if not self.rect().contains(event.pos()):
            self._drag_item = None
            self._drop_indicator_rect = None
            self.viewport().update()
            self._start_scene_drag()
            return

        target = self.itemAt(event.pos())
        if target and target is not self._drag_item:
            rect = self.visualItemRect(target)
            self._drop_indicator_rect = rect
        else:
            self._drop_indicator_rect = None
        self.viewport().update()

    def _start_scene_drag(self):
        node_event()
        item = self.currentItem()
        if not item: return
        thumb_path = item.data(QtCore.Qt.UserRole + 1)
        mime_data = QtCore.QMimeData()
        url = QtCore.QUrl.fromLocalFile(thumb_path)
        mime_data.setUrls([url])
        mime_data.setText(thumb_path)
        drag = QtGui.QDrag(self)
        drag.setMimeData(mime_data)
        pix = item.icon().pixmap(QtCore.QSize(50, 50))
        drag.setPixmap(pix)
        drag.setHotSpot(QtCore.QPoint(25, 75))
        # nodeeventcallback ile material atama islemini yakalayip secili materiali atar
        if hasattr(rt, "SarfinetMaterialBrowserCurrent"): 
            rt.SarfinetMaterialBrowserCurrent = rt.currentMaterialLibrary[self.currentRow()]
        drag.exec_(QtCore.Qt.CopyAction)
    
    def mouseReleaseEvent(self, event):
        self._drop_indicator_rect = None
        self.viewport().update()
        self.setCursor(QtCore.Qt.ArrowCursor)
        if event.button() == QtCore.Qt.LeftButton and self._drag_item:
            target_item = self.itemAt(event.pos())
            if target_item and target_item is not self._drag_item:
                self._rearrange(self._drag_item, target_item)
        self._drag_item = None
        self._drag_start = None

    def paintEvent(self, event):
        super(MaterialListWidget, self).paintEvent(event)
        if self._drop_indicator_rect is None:
            return
        painter = QtGui.QPainter(self.viewport())
        pen = QtGui.QPen(QtGui.QColor(125, 255, 255))
        pen.setWidth(2)
        painter.setPen(pen)
        rect = self._drop_indicator_rect
        painter.drawLine(rect.left()-4, rect.top(), rect.left()-4, rect.top()+rect.width())
        painter.end()
        
    def _rearrange(self, a, b):
        source_row = self.row(a)
        target_row = self.row(b)

        # --- UI sırası ---
        # a item'inin tüm verisini sakla
        a_icon, a_text = a.icon(), a.text()
        a_d0 = a.data(QtCore.Qt.UserRole)
        a_d1 = a.data(QtCore.Qt.UserRole + 1)
        a_d2 = a.data(QtCore.Qt.UserRole + 2)
        a_tip = a.toolTip()

        # a'yı listeden çıkar, target_row'a ekle
        self.takeItem(source_row)

        new_item = QtWidgets.QListWidgetItem(a_icon, a_text)
        new_item.setData(QtCore.Qt.UserRole,     a_d0)
        new_item.setData(QtCore.Qt.UserRole + 1, a_d1)
        new_item.setData(QtCore.Qt.UserRole + 2, a_d2)
        new_item.setToolTip(a_tip)

        self.insertItem(target_row, new_item)
        self.setCurrentItem(new_item)
        self.doItemsLayout()
        self.update()

        newlib = rt.materiallibrary()
        for i in range(self.count()):
            item = self.item(i)
            mat = item.data(QtCore.Qt.UserRole)
            sira = rt.findItem(rt.currentMaterialLibrary, mat)
            rt.append(newlib, rt.currentMaterialLibrary[sira-1])
        for i in range(newlib.count):
            rt.currentMaterialLibrary[i] = newlib[i]
        
class IconLoaderThread(QThread):
    icon_ready = Signal(int, QtGui.QIcon)
    finished_all = Signal()
    
    def __init__(self, mat_data_list, thumb_dir, size=bitmapsize):
        super(IconLoaderThread, self).__init__()
        # mat_data_list: [(index, MatIconData), ...]
        self.mat_data_list = mat_data_list
        self.thumb_dir = thumb_dir
        self.size = size

    def run(self):
        for index, data in self.mat_data_list:
            try:
                icon = self._build_icon(data)
            except Exception:
                icon = make_color_icon(data.mat_type, 0.0, 0.0, 0.0, self.size)
            self.icon_ready.emit(index, icon)
        self.finished_all.emit()
        

    def _build_icon(self, data):
        size = self.size
        is_multi = data.mat_type == "Multimaterial"

        # --- size2 hesapla (ayni mantigi koru) ---
        if data.has_edgeband:
            same = data.same_map
        else:
            same = True  # edgeband yok, tam boyut
        size2 = size if same else int(size * 0.80)

        # --- Base pixmap ---
        pix_bc = self._load_base(data, size, size2)

        # --- Edgeband pixmap ---
        pix_eb = None
        if data.has_edgeband and not same:
            pix_eb = self._load_edgeband(data, size)

        # --- Birlestir ---
        merged = QtGui.QPixmap(size, size)
        merged.fill(QtGui.QColor(0, 0, 0, 0))
        painter = QtGui.QPainter(merged)
        painter.setRenderHint(QtGui.QPainter.SmoothPixmapTransform)

        if pix_eb is not None:
            x1 = (size - pix_eb.width()) // 2
            y1 = (size - pix_eb.height()) // 2
            painter.drawPixmap(x1, y1, pix_eb)

        if pix_bc is not None:
            x2 = (size - pix_bc.width()) // 2
            y2 = (size - pix_bc.height()) // 2
            painter.drawPixmap(x2, y2, pix_bc)

        # --- Kenar çizgisi ---
        r, g, b, a = (0, 255, 0, 60) if is_multi else (255, 255, 255, 60)
        pen = QtGui.QPen(QtGui.QColor(r, g, b, a))
        pen.setWidth(2)
        painter.setPen(pen)
        painter.drawRect(1, 1, size - 2, size - 2)
        painter.end()

        return QtGui.QIcon(merged)

    def _load_base(self, data, size, size2):
        """Base color pixmap olusturur."""
        if data.base_is_map:
            if os.path.isfile(data.base_tex_path):
                pix = QtGui.QPixmap(data.base_tex_path)
                if not pix.isNull():
                    return pix.scaled(size2, size2,
                                      QtCore.Qt.IgnoreAspectRatio,
                                      QtCore.Qt.SmoothTransformation)
            # map not found
            return self._map_not_found_pixmap(size)
        elif data.base_color:
            r, g, b = data.base_color
            pix = QtGui.QPixmap(size2, size2)
            pix.fill(QtGui.QColor(int(r), int(g), int(b), 255))
            return pix
        return None

    def _load_edgeband(self, data, size):
        """Edgeband pixmap olusturur."""
        if data.eb_is_map:
            if os.path.isfile(data.eb_tex_path):
                pix = QtGui.QPixmap(data.eb_tex_path)
                if not pix.isNull():
                    return pix.scaled(size, size,
                                      QtCore.Qt.IgnoreAspectRatio,
                                      QtCore.Qt.SmoothTransformation)
            # map not found - kirmizi
            pix = QtGui.QPixmap(size, size)
            pix.fill(QtGui.QColor(255, 0, 0, 255))
            return pix
        elif data.eb_color:
            r, g, b = data.eb_color
            pix = QtGui.QPixmap(size, size)
            pix.fill(QtGui.QColor(int(r), int(g), int(b), 255))
            return pix
        return None

    def _map_not_found_pixmap(self, size):
        pixmap = QtGui.QPixmap(size, size)
        pixmap.fill(QtGui.QColor(0, 0, 0))
        painter = QtGui.QPainter(pixmap)
        font = QtGui.QFont("Segoe UI", 8, QtGui.QFont.Bold)
        painter.setFont(font)
        painter.setPen(QtGui.QColor(255, 255, 255, 180))
        painter.drawText(QtCore.QRect(0, 0, size, size),
                         QtCore.Qt.AlignCenter | QtCore.Qt.TextWordWrap,
                         "map not found")
        painter.end()
        return pixmap
        
# ---------------------------------------------------------------------------
# Ana Pencere
# ---------------------------------------------------------------------------

class SarfinetMaterialBrowser(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(SarfinetMaterialBrowser, self).__init__(parent)
        self.setWindowTitle(tra("window_title"))
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose) # pencere kapandiginda pencereyi hafizadan sil
        base_w, base_h = 511, 680
        self.resize(int(base_w * scale), int(base_h * scale))
        self.setWindowFlags(QtCore.Qt.Window)
        self.mat_file_path = rt.getMatLibFileName()
        self.mat_filter = None

        # Thumbnail klasoru
        try:
            self.thumb_dir = rt.sarfinet.getdir.resources + "/MaterialLibrary"
        except Exception:
            self.thumb_dir = os.path.join(os.path.expanduser("~"), "sarfinet_thumbs")
        if not os.path.exists(self.thumb_dir):
            os.makedirs(self.thumb_dir)

        self._build_ui()
        self.populate_library()

    # ------------------------------------------------------------------
    # UI Yapisi
    # ------------------------------------------------------------------

    def _build_ui(self):
        main_layout = QtWidgets.QVBoxLayout(self)
        main_layout.setContentsMargins(10, 10, 10, 8)
        main_layout.setSpacing(8)
        
        # --- Üst Araç Çubugu ---
        toolbar = QtWidgets.QHBoxLayout()
        toolbar.setSpacing(6)

        self.btn_load_file = QtWidgets.QPushButton(tra("load_library"))
        self.btn_load_file.setFixedHeight(32*scale)
        self.btn_load_file.setToolTip(tra("load_library_tooltip"))
        self.btn_load_file.clicked.connect(self.open_mat_file)

        self.btn_reload = QtWidgets.QPushButton(tra("refresh"))
        self.btn_reload.setFixedHeight(32*scale)
        self.btn_reload.setToolTip(tra("reload_material_library"))
        self.btn_reload.clicked.connect(self.populate_library)
        
        self.btn_save = QtWidgets.QPushButton(tra("save_library"))
        self.btn_save.setFixedHeight(32*scale)
        self.btn_save.setToolTip(tra("save_library_tooltip"))
        self.btn_save.clicked.connect(self._save_lib)
        
        self.btn_create_library = QtWidgets.QPushButton(tra("create_mat_library"))
        self.btn_create_library.setFixedHeight(32*scale)
        self.btn_create_library.setToolTip(tra("create_mat_library_tooltip"))
        self.btn_create_library.clicked.connect(self._create_library)
    
        self.lbl_file = QtWidgets.QLabel(tra("current_material_library"))
        self.lbl_file.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)

        toolbar.addWidget(self.btn_load_file)
        toolbar.addWidget(self.btn_reload)
        toolbar.addWidget(self.btn_save)
        toolbar.addWidget(self.btn_create_library)
        
        toolbar.addStretch()
        toolbar.addWidget(self.lbl_file)
        main_layout.addLayout(toolbar)

        # --- Arama + Filtre ---
        filter_layout = QtWidgets.QHBoxLayout()
        filter_layout.setSpacing(6)

        self.search_box = QtWidgets.QLineEdit()
        self.search_box.setPlaceholderText(tra("search_material"))
        self.search_box.setFixedHeight(30*scale)
        self.search_box.textChanged.connect(self._search)

        self.filter_combo = QtWidgets.QComboBox()
        self.filter_combo.setFixedHeight(30*scale)
        self.filter_combo.setMinimumWidth(180*scale)
        self.filter_combo.addItems(["All", "PhysicalMaterial", "Multimaterial"])
        self.filter_combo.currentIndexChanged.connect(self._filter_list)

        filter_layout.addWidget(self.search_box)
        filter_layout.addWidget(self.filter_combo)
        main_layout.addLayout(filter_layout)

        # --- Materyal Listesi ---
        self.mat_list = MaterialListWidget()
        self.mat_list.itemDoubleClicked.connect(self._edit_material)
        self.mat_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.mat_list.customContextMenuRequested.connect(self._show_context_menu)
        main_layout.addWidget(self.mat_list, stretch=1)

        # --- Alt Araç Çubugu ---
        bottom = QtWidgets.QHBoxLayout()
        bottom.setSpacing(6)

        self.lbl_count = QtWidgets.QLabel(tra("mat_count", n=0))
        self.lbl_count.setObjectName("lbl_count") # setstylesheet icin

        self.btn_add = QtWidgets.QPushButton(tra("add"))
        self.btn_add.setObjectName(tra("add"))
        self.btn_add.setFixedHeight(32*scale)
        self.btn_add.setMinimumWidth(90*scale)
        self.btn_add.setToolTip(tra("add"))
        self.btn_add.clicked.connect(self._add_material)

        self.btn_edit = QtWidgets.QPushButton(tra("edit"))
        self.btn_edit.setFixedHeight(32*scale)
        self.btn_edit.setMinimumWidth(90*scale)
        self.btn_edit.setToolTip(tra("edit_tooltip"))
        self.btn_edit.clicked.connect(self._edit_material)

        self.btn_delete = QtWidgets.QPushButton(tra("delete"))
        self.btn_delete.setObjectName("delete")
        self.btn_delete.setFixedHeight(32*scale)
        self.btn_delete.setMinimumWidth(80*scale)
        self.btn_delete.setToolTip(tra("remove_material_from_library"))
        self.btn_delete.clicked.connect(self._delete_material)

        self.btn_apply = QtWidgets.QPushButton(tra("assign_to_selection"))
        self.btn_apply.setFixedHeight(32*scale)
        self.btn_apply.setMinimumWidth(90*scale)
        self.btn_apply.setToolTip(tra("assign_to_selection"))
        self.btn_apply.clicked.connect(self._apply_material)

        bottom.addWidget(self.lbl_count)
        bottom.addStretch()
        bottom.addWidget(self.btn_add)
        bottom.addWidget(self.btn_edit)
        bottom.addWidget(self.btn_delete)
        bottom.addWidget(self.btn_apply)
        main_layout.addLayout(bottom)

    # ------------------------------------------------------------------
    # Kütüphane Yükleme
    # ------------------------------------------------------------------
    
    def populate_library(self):
        self.mat_list.clear()
        self._all_items = []
        count = 0
        mat_index_list = []

        try:
            lib = rt.currentMaterialLibrary
            for i in range(len(lib)):
                mat = lib[i]
                if not mat:
                    continue
                mat_type = str(rt.classOf(mat))
                if self.mat_filter is None or mat_type in self.mat_filter:
                    label = f"{mat.name}\n{mat_type}"
                    placeholder = make_color_icon(mat_type, 0.3, 0.3, 0.3)
                    item = QtWidgets.QListWidgetItem(placeholder, label)
                    item.setData(QtCore.Qt.UserRole,     mat)
                    item.setData(QtCore.Qt.UserRole + 1, self._get_thumb_path(mat))
                    item.setData(QtCore.Qt.UserRole + 2, mat_type)
                    item.setToolTip(f"Type: {mat_type}\nMap Path: {self._get_thumb_path(mat)}")
                    self._all_items.append(item)
                    self.mat_list.addItem(item)

                    # rt cagrilari ana thread'de yap
                    icon_data = collect_icon_data(mat)
                    mat_index_list.append((count, icon_data))
                    count += 1

        except Exception as e:
            print(tra("error_loading_library_info", error=e))

        self.lbl_count.setText(tra("mat_count", n=count))

        self._icon_thread = IconLoaderThread(mat_index_list, self.thumb_dir)
        self._icon_thread.icon_ready.connect(self._on_icon_ready)
        self._icon_thread.finished_all.connect(self._on_icons_loaded)
        self._icon_thread.start()

    def _on_icon_ready(self, index, icon):
        if index < len(self._all_items):
            self._all_items[index].setIcon(icon)

    def _on_icons_loaded(self):
        self.mat_list.update()

    def _get_thumb_path(self, mat):
        """
        PhysicalMaterial için texture yolunu döner.
        surukle birak islemi icin bir resim dosyasi gereklidir
        material'in kendi resim dosyasi yok ise SarfinetTempColor.jpg kullanilir.
        """
        try:
            mat_type = str(rt.classOf(mat))
            if mat_type == "Multimaterial": mat = mat[0]
            bcm = put_color_from_material(mat)
            if str(rt.classof(bcm)) == "Bitmaptexture" and os.path.exists(bcm.filename):
                return str(bcm.filename)
            else:
                # material'in kendi resim dosyasi mevcut degilse
                filepath = rt.getdir(rt.Name("matlib"))+"\SarfinetTempColor.jpg"
                if os.path.exists(filepath):
                    tempbitmap = rt.bitmap(16,16, filename=filepath)
                    return str(tempbitmap.filename)
                else:
                    # SarfinetTempColor.jpg mevcut degilse olustur.(16x16 siyah bir resim dosyasi)
                    tempbitmap = rt.bitmap(16,16, filename=filepath)
                    rt.save(tempbitmap)
                    return str(tempbitmap.filename)
        except Exception as e:
            print(f'thumbpath error: {e}')
            pass
        return ""

    def open_mat_file(self):
        """Dosya diyaloguyla .mat dosyasi sec ve yukle."""
        path, _ = QtWidgets.QFileDialog.getOpenFileName(
            self, "Select .mat File", "", "3ds Max Material Library (*.mat)"
        )
        if path:
            self._load_mat_file(path)

    def _load_mat_file(self, path):
        try:
            rt.loadMaterialLibrary(path)
            self.mat_file_path = path
            short = os.path.basename(path)
            self.lbl_file.setText(f"{short}")
            self.populate_library()
        except Exception as e:
            QtWidgets.QMessageBox.critical(
                self, "Sarfinet", tra("lib_not_loaded_info", error=e)
            )

    def _show_context_menu(self, pos):
        item = self.mat_list.itemAt(pos)
        menu = QtWidgets.QMenu(self)
        # menu.setStyleSheet(STYLE_SHEET)

        act_apply = menu.addAction(tra("assign_to_selection"))
        act_apply.setEnabled(item is not None)
        menu.addSeparator()
        act_add = menu.addAction(tra("add"))
        act_edit = menu.addAction(tra("edit"))
        act_edit.setEnabled(item is not None)
        act_view = menu.addAction(tra("view"))
        act_view.setEnabled(item is not None)
        act_del = menu.addAction(tra("delete"))
        act_del.setEnabled(item is not None)
        menu.addSeparator()
        act_reload = menu.addAction(tra("refresh"))

        action = menu.exec_(self.mat_list.mapToGlobal(pos))
        if action == act_apply:
            self._apply_material()
        elif action == act_add:
            self._add_material()
        elif action == act_edit:
            self._edit_material()
        elif action == act_view:
            self.view_bitmap()
        elif action == act_del:
            self._delete_material()
        elif action == act_reload:
            self.populate_library()

    def view_bitmap(self):
        item = self.mat_list.currentItem()
        index = self.mat_list.currentRow()
        mat = item.data(QtCore.Qt.UserRole)
        thumbdir = item.data(QtCore.Qt.UserRole + 1)
        bm = rt.openBitMap(thumbdir)
        rt.display(bm)

    def _search(self):
        search = self.search_box.text().lower().strip()
        visible = 0
        for item in self._all_items:
            mat = item.data(QtCore.Qt.UserRole)
            item.setHidden(True)
            if search in mat.name.lower():
                item.setHidden(False)
                visible += 1
        self.lbl_count.setText(tra("mat_count", n=visible))

    def _filter_list(self):

        self.mat_filter = None if self.filter_combo.currentIndex() == 0 else self.filter_combo.currentText()
        visible = 0
        for item in self._all_items:
            mat_type = item.data(QtCore.Qt.UserRole + 2)
            item.setHidden(True)
            if self.mat_filter is None or mat_type in self.mat_filter:
                item.setHidden(False)
                visible += 1
        self.lbl_count.setText(tra("mat_count", n=visible))

    def _add_material(self):
        lib = rt.currentMaterialLibrary
        if len(lib) == 0:
            QtWidgets.QMessageBox.warning(
                self, "Sarfinet", "First, load a .mat library."
            )
            return

        dlg = MaterialEditDialog(parent=self)
        if dlg.exec_() == QtWidgets.QDialog.Accepted:
            mat = dlg.current_mat
            rt.append(lib,mat)
            self.populate_library()
            self._status(f"'{mat.name}' {tra('created')}")
            
    def _edit_material(self):
        item = self.mat_list.currentItem()
        index = self.mat_list.currentRow()

        if not item:
            QtWidgets.QMessageBox.information(self, "Sarfinet", tra("select_mat_info"))
            return

        mat = item.data(QtCore.Qt.UserRole)
        dlg = MaterialEditDialog(mat, parent=self)
        if dlg.exec_() == QtWidgets.QDialog.Accepted:
            mat = dlg.current_mat
            rt.currentMaterialLibrary[index] = mat
            mat_type = str(rt.classOf(mat))
            icon = get_mat_icon(mat, self.thumb_dir)
            item.setIcon(icon)
            item.setText(f"{mat.name}\n{mat_type}")
            item.setData(QtCore.Qt.UserRole, mat)
            item.setData(QtCore.Qt.UserRole + 1, self._get_thumb_path(mat))
            item.setData(QtCore.Qt.UserRole + 2, mat_type)  # tip filtresi icin
            item.setToolTip(f"Type: {mat_type}\nMap Path: {self._get_thumb_path(mat)}")
            self._all_items[index] = item
            self._status(f"'{mat.name}' {tra('updated')}")
            rt.redrawViews()

    def _delete_material(self):
        item = self.mat_list.currentItem()
        index = self.mat_list.currentRow()
        if not item:
            QtWidgets.QMessageBox.information(self, "Sarfinet", tra("select_mat_info"))
            return

        mat = item.data(QtCore.Qt.UserRole)
        reply = QtWidgets.QMessageBox.question(
            self, "Sarfinet",
            tra("delete_mat_info", mat=mat.name),
            QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
        )
        if reply != QtWidgets.QMessageBox.Yes:return
        lib = rt.currentmateriallibrary
        if len(lib) == 0:return
            
        rt.deleteItem(lib, (index+1))
        self.populate_library()
        self._status(tra("deleted_info", mat=mat.name))

    def _apply_material(self):
        """Seçili materyali aktif medit slot'una atar."""
        item = self.mat_list.currentItem()
        if not item:return
            
        mat = item.data(QtCore.Qt.UserRole)
        for i in rt.selection:
            i.material = mat
        rt.redrawViews()

    def _save_lib(self):
        path, _ = QtWidgets.QFileDialog.getSaveFileName(
            self, tra("save_library"), "", "3ds Max Material Library (*.mat)"
        )
        if path:
            rt.saveTempMaterialLibrary(rt.currentMaterialLibrary,path)
            self._status(tra("lib_saved_info", lib=os.path.basename(path)))

    def _create_library(self):
        rt.execute("""
        (
            local path1 = getSaveFileName caption:"Save Library" filename:(getMatLibFileName()) types:"3ds Max Material Library (*.mat)"
            if path1 != undefined then
            (
                local arama_klasoru1 = if klasor != undefined then klasor else getinisetting sarfinet.inifile "Directories" "MaterialLibrary"
                local arama_klasoru2 = sarfinet.getdir.resources+"\MaterialLibrary"
                local matfolder = if doesfileexist arama_klasoru1 then arama_klasoru1 else arama_klasoru2
                local matlib = materiallibrary()
                local dosyalar = getfiles (matfolder+"\*")
                local formatlar = (getpropnames (dotnetclass "System.Drawing.Imaging.ImageFormat"))+#("jpg")
                local gecerliformatlar = for i in formatlar collect ("."+(tolower (i as string)))
                local dosyaisimleri = for i in dosyalar 
                where finditem gecerliformatlar (tolower (getfilenametype i)) collect i
                local dosyaisimleri_kucuk = for i in dosyaisimleri collect (tolower (filenamefrompath i))
                for i = 1 to dosyaisimleri.count do
                (
                    bmtex = bitmaptexture filename:dosyaisimleri[i]
                    pmat1 = physicalmaterial name:(getFilenameFile dosyaisimleri[i]) basecolor:white base_color_map:bmtex
                    pmat2 = physicalmaterial name:"raw" basecolor:blue
                    mats = #(pmat1,pmat2,(copy pmat1))
                    multimat = multimaterial name:(getFilenameFile dosyaisimleri[i]) count:3 materiallist:mats
                    append matlib multimat
                )
                local isimler = for i in currentmateriallibrary collect i.name
                for i in isimler do deleteitem currentmateriallibrary i -- gecici material kutuphanesini temizle
                for i in matlib do append currentmateriallibrary i -- gecici material kutuphanesine yeni material ekle
                savetempmateriallibrary currentmateriallibrary path1
            )
        )
        """)
        self.populate_library()
        
    def _set_title_if_open(self):
        """3 saniye sonra gelen komut, pencere kapatildiktan sonra gelirse setwindowtitle komutu hata verir"""
        if self.isVisible():
            self.setWindowTitle(tra("window_title"))

    def _status(self, msg):
        """Durum mesajini baslik cubugunda goster."""
        self.setWindowTitle(f"{tra('window_title')}  —  {msg}")
        QtCore.QTimer.singleShot(3000, self._set_title_if_open)
        
    def closeEvent(self, event):
        """Pencere kapatilirken bu fonksiyon çalisir."""
        rt.execute("""
            for i in (showNodeEventCallbacks asarray:true) do
            (
                if i[3] == "SarfinetMaterialBrowser_necb_fn" then
                (
                    i[1].enabled = false
                    i[1] = undefined
                )
            )
            gc light:true
        """)
        event.accept() 
# ---------------------------------------------------------------------------
# Giris Noktasi
# ---------------------------------------------------------------------------

def mabr():
    max_win = qtmax.GetQMaxMainWindow()
    for child in max_win.findChildren(QtWidgets.QDialog):
        if child.windowTitle() == tra("window_title"):
            child.close()
    ui = SarfinetMaterialBrowser(max_win)
    ui.show()
    return ui

if __name__ == "__main__":
    mabr()