"""FreeCAD macro to animate mode shapes.

These are the steps used to generate the data to be animated, these are the
usual steps:
- click on "CalculiXccxTools"
- select "Frequency"
- select "Write .inp file"
- select "Run CalculiX"

These are the steps to prepare for and perform the animation:
- open "Macro" in the ToolBar.
- select "Macros..." and choose "FemAnimateModeShapes.py" and "execute". A
  widget "Animate" will be generated in a separate window.
- Double-click on a set of results "CalculiX_frequency_mode_7", for example.
  This panel must be visible for the animation to work, Note (1).
- select "Abs displacement", for example.
- click the radio button "Displacement/Show"
- click on "Start Animation" in the widget.

The entries in the widget are:
- "scale" - scale for the displacements, 1 to 999, this may be a problem if the
  displacements are very large/small before scaling.
- "factor" - enter the value for the scale, this is linked to "scale"
- "number of steps..." - the number of steps in 1 vibratory cycle, if this
  value is increased the apparent speed of the animation will slow down.
- "steps/second" - the number of steps displayed per second, if this value is
  increased the animation will speed up.
- "number of cycles" - the number of cycles in the animation.
- "Start Animation" - starts the animation, this changes to "Stop Animation"
  during the animation.

Note(1) - if you click on "Results_mesh001", and start animation you may
  possibly be animating the wrong mode shape.
"""

import math
import os
import time

import FreeCAD as app

import FreeCADGui as gui

from PySide import QtGui  # FreeCAD's PySide!

__Name__ = 'Animate Mode Shapes for FEM'
__Comment__ = 'Animate mode shapes after running CalculiX'
__Author__ = 'mac_the_bike,galou'
__Version__ = '1.0.1'
__Date__ = '2022-02-04'
__License__ = 'LGPL-2.0-or-later'
__Web__ = 'https://forum.freecadweb.org/viewtopic.php?t=39081'
__Wiki__ = ''
__Icon__ = ''
__Help__ = ''
__Status__ = ''
__Requires__ = ''
__Communication__ = 'https://github.com/FreeCAD/FreeCAD-macros/issues/'
__Files__ = 'fem_animate_mode_shapes.ui'

# Items in the widget window:
# - startEndButton
# - amplitude
# - factor
# - steps
# - steps/second is coded as frames/second
# - loops


class FemAnimateModeShapes():
    def __init__(self):
        self.do_animation = False
        self.ui_file = os.path.join(app.getUserMacroDir(True),
                                    'fem_animate_mode_shapes.ui')
        self.inc = 1  # TODO: find a better name and use.

        self.form = gui.PySideUic.loadUi(self.ui_file)
        self._connect_widgets()
        self.form.show()
        self.box = self._message_box()

    def _connect_widgets(self):
        self.form.startEndButton.clicked.connect(self.start_stop)
        self.form.amplitude.valueChanged.connect(self._on_amplitude_changed)
        self.form.factor.valueChanged.connect(self._on_factor_changed)

    def start(self):
        self.do_animation = True
        self._do_animate()

    def start_stop(self):
        if self.do_animation:
            self.do_animation = False
        else:
            self.do_animation = True
            self._do_animate()

    def _on_amplitude_changed(self):
        if self.inc == 0:
            self.form.factor.setValue(self.form.amplitude.value())
        self.inc = 1 - self.inc

    def _on_factor_changed(self):
        if self.inc == 0:
            self.form.amplitude.setValue(int(self.form.factor.value()))
        self.inc = 1 - self.inc

    def _message_box(self):
        box = QtGui.QMessageBox()
        box.setWindowTitle('No Results')
        box.setText('Select a case and select a displacement type, e.g. Abs')
        box.setInformativeText('Tick the displacement radio button')
        box.setStandardButtons(QtGui.QMessageBox.Ok)
        box.setDefaultButton(QtGui.QMessageBox.Save)
        return box

    def _do_animate(self):
        if not self.do_animation:
            return
        try:
            mesh_obj = app.FEM_dialog['result_obj']
            mesh_obj.Mesh.ViewObject.applyDisplacement(0)
        except:
            self.box.exec_()
            self.do_animation = False
            return

        self.form.startEndButton.setText('Stop Animation')

        frame_count = self.form.frames.value()
        steps = self.form.steps.value()
        loops = self.form.loops.value()
        inc = 2 * math.pi / steps

        done = False
        for lo in range(loops):
            for st in range(steps):
                mesh_obj.Mesh.ViewObject.applyDisplacement(
                    math.sin(inc * st) * self.form.factor.value())
                gui.updateGui()
                if not self.do_animation:
                    done = True
                    break
                time.sleep(1. / frame_count)
            if done:
                break
        self.form.startEndButton.setText('Start Animation')
        self.do_animation = False


if __name__ == '__main__':
    FemAnimateModeShapes()
