"""This module deals with direct data exchange between the LAMMPS simulation code and OVITO.
The following function allows employing OVITO's data analysis and rendering functions
within a running molecular dynamics simulation."""
__all__ = ['lammps_to_ovito']
from typing import Optional
import ovito.data
import lammps

def lammps_to_ovito(lmp: lammps.Lammps, data_collection: Optional[ovito.data.DataCollection]) -> ovito.data.DataCollection:
    """Constructs an OVITO :py:class:`DataCollection` from a LAMMPS simulation state.
This function should be called from within a running LAMMPS molecular dynamics simulation to
hand over the current particle model to OVITO and perform data analysis or visualization
tasks in regular time intervals, see the usage example below.

:param lmp: The LAMMPS Python object, which is used by the function to access the particle data of the simulation.
:param data_collection: An existing :py:class:`DataCollection` to fill in with the particle model. A new data collection will be created if not provided.
:return: :py:class:`DataCollection` containing the converted particle data - only on rank 0 of a parallel LAMMPS simulation. On other ranks, the function returns ``None``.

Usage example:

In the LAMMPS simulation script, include the following `python` and `fix python/invoke` commands to
load the following Python script into the context of the LAMMPS simulation and invoke
the ``perform_dxa()`` function in regular timestep intervals during the MD run:

.. code-block:: none

  python perform_dxa source perform_dxa.py
  fix 1 all python/invoke 1000 end_of_step perform_dxa

The following script file, :file:`perform_dxa.py`, defines the Python function to be executed every
1000 timesteps. It runs OVITO's :py:class:`DislocationAnalysisModifier`
on the current simulation snapshot to identify all dislocation defects in the crystal::

    from lammps import lammps
    from ovito.io.lammps import lammps_to_ovito
    from ovito.io import export_file
    from ovito.modifiers import DislocationAnalysisModifier

    def perform_dxa(lammps_ptr):

        # Access the LAMMPS simulation state.
        lmp = lammps(ptr=lammps_ptr)

        # Convert simulation state to OVITO data representation.
        data = lammps_to_ovito(lmp)

        # Let OVITO perform data analysis only on MPI rank 0. Do nothing on other processors.
        if data is None:
            return

        # Run DXA.
        data.apply(DislocationAnalysisModifier(input_crystal_structure=DislocationAnalysisModifier.Lattice.FCC))

        # Output total length of all dislocation lines.
        total_line_length = data.attributes['DislocationAnalysis.total_line_length']
        print('Total dislocation line length:', total_line_length)

        # Dump dislocation lines to a CA file for later visualization in the OVITO desktop application.
        timestep = int(lmp.get_thermo('step'))
        export_file(data, f'dislocations.{timestep}.ca', format='ca')"""
    ...