from ngSkinTools2 import signal
from ngSkinTools2.api import PaintMode, PaintModeSettings, flood_weights
from ngSkinTools2.api.log import getLogger
from ngSkinTools2.api.pyside import QAction, QActionGroup, QtWidgets
from ngSkinTools2.api.python_compatibility import Object
from ngSkinTools2.api.session import session
from ngSkinTools2.signal import Signal
from ngSkinTools2.ui import qt, widgets
from ngSkinTools2.ui.layout import TabSetup, createTitledRow
from ngSkinTools2.ui.ui_lock import UiLock

log = getLogger("tab set weights")


def make_presets():
    presets = {m: PaintModeSettings() for m in PaintMode.all()}
    for k, v in presets.items():
        v.mode = k

    presets[PaintMode.smooth].intensity = 0.3
    presets[PaintMode.scale].intensity = 0.3
    presets[PaintMode.add].intensity = 0.1
    presets[PaintMode.scale].intensity = 0.95

    return presets


class Model(Object):
    def __init__(self):
        self.mode_changed = Signal("mode changed")
        self.presets = make_presets()
        self.current_settings = None
        self.set_mode(PaintMode.replace)

    def set_mode(self, mode):
        self.current_settings = self.presets[mode]
        self.mode_changed.emit()

    def apply(self):
        flood_weights(session.state.currentLayer.layer, influences=session.state.currentLayer.layer.paint_targets, settings=self.current_settings)


def build_ui(parent):
    model = Model()
    ui_lock = UiLock()

    def build_mode_settings_group():
        def mode_row():
            row = QtWidgets.QVBoxLayout()

            group = QActionGroup(parent)

            actions = {}

            def create_mode_button(toolbar, mode, label, tooltip):
                a = QAction(label, parent)
                a.setToolTip(tooltip)
                a.setStatusTip(tooltip)
                a.setCheckable(True)
                actions[mode] = a
                group.addAction(a)

                @qt.on(a.toggled)
                @ui_lock.skip_if_updating
                def toggled(checked):
                    if checked:
                        model.set_mode(mode)
                        update_ui()

                toolbar.addAction(a)

            t = QtWidgets.QToolBar()
            create_mode_button(t, PaintMode.replace, "Replace", "")
            create_mode_button(t, PaintMode.add, "Add", "")
            create_mode_button(t, PaintMode.scale, "Scale", "")
            row.addWidget(t)

            t = QtWidgets.QToolBar()
            create_mode_button(t, PaintMode.smooth, "Smooth", "")
            create_mode_button(t, PaintMode.sharpen, "Sharpen", "")
            row.addWidget(t)

            actions[model.current_settings.mode].setChecked(True)

            return row

        influences_limit = widgets.NumberSliderGroup(value_type=int, min_value=0, max_value=10)

        @signal.on(influences_limit.valueChanged)
        @ui_lock.skip_if_updating
        def influences_limit_changed():
            for _, v in model.presets.items():
                v.influences_limit = influences_limit.value()
            update_ui()

        intensity = widgets.NumberSliderGroup()

        @signal.on(intensity.valueChanged, qtParent=parent)
        @ui_lock.skip_if_updating
        def intensity_edited():
            model.current_settings.intensity = intensity.value()
            update_ui()

        iterations = widgets.NumberSliderGroup(value_type=int, min_value=1, max_value=100)

        @signal.on(iterations.valueChanged, qtParent=parent)
        @ui_lock.skip_if_updating
        def iterations_edited():
            model.current_settings.iterations = iterations.value()
            update_ui()

        fixed_influences = QtWidgets.QCheckBox("Only adjust existing vertex influences")
        fixed_influences.setToolTip(
            "When this option is enabled, smooth will only adjust existing influences per vertex, "
            "and won't include other influences from nearby vertices"
        )

        volume_neighbours = QtWidgets.QCheckBox("Smooth across gaps and thin surfaces")
        volume_neighbours.setToolTip(
            "Use all nearby neighbours, regardless if they belong to same surface. "
            "This will allow for smoothing to happen across gaps and thin surfaces."
        )

        limit_to_component_selection = QtWidgets.QCheckBox("Limit to component selection")
        limit_to_component_selection.setToolTip("When this option is enabled, smoothing will only happen between selected components")

        @qt.on(fixed_influences.stateChanged)
        @ui_lock.skip_if_updating
        def fixed_influences_changed(*_):
            model.current_settings.fixed_influences_per_vertex = fixed_influences.isChecked()

        @qt.on(limit_to_component_selection.stateChanged)
        @ui_lock.skip_if_updating
        def limit_to_component_selection_changed(*_):
            model.current_settings.limit_to_component_selection = limit_to_component_selection.isChecked()

        def update_ui():
            with ui_lock:
                widgets.set_paint_expo(intensity, model.current_settings.mode)

                intensity.set_value(model.current_settings.intensity)

                iterations.set_value(model.current_settings.iterations)
                iterations.set_enabled(model.current_settings.mode in [PaintMode.smooth, PaintMode.sharpen])

                fixed_influences.setEnabled(model.current_settings.mode in [PaintMode.smooth])
                fixed_influences.setChecked(model.current_settings.fixed_influences_per_vertex)

                limit_to_component_selection.setChecked(model.current_settings.limit_to_component_selection)
                limit_to_component_selection.setEnabled(fixed_influences.isEnabled())

                influences_limit.set_value(model.current_settings.influences_limit)

                volume_neighbours.setChecked(model.current_settings.use_volume_neighbours)
                volume_neighbours.setEnabled(model.current_settings.mode == PaintMode.smooth)

        settings_group = QtWidgets.QGroupBox("Mode Settings")
        layout = QtWidgets.QVBoxLayout()

        layout.addLayout(createTitledRow("Mode:", mode_row()))
        layout.addLayout(createTitledRow("Intensity:", intensity.layout()))
        layout.addLayout(createTitledRow("Iterations:", iterations.layout()))
        layout.addLayout(createTitledRow("Influences limit:", influences_limit.layout()))
        layout.addLayout(createTitledRow("Weight bleeding:", fixed_influences))
        layout.addLayout(createTitledRow("Volume smoothing:", volume_neighbours))
        layout.addLayout(createTitledRow("Isolation:", limit_to_component_selection))
        settings_group.setLayout(layout)

        update_ui()

        return settings_group

    def common_settings():
        layout = QtWidgets.QVBoxLayout()

        mirror = QtWidgets.QCheckBox("Mirror")
        layout.addLayout(createTitledRow("", mirror))

        @qt.on(mirror.stateChanged)
        @ui_lock.skip_if_updating
        def mirror_changed(*_):
            for _, v in model.presets.items():
                v.mirror = mirror.isChecked()

        redistribute_removed_weight = QtWidgets.QCheckBox("Distribute to other influences")
        layout.addLayout(createTitledRow("Removed weight:", redistribute_removed_weight))

        @qt.on(redistribute_removed_weight.stateChanged)
        def redistribute_removed_weight_changed():
            for _, v in model.presets.items():
                v.distribute_to_other_influences = redistribute_removed_weight.isChecked()

        @signal.on(model.mode_changed, qtParent=layout)
        def update_ui():
            mirror.setChecked(model.current_settings.mirror)
            redistribute_removed_weight.setChecked(model.current_settings.distribute_to_other_influences)

        group = QtWidgets.QGroupBox("Common Settings")
        group.setLayout(layout)

        update_ui()

        return group

    def apply_button():
        btn = QtWidgets.QPushButton("Apply")
        btn.setToolTip("Apply selected operation to vertex")

        @qt.on(btn.clicked)
        def clicked():
            model.apply()

        return btn

    tab = TabSetup()
    tab.innerLayout.addWidget(build_mode_settings_group())
    tab.innerLayout.addWidget(common_settings())
    tab.innerLayout.addStretch()

    tab.lowerButtonsRow.addWidget(apply_button())

    @signal.on(session.events.targetChanged, qtParent=tab.tabContents)
    def update_tab_enabled():
        tab.tabContents.setEnabled(session.state.layersAvailable)

    update_tab_enabled()

    return tab.tabContents
