# -*- coding: utf-8 -*-
# ***************************************************************************
# *   Copyright (c) 2020 sliptonic <shopinthewoods@gmail.com>               *
# *                                                                         *
# *   This program is free software; you can redistribute it and/or modify  *
# *   it under the terms of the GNU Lesser General Public License (LGPL)    *
# *   as published by the Free Software Foundation; either version 2 of     *
# *   the License, or (at your option) any later version.                   *
# *   for detail see the LICENCE text file.                                 *
# *                                                                         *
# *   This program is distributed in the hope that it will be useful,       *
# *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
# *   GNU Library General Public License for more details.                  *
# *                                                                         *
# *   You should have received a copy of the GNU Library General Public     *
# *   License along with this program; if not, write to the Free Software   *
# *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
# *   USA                                                                   *
# *                                                                         *
# ***************************************************************************

from PySide import QtCore, QtGui
import FreeCAD
import FreeCADGui
import PathScripts.PathIconViewProvider as PathIconViewProvider
import PathScripts.PathLog as PathLog
import PathScripts.PathPropertyBag as PathPropertyBag
import PathScripts.PathPropertyEditor as PathPropertyEditor
import PathScripts.PathUtil as PathUtil
import re


__title__ = "Property Bag Editor"
__author__ = "sliptonic (Brad Collette)"
__url__ = "https://www.freecadweb.org"
__doc__ = "Task panel editor for a PropertyBag"

if False:
    PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
    PathLog.trackModule(PathLog.thisModule())
else:
    PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())

translate = FreeCAD.Qt.translate


class ViewProvider(object):
    """ViewProvider for a PropertyBag.
    It's sole job is to provide an icon and invoke the TaskPanel on edit."""

    def __init__(self, vobj, name):
        PathLog.track(name)
        vobj.Proxy = self
        self.icon = name
        # mode = 2
        self.obj = None
        self.vobj = None

    def attach(self, vobj):
        PathLog.track()
        self.vobj = vobj
        self.obj = vobj.Object

    def getIcon(self):
        return ":/icons/Path-SetupSheet.svg"

    def __getstate__(self):
        return None

    def __setstate__(self, state):
        return None

    def getDisplayMode(self, mode):
        return "Default"

    def setEdit(self, vobj, mode=0):
        PathLog.track()
        taskPanel = TaskPanel(vobj)
        FreeCADGui.Control.closeDialog()
        FreeCADGui.Control.showDialog(taskPanel)
        taskPanel.setupUi()
        return True

    def unsetEdit(self, vobj, mode):
        FreeCADGui.Control.closeDialog()
        return

    def claimChildren(self):
        return []

    def doubleClicked(self, vobj):
        self.setEdit(vobj)


class Delegate(QtGui.QStyledItemDelegate):
    RoleObject = QtCore.Qt.UserRole + 1
    RoleProperty = QtCore.Qt.UserRole + 2
    RoleEditor = QtCore.Qt.UserRole + 3

    # def paint(self, painter, option, index):
    #    #PathLog.track(index.column(), type(option))

    def createEditor(self, parent, option, index):
        editor = PathPropertyEditor.Editor(
            index.data(self.RoleObject), index.data(self.RoleProperty)
        )
        index.model().setData(index, editor, self.RoleEditor)
        return editor.widget(parent)

    def setEditorData(self, widget, index):
        PathLog.track(index.row(), index.column())
        index.data(self.RoleEditor).setEditorData(widget)

    def setModelData(self, widget, model, index):
        PathLog.track(index.row(), index.column())
        editor = index.data(self.RoleEditor)
        editor.setModelData(widget)
        index.model().setData(index, editor.displayString(), QtCore.Qt.DisplayRole)

    def updateEditorGeometry(self, widget, option, index):
        widget.setGeometry(option.rect)


class PropertyCreate(object):
    def __init__(self, obj, grp, typ, another):
        self.obj = obj
        self.form = FreeCADGui.PySideUic.loadUi(":panels/PropertyCreate.ui")

        obj.Proxy.refreshCustomPropertyGroups()
        for g in sorted(obj.CustomPropertyGroups):
            self.form.propertyGroup.addItem(g)
        if grp:
            self.form.propertyGroup.setCurrentText(grp)

        for t in sorted(PathPropertyBag.SupportedPropertyType):
            self.form.propertyType.addItem(t)
            if PathPropertyBag.SupportedPropertyType[t] == typ:
                typ = t
        if typ:
            self.form.propertyType.setCurrentText(typ)
        else:
            self.form.propertyType.setCurrentText("String")
        self.form.createAnother.setChecked(another)

        self.form.propertyGroup.currentTextChanged.connect(self.updateUI)
        self.form.propertyGroup.currentIndexChanged.connect(self.updateUI)
        self.form.propertyName.textChanged.connect(self.updateUI)
        self.form.propertyType.currentIndexChanged.connect(self.updateUI)
        self.form.propertyEnum.textChanged.connect(self.updateUI)

    def updateUI(self):

        typeSet = True
        if self.propertyIsEnumeration():
            self.form.labelEnum.setEnabled(True)
            self.form.propertyEnum.setEnabled(True)
            typeSet = self.form.propertyEnum.text().strip() != ""
        else:
            self.form.labelEnum.setEnabled(False)
            self.form.propertyEnum.setEnabled(False)
            if self.form.propertyEnum.text().strip():
                self.form.propertyEnum.setText("")

        ok = self.form.buttonBox.button(QtGui.QDialogButtonBox.Ok)

        if not re.match("^[A-Za-z0-9_]*$", self.form.propertyName.text()):
            typeSet = False

        if typeSet and self.propertyGroup():
            ok.setEnabled(True)
        else:
            ok.setEnabled(False)

    def propertyName(self):
        return self.form.propertyName.text().strip()

    def propertyGroup(self):
        return self.form.propertyGroup.currentText().strip()

    def propertyType(self):
        return PathPropertyBag.SupportedPropertyType[
            self.form.propertyType.currentText()
        ].strip()

    def propertyInfo(self):
        return self.form.propertyInfo.toPlainText().strip()

    def createAnother(self):
        return self.form.createAnother.isChecked()

    def propertyEnumerations(self):
        return [s.strip() for s in self.form.propertyEnum.text().strip().split(",")]

    def propertyIsEnumeration(self):
        return self.propertyType() == "App::PropertyEnumeration"

    def exec_(self, name):
        if name:
            # property exists - this is an edit operation
            self.form.propertyName.setText(name)
            if self.propertyIsEnumeration():
                self.form.propertyEnum.setText(
                    ",".join(self.obj.getEnumerationsOfProperty(name))
                )
            self.form.propertyInfo.setText(self.obj.getDocumentationOfProperty(name))

            self.form.labelName.setEnabled(False)
            self.form.propertyName.setEnabled(False)
            self.form.labelType.setEnabled(False)
            self.form.propertyType.setEnabled(False)
            self.form.createAnother.setEnabled(False)

        else:
            self.form.propertyName.setText("")
            self.form.propertyInfo.setText("")
            self.form.propertyEnum.setText("")
            # self.form.propertyName.setFocus()

        self.updateUI()

        return self.form.exec_()


class TaskPanel(object):
    ColumnName = 0
    # ColumnType = 1
    ColumnVal = 1
    # TableHeaders = ['Property', 'Type', 'Value']
    TableHeaders = ["Property", "Value"]

    def __init__(self, vobj):
        self.obj = vobj.Object
        self.props = sorted(self.obj.Proxy.getCustomProperties())
        self.form = FreeCADGui.PySideUic.loadUi(":panels/PropertyBag.ui")

        # initialized later
        self.model = None
        self.delegate = None
        FreeCAD.ActiveDocument.openTransaction("Edit PropertyBag")

    def updateData(self, topLeft, bottomRight):
        pass

    def _setupProperty(self, i, name):
        typ = PathPropertyBag.getPropertyTypeName(self.obj.getTypeIdOfProperty(name))
        val = PathUtil.getPropertyValueString(self.obj, name)
        info = self.obj.getDocumentationOfProperty(name)

        self.model.setData(
            self.model.index(i, self.ColumnName), name, QtCore.Qt.EditRole
        )
        # self.model.setData(self.model.index(i, self.ColumnType), typ,       QtCore.Qt.EditRole)
        self.model.setData(
            self.model.index(i, self.ColumnVal), self.obj, Delegate.RoleObject
        )
        self.model.setData(
            self.model.index(i, self.ColumnVal), name, Delegate.RoleProperty
        )
        self.model.setData(
            self.model.index(i, self.ColumnVal), val, QtCore.Qt.DisplayRole
        )

        self.model.setData(
            self.model.index(i, self.ColumnName), typ, QtCore.Qt.ToolTipRole
        )
        # self.model.setData(self.model.index(i, self.ColumnType), info,      QtCore.Qt.ToolTipRole)
        self.model.setData(
            self.model.index(i, self.ColumnVal), info, QtCore.Qt.ToolTipRole
        )

        self.model.item(i, self.ColumnName).setEditable(False)
        # self.model.item(i, self.ColumnType).setEditable(False)

    def setupUi(self):
        PathLog.track()

        self.delegate = Delegate(self.form)
        self.model = QtGui.QStandardItemModel(
            len(self.props), len(self.TableHeaders), self.form
        )
        self.model.setHorizontalHeaderLabels(self.TableHeaders)

        for i, name in enumerate(self.props):
            self._setupProperty(i, name)

        self.form.table.setModel(self.model)
        self.form.table.setItemDelegateForColumn(self.ColumnVal, self.delegate)
        self.form.table.resizeColumnsToContents()

        self.model.dataChanged.connect(self.updateData)
        self.form.table.selectionModel().selectionChanged.connect(self.propertySelected)
        self.form.add.clicked.connect(self.propertyAdd)
        self.form.remove.clicked.connect(self.propertyRemove)
        self.form.modify.clicked.connect(self.propertyModify)
        self.form.table.doubleClicked.connect(self.propertyModifyIndex)
        self.propertySelected([])

    def accept(self):
        FreeCAD.ActiveDocument.commitTransaction()
        FreeCADGui.ActiveDocument.resetEdit()
        FreeCADGui.Control.closeDialog()
        FreeCAD.ActiveDocument.recompute()

    def reject(self):
        FreeCAD.ActiveDocument.abortTransaction()
        FreeCADGui.Control.closeDialog()
        FreeCAD.ActiveDocument.recompute()

    def propertySelected(self, selection):
        PathLog.track()
        if selection:
            self.form.modify.setEnabled(True)
            self.form.remove.setEnabled(True)
        else:
            self.form.modify.setEnabled(False)
            self.form.remove.setEnabled(False)

    def addCustomProperty(self, obj, dialog):
        name = dialog.propertyName()
        typ = dialog.propertyType()
        grp = dialog.propertyGroup()
        info = dialog.propertyInfo()
        propname = self.obj.Proxy.addCustomProperty(typ, name, grp, info)
        if dialog.propertyIsEnumeration():
            setattr(self.obj, name, dialog.propertyEnumerations())
        return (propname, info)

    def propertyAdd(self):
        PathLog.track()
        more = False
        grp = None
        typ = None
        while True:
            dialog = PropertyCreate(self.obj, grp, typ, more)
            if dialog.exec_(None):
                # if we block signals the view doesn't get updated, surprise, surprise
                # self.model.blockSignals(True)
                name, info = self.addCustomProperty(self.obj, dialog)
                index = 0
                for i in range(self.model.rowCount()):
                    index = i
                    if (
                        self.model.item(i, self.ColumnName).data(QtCore.Qt.EditRole)
                        > dialog.propertyName()
                    ):
                        break
                self.model.insertRows(index, 1)
                self._setupProperty(index, name)
                self.form.table.selectionModel().setCurrentIndex(
                    self.model.index(index, 0), QtCore.QItemSelectionModel.Rows
                )
                # self.model.blockSignals(False)
                more = dialog.createAnother()
            else:
                more = False
            if not more:
                break

    def propertyModifyIndex(self, index):
        PathLog.track(index.row(), index.column())
        row = index.row()

        obj = self.model.item(row, self.ColumnVal).data(Delegate.RoleObject)
        nam = self.model.item(row, self.ColumnVal).data(Delegate.RoleProperty)
        grp = obj.getGroupOfProperty(nam)
        typ = obj.getTypeIdOfProperty(nam)

        dialog = PropertyCreate(self.obj, grp, typ, False)
        if dialog.exec_(nam):
            val = getattr(obj, nam)
            obj.removeProperty(nam)
            name, info = self.addCustomProperty(self.obj, dialog)
            try:
                setattr(obj, nam, val)
            except Exception:
                # this can happen if the old enumeration value doesn't exist anymore
                pass
            newVal = PathUtil.getPropertyValueString(obj, nam)
            self.model.setData(
                self.model.index(row, self.ColumnVal), newVal, QtCore.Qt.DisplayRole
            )

            # self.model.setData(self.model.index(row, self.ColumnType), info, QtCore.Qt.ToolTipRole)
            self.model.setData(
                self.model.index(row, self.ColumnVal), info, QtCore.Qt.ToolTipRole
            )

    def propertyModify(self):
        PathLog.track()
        rows = []
        for index in self.form.table.selectionModel().selectedIndexes():
            row = index.row()
            if row in rows:
                continue
            rows.append(row)

            self.propertyModifyIndex(index)

    def propertyRemove(self):
        PathLog.track()
        # first find all rows which need to be removed
        rows = []
        for index in self.form.table.selectionModel().selectedIndexes():
            if not index.row() in rows:
                rows.append(index.row())

        # then remove them in reverse order so the indexes of the remaining rows
        # to delete are still valid
        for row in reversed(sorted(rows)):
            self.obj.removeProperty(self.model.item(row).data(QtCore.Qt.EditRole))
            self.model.removeRow(row)


def Create(name="PropertyBag"):
    """Create(name = 'PropertyBag') ... creates a new setup sheet"""
    FreeCAD.ActiveDocument.openTransaction("Create PropertyBag")
    pcont = PathPropertyBag.Create(name)
    PathIconViewProvider.Attach(pcont.ViewObject, name)
    return pcont


PathIconViewProvider.RegisterViewProvider("PropertyBag", ViewProvider)


class PropertyBagCreateCommand(object):
    """Command to create a property container object"""

    def __init__(self):
        pass

    def GetResources(self):
        return {
            "MenuText": translate("Path_PropertyBag", "PropertyBag"),
            "ToolTip": translate(
                "Path_PropertyBag",
                "Creates an object which can be used to store reference properties.",
            ),
        }

    def IsActive(self):
        return not FreeCAD.ActiveDocument is None

    def Activated(self):
        sel = FreeCADGui.Selection.getSelectionEx()
        obj = Create()
        body = None
        if sel:
            if "PartDesign::Body" == sel[0].Object.TypeId:
                body = sel[0].Object
            elif hasattr(sel[0].Object, "getParentGeoFeatureGroup"):
                body = sel[0].Object.getParentGeoFeatureGroup()
        if body:
            obj.Label = "Attributes"
            group = body.Group
            group.append(obj)
            body.Group = group


if FreeCAD.GuiUp:
    FreeCADGui.addCommand("Path_PropertyBag", PropertyBagCreateCommand())

FreeCAD.Console.PrintLog("Loading PathPropertyBagGui ... done\n")
