#!/usr/bin/env python

import sys
from PyQt6.QtCore import (Qt, QAbstractTableModel, QModelIndex, QSortFilterProxyModel)
from PyQt6.QtWidgets import (QApplication, QMainWindow, QTableView, QLineEdit, QVBoxLayout, QWidget)

class CustomTableModel(QAbstractTableModel):
    def __init__(self, data):
        super().__init__()
        self._data = data

    def rowCount(self, parent=QModelIndex()):
        return len(self._data)

    def columnCount(self, parent=QModelIndex()):
        return 2  # Row number and data

    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
        if role == Qt.ItemDataRole.DisplayRole:
            row, col = index.row(), index.column()
            if col == 0:
                return row + 1  # Row number (1-based index)
            elif col == 1:
                return self._data[row]

    def setData(self, index, value, role=Qt.ItemDataRole.EditRole):
        if role == Qt.ItemDataRole.EditRole and index.isValid():
            self._data[index.row()] = value
            self.dataChanged.emit(index, index, [Qt.ItemDataRole.EditRole])
            return True
        return False

    def flags(self, index):
        default_flags = Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled
        if index.isValid():
            return default_flags | Qt.ItemFlag.ItemIsDragEnabled | Qt.ItemFlag.ItemIsDropEnabled
        return default_flags | Qt.ItemFlag.ItemIsDropEnabled

    def removeRow(self, row):
        self.beginRemoveRows(QModelIndex(), row, row)
        self._data.pop(row)
        self.endRemoveRows()

    def insertRow(self, row, value):
        self.beginInsertRows(QModelIndex(), row, row)
        self._data.insert(row, value)
        self.endInsertRows()

    def moveRows(self, sourceParent, sourceRow, count, destinationParent, destinationRow):
        if sourceRow < destinationRow:
            destinationRow -= 1

        self.beginMoveRows(sourceParent, sourceRow, sourceRow, destinationParent, destinationRow)
        row_data = self._data.pop(sourceRow)
        self._data.insert(destinationRow, row_data)
        self.endMoveRows()
        return True

class ProxyModel(QSortFilterProxyModel):
    def __init__(self):
        super().__init__()
        self.filterString = ""

    def setFilterString(self, text):
        self.filterString = text
        self.invalidateFilter()

    def filterAcceptsRow(self, source_row, source_parent):
        if self.filterString:
            data = self.sourceModel().data(self.sourceModel().index(source_row, 1), Qt.ItemDataRole.DisplayRole)
            return self.filterString in str(data)
        return True

class TableView(QTableView):
    def __init__(self, model):
        super().__init__()
        self.setModel(model)
        self.setDragDropMode(QTableView.DragDropMode.InternalMove)
        self.setSelectionBehavior(QTableView.SelectionBehavior.SelectRows)
        self.setSortingEnabled(False)
        self.setDragDropOverwriteMode(False)

    def dropEvent(self, event):
        source_index = self.indexAt(event.pos())
        if not source_index.isValid():
            return

        destination_row = source_index.row()
        dragged_row = self.currentIndex().row()

        if dragged_row != destination_row:
            self.model().sourceModel().moveRows(QModelIndex(), dragged_row, 1, QModelIndex(), destination_row)
        super().dropEvent(event)
        self.model().layoutChanged.emit()  # Refresh model to update row numbers

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.data = ["dog", "hog", "don", "cat", "bat"]

        self.baseModel = CustomTableModel(self.data)
        self.proxyModel = ProxyModel()
        self.proxyModel.setSourceModel(self.baseModel)

        self.view = TableView(self.proxyModel)

        self.filterLineEdit = QLineEdit()
        self.filterLineEdit.setPlaceholderText("Filter by substring")
        self.filterLineEdit.textChanged.connect(self.proxyModel.setFilterString)

        layout = QVBoxLayout()
        layout.addWidget(self.filterLineEdit)
        layout.addWidget(self.view)

        container = QWidget()
        container.setLayout(layout)
        self.setCentralWidget(container)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

