from .energy_calculators import EnergyCalculator


class MOPACEnergy(EnergyCalculator):
    def __init__(self, mopac_path):
        self.mopac_path = mopac_path

    def energy(self, mol, conformer=-1):
        """
        Calculates the energy using MOPAC.

        Note that this requires MOPAC to be installed and have a
        valid license.

        Parameters
        ----------
        settings : :class:`dict`, optional
            A dictionary which maps the names of the optimization
            parameters to their values. Valid values are:

                'hamiltonian' : :class:`str` (default = ``'PM7'``
                    A series of different methods can be selected:
                    PM7, PM6, AM1, CIS (CISD, CISDT), MNDO, RM1, etc..

                    PM7 is the latest version of the reparametrization
                    of NDDO theory, where all the atomic and diatomic
                    parameters were re-optimized / updated from PM6
                    [#]_.

                'eps' : :class:`float` (default = ``80.1``)
                    Sets the dielectric constant for the solvent.
                    Presence of this keyword will cause the COSMO
                    (Conductor-like Screening Model) method to be used
                    to approximate the effect of a solvent model
                    surrounding the molecule. Solvents with a low
                    dielectric constant are not likely to work well
                    with this model. ``0`` means that the dielectric
                    constant is not included in the calculation.
                    ``80.1`` can be used to model a water environment
                    at room temperature.

                'charge' : :class:`float` (default = ``0``)
                    The charge of the system.

                'timeout' : :class:`float` (default = ``172800``)
                    The amount in seconds the calculation is allowed to
                    run before being terminated. The default value is
                    ``2`` days or ``172,800`` seconds.

        mopac_path : :class:`str`
            The full path to the MOPAC installation.

        Returns
        -------
        :class:`float`
            The calculated energy.

        References
        ----------
        .. [#] http://openmopac.net/PM7_accuracy/PM7_accuracy.html

        """

        if settings is None:
            settings = {}

        # Define default vals for the MOPAC input
        vals = {
                'hamiltonian': 'PM7',
                'method': 'NOOPT',
                'eps': 80.1,
                'charge': 0,
                'timeout': 172800,
                }

        vals.update(settings)

        # To prevent conflicts when running this function in parallel,
        # a temporary copy of the molecular structure file is made and
        # used for mopac calculations.

        # Unique file name is generated by inserting a random int into
        # the file path.
        tmp_file = "{}.mol".format(uuid4().int)
        self.molecule.write(tmp_file)

        file_root, ext = os.path.splitext(tmp_file)

        # Generate the input file
        _create_mop(file_root, self.molecule, vals)
        # Run MOPAC
        _run_mopac(file_root, mopac_path)
        return _extract_MOPAC_en(file_root)
