# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)

import os

from spack import *


class Dftbplus(MakefilePackage):
    """DFTB+ is an implementation of the
    Density Functional based Tight Binding (DFTB) method,
    containing many extensions to the original method."""

    homepage = "https://www.dftbplus.org"
    url      = "https://github.com/dftbplus/dftbplus/archive/19.1.tar.gz"

    version('19.1', sha256='4d07f5c6102f06999d8cfdb1d17f5b59f9f2b804697f14b3bc562e3ea094b8a8')

    resource(name='slakos',
             url='https://github.com/dftbplus/testparams/archive/dftbplus-18.2.tar.gz',
             sha256='bd191b3d240c1a81a8754a365e53a78b581fc92eb074dd5beb8b56a669a8d3d1',
             destination='external/slakos',
             when='@18.2:')

    variant('mpi', default=True,
            description="Build an MPI-paralelised version of the code.")

    variant('gpu', default=False,
            description="Use the MAGMA library "
            "for GPU accelerated computation")

    variant('elsi', default=False,
            description="Use the ELSI library for large scale systems. "
            "Only has any effect if you build with '+mpi'")

    variant('sockets', default=False,
            description="Whether the socket library "
            "(external control) should be linked")

    variant('arpack', default=False,
            description="Use ARPACK for excited state DFTB functionality")

    variant('transport', default=False,
            description="Whether transport via libNEGF should be included. "
            "Only affects parallel build. "
            "(serial version is built without libNEGF/transport)")

    variant('dftd3', default=False,
            description="Use DftD3 dispersion library "
            "(if you need this dispersion model)")

    depends_on('lapack')
    depends_on('blas')
    depends_on('scalapack', when="+mpi")
    depends_on('mpi', when="+mpi")
    depends_on('elsi', when="+elsi")
    depends_on('magma', when="+gpu")
    depends_on('arpack-ng', when="+arpack")
    depends_on('dftd3-lib@0.9.2', when="+dftd3")

    def edit(self, spec, prefix):
        """
        First, change the ROOT variable, because, for some reason,
        the Makefile and the spack install script run in different directories

        Then, if using GCC, rename the file 'sys/make.x86_64-linux-gnu'
        to make.arch.

        After that, edit the make.arch to point to the dependencies

        And the last thing we do here is to set the installdir
        """
        dircwd = os.getcwd()
        makefile = FileFilter("makefile")
        makefile.filter("ROOT := .*", "ROOT := {0}".format(dircwd))

        archmake = join_path(".", "sys", "make.x86_64-linux-gnu")
        copy(archmake, join_path(dircwd, "make.arch"))

        march = FileFilter(join_path(dircwd, 'make.arch'))

        mconfig = FileFilter(join_path(dircwd, 'make.config'))

        mconfig.filter('INSTALLDIR := .*', 'INSTALLDIR := {0}'.format(prefix))

        if '+gpu' in self.spec:
            march.filter('MAGMADIR = .*',
                         'MAGMADIR = {0}'.format(spec['magma'].prefix))

            mconfig.filter('WITH_GPU := .*', 'WITH_GPU := 1')

        if '+mpi' in self.spec:
            march.filter('SCALAPACKDIR = .*',
                         'SCALAPACKDIR = {0}'.format(spec['scalapack'].prefix))

            march.filter('LIB_LAPACK = -l.*',
                         'LIB_LAPACK = {0}'.format(spec['blas'].libs.ld_flags))

            march.filter('mpifort', '{0}'.format(spec['mpi'].mpifc))

            mconfig.filter('WITH_MPI := .*', 'WITH_MPI := 1')

            if '+elsi' in self.spec:
                mconfig.filter('WITH_ELSI := .*', 'WITH_ELSI := 1')

                has_pexsi = '+enable_pexsi' in spec['elsi']

                mconfig.filter('WITH_PEXSI := .*', 'WITH_PEXSI := {0}'.format(
                    '1' if has_pexsi is True else '0'
                ))

                march.filter("ELSIINCDIR .*", "ELSIINCDIR = {0}".format(
                    spec['elsi'].prefix.include
                ))

                march.filter("ELSIDIR .*",
                             "ELSIDIR = {0}".format(spec['elsi'].prefix))

        else:
            march.filter('LIB_LAPACK += -l.*', 'LIB_LAPACK += {0}'.format(
                spec['blas'].libs.ld_flags))

        if '+sockets' in self.spec:
            mconfig.filter('WITH_SOCKETS := .*', 'WITH_SOCKETS := 1')

        if '+transport' in self.spec:
            mconfig.filter('WITH_TRANSPORT := .*', 'WITH_TRANSPORT := 1')

        if '+arpack' in self.spec:
            march.filter('ARPACK_LIBS = .*', 'ARPACK_LIBS = {0}'.format(
                spec['arpack-ng'].libs.ld_flags
            ))

            mconfig.filter('WITH_ARPACK := .*', 'WITH_ARPACK := 1')

        if '+dftd3' in self.spec:
            march.filter('COMPILE_DFTD3 = .*', 'COMPILE_DFTD3 = 0')
            march.filter('DFTD3_INCS = .*', 'DFTD3_INCS = -I{0}'.format(
                spec['dftd3-lib'].prefix.include
            ))

            march.filter('DFTD3_LIBS = .*',
                         'DFTD3_LIBS = -L{0} -ldftd3'.format(
                             spec['dftd3-lib'].prefix))

            mconfig.filter('WITH_DFTD3 := .*', 'WITH_DFTD3 := 1')
